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