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