Line data Source code
1 : /*
2 : * Recycle bin VFS module for Samba.
3 : *
4 : * Copyright (C) 2001, Brandon Stone, Amherst College, <bbstone@amherst.edu>.
5 : * Copyright (C) 2002, Jeremy Allison - modified to make a VFS module.
6 : * Copyright (C) 2002, Alexander Bokovoy - cascaded VFS adoption,
7 : * Copyright (C) 2002, Juergen Hasch - added some options.
8 : * Copyright (C) 2002, Simo Sorce
9 : * Copyright (C) 2002, Stefan (metze) Metzmacher
10 : *
11 : * This program is free software; you can redistribute it and/or modify
12 : * it under the terms of the GNU General Public License as published by
13 : * the Free Software Foundation; either version 3 of the License, or
14 : * (at your option) any later version.
15 : *
16 : * This program is distributed in the hope that it will be useful,
17 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : * GNU General Public License for more details.
20 : *
21 : * You should have received a copy of the GNU General Public License
22 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include "includes.h"
26 : #include "smbd/smbd.h"
27 : #include "system/filesys.h"
28 : #include "../librpc/gen_ndr/ndr_netlogon.h"
29 : #include "auth.h"
30 : #include "source3/lib/substitute.h"
31 :
32 : #define ALLOC_CHECK(ptr, label) do { if ((ptr) == NULL) { DEBUG(0, ("recycle.bin: out of memory!\n")); errno = ENOMEM; goto label; } } while(0)
33 :
34 : static int vfs_recycle_debug_level = DBGC_VFS;
35 :
36 : #undef DBGC_CLASS
37 : #define DBGC_CLASS vfs_recycle_debug_level
38 :
39 : struct recycle_config_data {
40 : const char *repository;
41 : bool keeptree;
42 : bool versions;
43 : bool touch;
44 : bool touch_mtime;
45 : const char **exclude;
46 : const char **exclude_dir;
47 : const char **noversions;
48 : mode_t directory_mode;
49 : mode_t subdir_mode;
50 : off_t minsize;
51 : off_t maxsize;
52 : };
53 :
54 8 : static int vfs_recycle_connect(struct vfs_handle_struct *handle,
55 : const char *service,
56 : const char *user)
57 : {
58 8 : struct recycle_config_data *config = NULL;
59 : int ret;
60 : int t;
61 : const char *buff;
62 :
63 8 : ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
64 8 : if (ret < 0) {
65 0 : return ret;
66 : }
67 :
68 8 : if (IS_IPC(handle->conn) || IS_PRINT(handle->conn)) {
69 0 : return 0;
70 : }
71 :
72 8 : config = talloc_zero(handle->conn, struct recycle_config_data);
73 8 : if (config == NULL) {
74 0 : DBG_ERR("talloc_zero() failed\n");
75 0 : errno = ENOMEM;
76 0 : return -1;
77 : }
78 8 : config->repository = lp_parm_const_string(SNUM(handle->conn),
79 : "recycle",
80 : "repository",
81 : ".recycle");
82 8 : config->keeptree = lp_parm_bool(SNUM(handle->conn),
83 : "recycle",
84 : "keeptree",
85 : False);
86 8 : config->versions = lp_parm_bool(SNUM(handle->conn),
87 : "recycle",
88 : "versions",
89 : False);
90 8 : config->touch = lp_parm_bool(SNUM(handle->conn),
91 : "recycle",
92 : "touch",
93 : False);
94 8 : config->touch_mtime = lp_parm_bool(SNUM(handle->conn),
95 : "recycle",
96 : "touch_mtime",
97 : False);
98 8 : config->exclude = lp_parm_string_list(SNUM(handle->conn),
99 : "recycle",
100 : "exclude",
101 : NULL);
102 8 : config->exclude_dir = lp_parm_string_list(SNUM(handle->conn),
103 : "recycle",
104 : "exclude_dir",
105 : NULL);
106 8 : config->noversions = lp_parm_string_list(SNUM(handle->conn),
107 : "recycle",
108 : "noversions",
109 : NULL);
110 8 : config->minsize = conv_str_size(lp_parm_const_string(
111 8 : SNUM(handle->conn), "recycle", "minsize", NULL));
112 8 : config->maxsize = conv_str_size(lp_parm_const_string(
113 8 : SNUM(handle->conn), "recycle", "maxsize", NULL));
114 :
115 8 : buff = lp_parm_const_string(SNUM(handle->conn),
116 : "recycle",
117 : "directory_mode",
118 : NULL);
119 8 : if (buff != NULL ) {
120 8 : sscanf(buff, "%o", &t);
121 : } else {
122 0 : t = S_IRUSR | S_IWUSR | S_IXUSR;
123 : }
124 8 : config->directory_mode = (mode_t)t;
125 :
126 8 : buff = lp_parm_const_string(SNUM(handle->conn),
127 : "recycle",
128 : "subdir_mode",
129 : NULL);
130 8 : if (buff != NULL ) {
131 0 : sscanf(buff, "%o", &t);
132 : } else {
133 8 : t = config->directory_mode;
134 : }
135 8 : config->subdir_mode = (mode_t)t;
136 :
137 8 : SMB_VFS_HANDLE_SET_DATA(
138 : handle, config, NULL, struct recycle_config_data, return -1);
139 8 : return 0;
140 : }
141 :
142 4 : static bool recycle_directory_exist(vfs_handle_struct *handle, const char *dname)
143 : {
144 4 : struct smb_filename smb_fname = {
145 : .base_name = discard_const_p(char, dname)
146 : };
147 :
148 4 : if (SMB_VFS_STAT(handle->conn, &smb_fname) == 0) {
149 0 : if (S_ISDIR(smb_fname.st.st_ex_mode)) {
150 0 : return True;
151 : }
152 : }
153 :
154 4 : return False;
155 : }
156 :
157 4 : static bool recycle_file_exist(vfs_handle_struct *handle,
158 : const struct smb_filename *smb_fname)
159 : {
160 4 : struct smb_filename *smb_fname_tmp = NULL;
161 4 : bool ret = false;
162 :
163 4 : smb_fname_tmp = cp_smb_filename(talloc_tos(), smb_fname);
164 4 : if (smb_fname_tmp == NULL) {
165 0 : return false;
166 : }
167 :
168 4 : if (SMB_VFS_STAT(handle->conn, smb_fname_tmp) == 0) {
169 0 : if (S_ISREG(smb_fname_tmp->st.st_ex_mode)) {
170 0 : ret = true;
171 : }
172 : }
173 :
174 4 : TALLOC_FREE(smb_fname_tmp);
175 4 : return ret;
176 : }
177 :
178 : /**
179 : * Return file size
180 : * @param conn connection
181 : * @param fname file name
182 : * @return size in bytes
183 : **/
184 4 : static off_t recycle_get_file_size(vfs_handle_struct *handle,
185 : const struct smb_filename *smb_fname)
186 : {
187 4 : struct smb_filename *smb_fname_tmp = NULL;
188 : off_t size;
189 :
190 4 : smb_fname_tmp = cp_smb_filename(talloc_tos(), smb_fname);
191 4 : if (smb_fname_tmp == NULL) {
192 0 : size = (off_t)0;
193 0 : goto out;
194 : }
195 :
196 4 : if (SMB_VFS_STAT(handle->conn, smb_fname_tmp) != 0) {
197 0 : DBG_DEBUG("stat for %s returned %s\n",
198 : smb_fname_str_dbg(smb_fname_tmp), strerror(errno));
199 0 : size = (off_t)0;
200 0 : goto out;
201 : }
202 :
203 4 : size = smb_fname_tmp->st.st_ex_size;
204 4 : out:
205 4 : TALLOC_FREE(smb_fname_tmp);
206 4 : return size;
207 : }
208 :
209 : /**
210 : * Create directory tree
211 : * @param conn connection
212 : * @param dname Directory tree to be created
213 : * @param directory mode
214 : * @param subdirectory mode
215 : * @return Returns True for success
216 : **/
217 2 : static bool recycle_create_dir(vfs_handle_struct *handle,
218 : const char *dname,
219 : mode_t dir_mode,
220 : mode_t subdir_mode)
221 : {
222 : size_t len;
223 2 : mode_t mode = dir_mode;
224 2 : char *new_dir = NULL;
225 2 : char *tmp_str = NULL;
226 : char *token;
227 : char *tok_str;
228 2 : bool ret = False;
229 : char *saveptr;
230 :
231 2 : tmp_str = SMB_STRDUP(dname);
232 2 : ALLOC_CHECK(tmp_str, done);
233 2 : tok_str = tmp_str;
234 :
235 2 : len = strlen(dname)+1;
236 2 : new_dir = (char *)SMB_MALLOC(len + 1);
237 2 : ALLOC_CHECK(new_dir, done);
238 2 : *new_dir = '\0';
239 2 : if (dname[0] == '/') {
240 : /* Absolute path. */
241 0 : if (strlcat(new_dir,"/",len+1) >= len+1) {
242 0 : goto done;
243 : }
244 : }
245 :
246 : /* Create directory tree if necessary */
247 4 : for(token = strtok_r(tok_str, "/", &saveptr); token;
248 2 : token = strtok_r(NULL, "/", &saveptr)) {
249 2 : if (strlcat(new_dir, token, len+1) >= len+1) {
250 0 : goto done;
251 : }
252 2 : if (recycle_directory_exist(handle, new_dir))
253 0 : DEBUG(10, ("recycle: dir %s already exists\n", new_dir));
254 : else {
255 2 : struct smb_filename *smb_fname = NULL;
256 : int retval;
257 :
258 2 : DEBUG(5, ("recycle: creating new dir %s\n", new_dir));
259 :
260 2 : smb_fname = synthetic_smb_fname(talloc_tos(),
261 : new_dir,
262 : NULL,
263 : NULL,
264 : 0,
265 : 0);
266 2 : if (smb_fname == NULL) {
267 0 : goto done;
268 : }
269 :
270 2 : retval = SMB_VFS_NEXT_MKDIRAT(handle,
271 : handle->conn->cwd_fsp,
272 : smb_fname,
273 : mode);
274 2 : if (retval != 0) {
275 0 : DBG_WARNING("recycle: mkdirat failed "
276 : "for %s with error: %s\n",
277 : new_dir,
278 : strerror(errno));
279 0 : TALLOC_FREE(smb_fname);
280 0 : ret = False;
281 0 : goto done;
282 : }
283 2 : TALLOC_FREE(smb_fname);
284 : }
285 2 : if (strlcat(new_dir, "/", len+1) >= len+1) {
286 0 : goto done;
287 : }
288 2 : mode = subdir_mode;
289 : }
290 :
291 2 : ret = True;
292 2 : done:
293 2 : SAFE_FREE(tmp_str);
294 2 : SAFE_FREE(new_dir);
295 2 : return ret;
296 : }
297 :
298 : /**
299 : * Check if any of the components of "exclude_list" are contained in path.
300 : * Return True if found
301 : **/
302 :
303 2 : static bool matchdirparam(const char **dir_exclude_list, char *path)
304 : {
305 2 : char *startp = NULL, *endp = NULL;
306 :
307 2 : if (dir_exclude_list == NULL || dir_exclude_list[0] == NULL ||
308 0 : *dir_exclude_list[0] == '\0' || path == NULL || *path == '\0') {
309 2 : return False;
310 : }
311 :
312 : /*
313 : * Walk the components of path, looking for matches with the
314 : * exclude list on each component.
315 : */
316 :
317 0 : for (startp = path; startp; startp = endp) {
318 : int i;
319 :
320 0 : while (*startp == '/') {
321 0 : startp++;
322 : }
323 0 : endp = strchr(startp, '/');
324 0 : if (endp) {
325 0 : *endp = '\0';
326 : }
327 :
328 0 : for(i=0; dir_exclude_list[i] ; i++) {
329 0 : if(unix_wild_match(dir_exclude_list[i], startp)) {
330 : /* Repair path. */
331 0 : if (endp) {
332 0 : *endp = '/';
333 : }
334 0 : return True;
335 : }
336 : }
337 :
338 : /* Repair path. */
339 0 : if (endp) {
340 0 : *endp = '/';
341 : }
342 : }
343 :
344 0 : return False;
345 : }
346 :
347 : /**
348 : * Check if needle is contained in haystack, * and ? patterns are resolved
349 : * @param haystack list of parameters separated by delimimiter character
350 : * @param needle string to be matched exectly to haystack including pattern matching
351 : * @return True if found
352 : **/
353 4 : static bool matchparam(const char **haystack_list, const char *needle)
354 : {
355 : int i;
356 :
357 4 : if (haystack_list == NULL || haystack_list[0] == NULL ||
358 4 : *haystack_list[0] == '\0' || needle == NULL || *needle == '\0') {
359 0 : return False;
360 : }
361 :
362 6 : for(i=0; haystack_list[i] ; i++) {
363 4 : if(unix_wild_match(haystack_list[i], needle)) {
364 2 : return True;
365 : }
366 : }
367 :
368 2 : return False;
369 : }
370 :
371 : /**
372 : * Touch access or modify date
373 : **/
374 0 : static void recycle_do_touch(vfs_handle_struct *handle,
375 : const struct smb_filename *smb_fname,
376 : bool touch_mtime)
377 : {
378 0 : struct smb_filename *smb_fname_tmp = NULL;
379 : struct smb_file_time ft;
380 : int ret, err;
381 : NTSTATUS status;
382 :
383 0 : init_smb_file_time(&ft);
384 :
385 0 : status = synthetic_pathref(talloc_tos(),
386 0 : handle->conn->cwd_fsp,
387 0 : smb_fname->base_name,
388 0 : smb_fname->stream_name,
389 : NULL,
390 0 : smb_fname->twrp,
391 0 : smb_fname->flags,
392 : &smb_fname_tmp);
393 0 : if (!NT_STATUS_IS_OK(status)) {
394 0 : DBG_DEBUG("synthetic_pathref for '%s' failed: %s\n",
395 : smb_fname_str_dbg(smb_fname), nt_errstr(status));
396 0 : return;
397 : }
398 :
399 : /* atime */
400 0 : ft.atime = timespec_current();
401 : /* mtime */
402 0 : ft.mtime = touch_mtime ? ft.atime : smb_fname_tmp->st.st_ex_mtime;
403 :
404 0 : become_root();
405 0 : ret = SMB_VFS_NEXT_FNTIMES(handle, smb_fname_tmp->fsp, &ft);
406 0 : err = errno;
407 0 : unbecome_root();
408 0 : if (ret == -1 ) {
409 0 : DEBUG(0, ("recycle: touching %s failed, reason = %s\n",
410 : smb_fname_str_dbg(smb_fname_tmp), strerror(err)));
411 : }
412 :
413 0 : TALLOC_FREE(smb_fname_tmp);
414 : }
415 :
416 : /**
417 : * Check if file should be recycled
418 : **/
419 4 : static int recycle_unlink_internal(vfs_handle_struct *handle,
420 : struct files_struct *dirfsp,
421 : const struct smb_filename *smb_fname,
422 : int flags)
423 : {
424 : const struct loadparm_substitution *lp_sub =
425 4 : loadparm_s3_global_substitution();
426 4 : connection_struct *conn = handle->conn;
427 4 : struct smb_filename *full_fname = NULL;
428 4 : char *path_name = NULL;
429 4 : char *temp_name = NULL;
430 4 : char *final_name = NULL;
431 4 : struct smb_filename *smb_fname_final = NULL;
432 : const char *base;
433 4 : char *repository = NULL;
434 4 : int i = 1;
435 : off_t file_size; /* space_avail; */
436 : bool exist;
437 4 : int rc = -1;
438 : struct recycle_config_data *config;
439 :
440 4 : SMB_VFS_HANDLE_GET_DATA(handle,
441 : config,
442 : struct recycle_config_data,
443 : return true);
444 :
445 4 : repository = talloc_sub_full(
446 : NULL,
447 4 : lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),
448 4 : conn->session_info->unix_info->unix_name,
449 4 : conn->connectpath,
450 4 : conn->session_info->unix_token->gid,
451 4 : conn->session_info->unix_info->sanitized_username,
452 4 : conn->session_info->info->domain_name,
453 : config->repository);
454 4 : ALLOC_CHECK(repository, done);
455 : /* shouldn't we allow absolute path names here? --metze */
456 : /* Yes :-). JRA. */
457 4 : trim_char(repository, '\0', '/');
458 :
459 4 : if(!repository || *(repository) == '\0') {
460 0 : DEBUG(3, ("recycle: repository path not set, purging %s...\n",
461 : smb_fname_str_dbg(smb_fname)));
462 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
463 : dirfsp,
464 : smb_fname,
465 : flags);
466 0 : goto done;
467 : }
468 :
469 4 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
470 : dirfsp,
471 : smb_fname);
472 4 : if (full_fname == NULL) {
473 0 : return -1;
474 : }
475 :
476 : /* we don't recycle the recycle bin... */
477 4 : if (strncmp(full_fname->base_name, repository,
478 : strlen(repository)) == 0) {
479 0 : DEBUG(3, ("recycle: File is within recycling bin, unlinking ...\n"));
480 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
481 : dirfsp,
482 : smb_fname,
483 : flags);
484 0 : goto done;
485 : }
486 :
487 4 : file_size = recycle_get_file_size(handle, full_fname);
488 : /* it is wrong to purge filenames only because they are empty imho
489 : * --- simo
490 : *
491 : if(fsize == 0) {
492 : DEBUG(3, ("recycle: File %s is empty, purging...\n", file_name));
493 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
494 : dirfsp,
495 : file_name,
496 : flags);
497 : goto done;
498 : }
499 : */
500 :
501 : /* FIXME: this is wrong, we should check the whole size of the recycle bin is
502 : * not greater then maxsize, not the size of the single file, also it is better
503 : * to remove older files
504 : */
505 4 : if (config->maxsize > 0 && file_size > config->maxsize) {
506 0 : DBG_NOTICE("File %s exceeds maximum recycle size, "
507 : "purging... \n",
508 : smb_fname_str_dbg(full_fname));
509 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
510 : dirfsp,
511 : smb_fname,
512 : flags);
513 0 : goto done;
514 : }
515 4 : if (config->minsize > 0 && file_size < config->minsize) {
516 0 : DBG_NOTICE("File %s lowers minimum recycle size, "
517 : "purging... \n",
518 : smb_fname_str_dbg(full_fname));
519 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
520 : dirfsp,
521 : smb_fname,
522 : flags);
523 0 : goto done;
524 : }
525 :
526 : /* FIXME: this is wrong: moving files with rename does not change the disk space
527 : * allocation
528 : *
529 : space_avail = SMB_VFS_NEXT_DISK_FREE(handle, ".", True, &bsize, &dfree, &dsize) * 1024L;
530 : DEBUG(5, ("space_avail = %Lu, file_size = %Lu\n", space_avail, file_size));
531 : if(space_avail < file_size) {
532 : DEBUG(3, ("recycle: Not enough diskspace, purging file %s\n", file_name));
533 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
534 : dirfsp,
535 : file_name,
536 : flags);
537 : goto done;
538 : }
539 : */
540 :
541 : /* extract filename and path */
542 4 : if (!parent_dirname(talloc_tos(), full_fname->base_name, &path_name, &base)) {
543 0 : rc = -1;
544 0 : errno = ENOMEM;
545 0 : goto done;
546 : }
547 :
548 : /* original filename with path */
549 4 : DEBUG(10, ("recycle: fname = %s\n", smb_fname_str_dbg(full_fname)));
550 : /* original path */
551 4 : DEBUG(10, ("recycle: fpath = %s\n", path_name));
552 : /* filename without path */
553 4 : DEBUG(10, ("recycle: base = %s\n", base));
554 :
555 4 : if (matchparam(config->exclude, base)) {
556 2 : DEBUG(3, ("recycle: file %s is excluded \n", base));
557 2 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
558 : dirfsp,
559 : smb_fname,
560 : flags);
561 2 : goto done;
562 : }
563 :
564 2 : if (matchdirparam(config->exclude_dir, path_name)) {
565 0 : DEBUG(3, ("recycle: directory %s is excluded \n", path_name));
566 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
567 : dirfsp,
568 : smb_fname,
569 : flags);
570 0 : goto done;
571 : }
572 :
573 2 : if (config->keeptree) {
574 0 : if (asprintf(&temp_name, "%s/%s", repository, path_name) == -1) {
575 0 : ALLOC_CHECK(temp_name, done);
576 : }
577 : } else {
578 2 : temp_name = SMB_STRDUP(repository);
579 : }
580 2 : ALLOC_CHECK(temp_name, done);
581 :
582 2 : exist = recycle_directory_exist(handle, temp_name);
583 2 : if (exist) {
584 0 : DEBUG(10, ("recycle: Directory already exists\n"));
585 : } else {
586 2 : DEBUG(10, ("recycle: Creating directory %s\n", temp_name));
587 2 : if (recycle_create_dir(handle,
588 : temp_name,
589 : config->directory_mode,
590 2 : config->subdir_mode) == False)
591 : {
592 0 : DEBUG(3, ("recycle: Could not create directory, "
593 : "purging %s...\n",
594 : smb_fname_str_dbg(full_fname)));
595 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
596 : dirfsp,
597 : smb_fname,
598 : flags);
599 0 : goto done;
600 : }
601 : }
602 :
603 2 : if (asprintf(&final_name, "%s/%s", temp_name, base) == -1) {
604 0 : ALLOC_CHECK(final_name, done);
605 : }
606 :
607 : /* Create smb_fname with final base name and orig stream name. */
608 2 : smb_fname_final = synthetic_smb_fname(talloc_tos(),
609 : final_name,
610 2 : full_fname->stream_name,
611 : NULL,
612 : full_fname->twrp,
613 : full_fname->flags);
614 2 : if (smb_fname_final == NULL) {
615 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
616 : dirfsp,
617 : smb_fname,
618 : flags);
619 0 : goto done;
620 : }
621 :
622 : /* new filename with path */
623 2 : DEBUG(10, ("recycle: recycled file name: %s\n",
624 : smb_fname_str_dbg(smb_fname_final)));
625 :
626 : /* check if we should delete file from recycle bin */
627 2 : if (recycle_file_exist(handle, smb_fname_final)) {
628 0 : if (config->versions == False ||
629 0 : matchparam(config->noversions, base) == True) {
630 0 : DEBUG(3, ("recycle: Removing old file %s from recycle "
631 : "bin\n", smb_fname_str_dbg(smb_fname_final)));
632 0 : if (SMB_VFS_NEXT_UNLINKAT(handle,
633 : dirfsp->conn->cwd_fsp,
634 : smb_fname_final,
635 : flags) != 0) {
636 0 : DEBUG(1, ("recycle: Error deleting old file: %s\n", strerror(errno)));
637 : }
638 : }
639 : }
640 :
641 : /* rename file we move to recycle bin */
642 2 : i = 1;
643 2 : while (recycle_file_exist(handle, smb_fname_final)) {
644 0 : SAFE_FREE(final_name);
645 0 : if (asprintf(&final_name, "%s/Copy #%d of %s", temp_name, i++, base) == -1) {
646 0 : ALLOC_CHECK(final_name, done);
647 : }
648 0 : TALLOC_FREE(smb_fname_final->base_name);
649 0 : smb_fname_final->base_name = talloc_strdup(smb_fname_final,
650 : final_name);
651 0 : if (smb_fname_final->base_name == NULL) {
652 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
653 : dirfsp,
654 : smb_fname,
655 : flags);
656 0 : goto done;
657 : }
658 : }
659 :
660 2 : DEBUG(10, ("recycle: Moving %s to %s\n", smb_fname_str_dbg(full_fname),
661 : smb_fname_str_dbg(smb_fname_final)));
662 2 : rc = SMB_VFS_NEXT_RENAMEAT(handle,
663 : dirfsp,
664 : smb_fname,
665 : handle->conn->cwd_fsp,
666 : smb_fname_final);
667 2 : if (rc != 0) {
668 0 : DEBUG(3, ("recycle: Move error %d (%s), purging file %s "
669 : "(%s)\n", errno, strerror(errno),
670 : smb_fname_str_dbg(full_fname),
671 : smb_fname_str_dbg(smb_fname_final)));
672 0 : rc = SMB_VFS_NEXT_UNLINKAT(handle,
673 : dirfsp,
674 : smb_fname,
675 : flags);
676 0 : goto done;
677 : }
678 :
679 : /* touch access date of moved file */
680 2 : if (config->touch || config->touch_mtime)
681 0 : recycle_do_touch(handle, smb_fname_final, config->touch_mtime);
682 :
683 2 : done:
684 4 : TALLOC_FREE(path_name);
685 4 : SAFE_FREE(temp_name);
686 4 : SAFE_FREE(final_name);
687 4 : TALLOC_FREE(full_fname);
688 4 : TALLOC_FREE(smb_fname_final);
689 4 : TALLOC_FREE(repository);
690 4 : return rc;
691 : }
692 :
693 4 : static int recycle_unlinkat(vfs_handle_struct *handle,
694 : struct files_struct *dirfsp,
695 : const struct smb_filename *smb_fname,
696 : int flags)
697 : {
698 : int ret;
699 :
700 4 : if (flags & AT_REMOVEDIR) {
701 0 : ret = SMB_VFS_NEXT_UNLINKAT(handle,
702 : dirfsp,
703 : smb_fname,
704 : flags);
705 : } else {
706 4 : ret = recycle_unlink_internal(handle,
707 : dirfsp,
708 : smb_fname,
709 : flags);
710 : }
711 4 : return ret;
712 : }
713 :
714 : static struct vfs_fn_pointers vfs_recycle_fns = {
715 : .connect_fn = vfs_recycle_connect,
716 : .unlinkat_fn = recycle_unlinkat,
717 : };
718 :
719 : static_decl_vfs;
720 35 : NTSTATUS vfs_recycle_init(TALLOC_CTX *ctx)
721 : {
722 35 : NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "recycle",
723 : &vfs_recycle_fns);
724 :
725 35 : if (!NT_STATUS_IS_OK(ret))
726 0 : return ret;
727 :
728 35 : vfs_recycle_debug_level = debug_add_class("recycle");
729 35 : if (vfs_recycle_debug_level == -1) {
730 0 : vfs_recycle_debug_level = DBGC_VFS;
731 0 : DEBUG(0, ("vfs_recycle: Couldn't register custom debugging class!\n"));
732 : } else {
733 35 : DEBUG(10, ("vfs_recycle: Debug class number of 'recycle': %d\n", vfs_recycle_debug_level));
734 : }
735 :
736 35 : return ret;
737 : }
|