00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "includes.h"
00027
00028 #define ALLOC_CHECK(ptr, label) do { if ((ptr) == NULL) { DEBUG(0, ("recycle.bin: out of memory!\n")); errno = ENOMEM; goto label; } } while(0)
00029
00030 static int vfs_recycle_debug_level = DBGC_VFS;
00031
00032 #undef DBGC_CLASS
00033 #define DBGC_CLASS vfs_recycle_debug_level
00034
00035 static int recycle_connect(vfs_handle_struct *handle, const char *service, const char *user);
00036 static void recycle_disconnect(vfs_handle_struct *handle);
00037 static int recycle_unlink(vfs_handle_struct *handle, const char *name);
00038
00039 static vfs_op_tuple recycle_ops[] = {
00040
00041
00042 {SMB_VFS_OP(recycle_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT},
00043 {SMB_VFS_OP(recycle_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_TRANSPARENT},
00044
00045
00046 {SMB_VFS_OP(recycle_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
00047
00048 {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
00049 };
00050
00051 static int recycle_connect(vfs_handle_struct *handle, const char *service, const char *user)
00052 {
00053 DEBUG(10,("recycle_connect() connect to service[%s] as user[%s].\n",
00054 service,user));
00055
00056 return SMB_VFS_NEXT_CONNECT(handle, service, user);
00057 }
00058
00059 static void recycle_disconnect(vfs_handle_struct *handle)
00060 {
00061 DEBUG(10,("recycle_disconnect() connect to service[%s].\n",
00062 lp_servicename(SNUM(handle->conn))));
00063
00064 SMB_VFS_NEXT_DISCONNECT(handle);
00065 }
00066
00067 static const char *recycle_repository(vfs_handle_struct *handle)
00068 {
00069 const char *tmp_str = NULL;
00070
00071
00072 tmp_str = lp_parm_const_string(SNUM(handle->conn), "recycle", "repository",".recycle");
00073
00074 DEBUG(10, ("recycle: repository = %s\n", tmp_str));
00075
00076 return tmp_str;
00077 }
00078
00079 static BOOL recycle_keep_dir_tree(vfs_handle_struct *handle)
00080 {
00081 BOOL ret;
00082
00083 ret = lp_parm_bool(SNUM(handle->conn), "recycle", "keeptree", False);
00084
00085 DEBUG(10, ("recycle_bin: keeptree = %s\n", ret?"True":"False"));
00086
00087 return ret;
00088 }
00089
00090 static BOOL recycle_versions(vfs_handle_struct *handle)
00091 {
00092 BOOL ret;
00093
00094 ret = lp_parm_bool(SNUM(handle->conn), "recycle", "versions", False);
00095
00096 DEBUG(10, ("recycle: versions = %s\n", ret?"True":"False"));
00097
00098 return ret;
00099 }
00100
00101 static BOOL recycle_touch(vfs_handle_struct *handle)
00102 {
00103 BOOL ret;
00104
00105 ret = lp_parm_bool(SNUM(handle->conn), "recycle", "touch", False);
00106
00107 DEBUG(10, ("recycle: touch = %s\n", ret?"True":"False"));
00108
00109 return ret;
00110 }
00111
00112 static BOOL recycle_touch_mtime(vfs_handle_struct *handle)
00113 {
00114 BOOL ret;
00115
00116 ret = lp_parm_bool(SNUM(handle->conn), "recycle", "touch_mtime", False);
00117
00118 DEBUG(10, ("recycle: touch_mtime = %s\n", ret?"True":"False"));
00119
00120 return ret;
00121 }
00122
00123 static const char **recycle_exclude(vfs_handle_struct *handle)
00124 {
00125 const char **tmp_lp;
00126
00127 tmp_lp = lp_parm_string_list(SNUM(handle->conn), "recycle", "exclude", NULL);
00128
00129 DEBUG(10, ("recycle: exclude = %s ...\n", tmp_lp?*tmp_lp:""));
00130
00131 return tmp_lp;
00132 }
00133
00134 static const char **recycle_exclude_dir(vfs_handle_struct *handle)
00135 {
00136 const char **tmp_lp;
00137
00138 tmp_lp = lp_parm_string_list(SNUM(handle->conn), "recycle", "exclude_dir", NULL);
00139
00140 DEBUG(10, ("recycle: exclude_dir = %s ...\n", tmp_lp?*tmp_lp:""));
00141
00142 return tmp_lp;
00143 }
00144
00145 static const char **recycle_noversions(vfs_handle_struct *handle)
00146 {
00147 const char **tmp_lp;
00148
00149 tmp_lp = lp_parm_string_list(SNUM(handle->conn), "recycle", "noversions", NULL);
00150
00151 DEBUG(10, ("recycle: noversions = %s\n", tmp_lp?*tmp_lp:""));
00152
00153 return tmp_lp;
00154 }
00155
00156 static SMB_OFF_T recycle_maxsize(vfs_handle_struct *handle)
00157 {
00158 SMB_OFF_T maxsize;
00159
00160 maxsize = conv_str_size(lp_parm_const_string(SNUM(handle->conn),
00161 "recycle", "maxsize", NULL));
00162
00163 DEBUG(10, ("recycle: maxsize = %lu\n", (long unsigned int)maxsize));
00164
00165 return maxsize;
00166 }
00167
00168 static SMB_OFF_T recycle_minsize(vfs_handle_struct *handle)
00169 {
00170 SMB_OFF_T minsize;
00171
00172 minsize = conv_str_size(lp_parm_const_string(SNUM(handle->conn),
00173 "recycle", "minsize", NULL));
00174
00175 DEBUG(10, ("recycle: minsize = %lu\n", (long unsigned int)minsize));
00176
00177 return minsize;
00178 }
00179
00180 static mode_t recycle_directory_mode(vfs_handle_struct *handle)
00181 {
00182 int dirmode;
00183 const char *buff;
00184
00185 buff = lp_parm_const_string(SNUM(handle->conn), "recycle", "directory_mode", NULL);
00186
00187 if (buff != NULL ) {
00188 sscanf(buff, "%o", &dirmode);
00189 } else {
00190 dirmode=S_IRUSR | S_IWUSR | S_IXUSR;
00191 }
00192
00193 DEBUG(10, ("recycle: directory_mode = %o\n", dirmode));
00194 return (mode_t)dirmode;
00195 }
00196
00197 static mode_t recycle_subdir_mode(vfs_handle_struct *handle)
00198 {
00199 int dirmode;
00200 const char *buff;
00201
00202 buff = lp_parm_const_string(SNUM(handle->conn), "recycle", "subdir_mode", NULL);
00203
00204 if (buff != NULL ) {
00205 sscanf(buff, "%o", &dirmode);
00206 } else {
00207 dirmode=recycle_directory_mode(handle);
00208 }
00209
00210 DEBUG(10, ("recycle: subdir_mode = %o\n", dirmode));
00211 return (mode_t)dirmode;
00212 }
00213
00214 static BOOL recycle_directory_exist(vfs_handle_struct *handle, const char *dname)
00215 {
00216 SMB_STRUCT_STAT st;
00217
00218 if (SMB_VFS_NEXT_STAT(handle, dname, &st) == 0) {
00219 if (S_ISDIR(st.st_mode)) {
00220 return True;
00221 }
00222 }
00223
00224 return False;
00225 }
00226
00227 static BOOL recycle_file_exist(vfs_handle_struct *handle, const char *fname)
00228 {
00229 SMB_STRUCT_STAT st;
00230
00231 if (SMB_VFS_NEXT_STAT(handle, fname, &st) == 0) {
00232 if (S_ISREG(st.st_mode)) {
00233 return True;
00234 }
00235 }
00236
00237 return False;
00238 }
00239
00240
00241
00242
00243
00244
00245
00246 static SMB_OFF_T recycle_get_file_size(vfs_handle_struct *handle, const char *fname)
00247 {
00248 SMB_STRUCT_STAT st;
00249
00250 if (SMB_VFS_NEXT_STAT(handle, fname, &st) != 0) {
00251 DEBUG(0,("recycle: stat for %s returned %s\n", fname, strerror(errno)));
00252 return (SMB_OFF_T)0;
00253 }
00254
00255 return(st.st_size);
00256 }
00257
00258
00259
00260
00261
00262
00263
00264 static BOOL recycle_create_dir(vfs_handle_struct *handle, const char *dname)
00265 {
00266 size_t len;
00267 mode_t mode;
00268 char *new_dir = NULL;
00269 char *tmp_str = NULL;
00270 char *token;
00271 char *tok_str;
00272 BOOL ret = False;
00273
00274 mode = recycle_directory_mode(handle);
00275
00276 tmp_str = SMB_STRDUP(dname);
00277 ALLOC_CHECK(tmp_str, done);
00278 tok_str = tmp_str;
00279
00280 len = strlen(dname)+1;
00281 new_dir = (char *)SMB_MALLOC(len + 1);
00282 ALLOC_CHECK(new_dir, done);
00283 *new_dir = '\0';
00284 if (dname[0] == '/') {
00285
00286 safe_strcat(new_dir,"/",len);
00287 }
00288
00289
00290 for(token = strtok(tok_str, "/"); token; token = strtok(NULL, "/")) {
00291 safe_strcat(new_dir, token, len);
00292 if (recycle_directory_exist(handle, new_dir))
00293 DEBUG(10, ("recycle: dir %s already exists\n", new_dir));
00294 else {
00295 DEBUG(5, ("recycle: creating new dir %s\n", new_dir));
00296 if (SMB_VFS_NEXT_MKDIR(handle, new_dir, mode) != 0) {
00297 DEBUG(1,("recycle: mkdir failed for %s with error: %s\n", new_dir, strerror(errno)));
00298 ret = False;
00299 goto done;
00300 }
00301 }
00302 safe_strcat(new_dir, "/", len);
00303 mode = recycle_subdir_mode(handle);
00304 }
00305
00306 ret = True;
00307 done:
00308 SAFE_FREE(tmp_str);
00309 SAFE_FREE(new_dir);
00310 return ret;
00311 }
00312
00313
00314
00315
00316
00317
00318 static BOOL matchdirparam(const char **dir_exclude_list, char *path)
00319 {
00320 char *startp = NULL, *endp = NULL;
00321
00322 if (dir_exclude_list == NULL || dir_exclude_list[0] == NULL ||
00323 *dir_exclude_list[0] == '\0' || path == NULL || *path == '\0') {
00324 return False;
00325 }
00326
00327
00328
00329
00330
00331
00332 for (startp = path; startp; startp = endp) {
00333 int i;
00334
00335 while (*startp == '/') {
00336 startp++;
00337 }
00338 endp = strchr(startp, '/');
00339 if (endp) {
00340 *endp = '\0';
00341 }
00342
00343 for(i=0; dir_exclude_list[i] ; i++) {
00344 if(unix_wild_match(dir_exclude_list[i], startp)) {
00345
00346 if (endp) {
00347 *endp = '/';
00348 }
00349 return True;
00350 }
00351 }
00352
00353
00354 if (endp) {
00355 *endp = '/';
00356 }
00357 }
00358
00359 return False;
00360 }
00361
00362
00363
00364
00365
00366
00367
00368 static BOOL matchparam(const char **haystack_list, const char *needle)
00369 {
00370 int i;
00371
00372 if (haystack_list == NULL || haystack_list[0] == NULL ||
00373 *haystack_list[0] == '\0' || needle == NULL || *needle == '\0') {
00374 return False;
00375 }
00376
00377 for(i=0; haystack_list[i] ; i++) {
00378 if(unix_wild_match(haystack_list[i], needle)) {
00379 return True;
00380 }
00381 }
00382
00383 return False;
00384 }
00385
00386
00387
00388
00389 static void recycle_do_touch(vfs_handle_struct *handle, const char *fname, BOOL touch_mtime)
00390 {
00391 SMB_STRUCT_STAT st;
00392 struct timespec ts[2];
00393
00394 if (SMB_VFS_NEXT_STAT(handle, fname, &st) != 0) {
00395 DEBUG(0,("recycle: stat for %s returned %s\n", fname, strerror(errno)));
00396 return;
00397 }
00398 ts[0] = timespec_current();
00399 ts[1] = touch_mtime ? ts[0] : get_mtimespec(&st);
00400
00401 if (SMB_VFS_NEXT_NTIMES(handle, fname, ts) == -1 ) {
00402 DEBUG(0, ("recycle: touching %s failed, reason = %s\n", fname, strerror(errno)));
00403 }
00404 }
00405
00406 extern userdom_struct current_user_info;
00407
00408
00409
00410
00411 static int recycle_unlink(vfs_handle_struct *handle, const char *file_name)
00412 {
00413 connection_struct *conn = handle->conn;
00414 char *path_name = NULL;
00415 char *temp_name = NULL;
00416 char *final_name = NULL;
00417 const char *base;
00418 char *repository = NULL;
00419 int i = 1;
00420 SMB_OFF_T maxsize, minsize;
00421 SMB_OFF_T file_size;
00422 BOOL exist;
00423 int rc = -1;
00424
00425 repository = talloc_sub_advanced(NULL, lp_servicename(SNUM(conn)),
00426 conn->user,
00427 conn->connectpath, conn->gid,
00428 get_current_username(),
00429 current_user_info.domain,
00430 recycle_repository(handle));
00431 ALLOC_CHECK(repository, done);
00432
00433
00434 trim_char(repository, '\0', '/');
00435
00436 if(!repository || *(repository) == '\0') {
00437 DEBUG(3, ("recycle: repository path not set, purging %s...\n", file_name));
00438 rc = SMB_VFS_NEXT_UNLINK(handle, file_name);
00439 goto done;
00440 }
00441
00442
00443 if (strncmp(file_name, repository, strlen(repository)) == 0) {
00444 DEBUG(3, ("recycle: File is within recycling bin, unlinking ...\n"));
00445 rc = SMB_VFS_NEXT_UNLINK(handle, file_name);
00446 goto done;
00447 }
00448
00449 file_size = recycle_get_file_size(handle, file_name);
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464 maxsize = recycle_maxsize(handle);
00465 if(maxsize > 0 && file_size > maxsize) {
00466 DEBUG(3, ("recycle: File %s exceeds maximum recycle size, purging... \n", file_name));
00467 rc = SMB_VFS_NEXT_UNLINK(handle, file_name);
00468 goto done;
00469 }
00470 minsize = recycle_minsize(handle);
00471 if(minsize > 0 && file_size < minsize) {
00472 DEBUG(3, ("recycle: File %s lowers minimum recycle size, purging... \n", file_name));
00473 rc = SMB_VFS_NEXT_UNLINK(handle, file_name);
00474 goto done;
00475 }
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490 base = strrchr(file_name, '/');
00491 if (base == NULL) {
00492 base = file_name;
00493 path_name = SMB_STRDUP("/");
00494 ALLOC_CHECK(path_name, done);
00495 }
00496 else {
00497 path_name = SMB_STRDUP(file_name);
00498 ALLOC_CHECK(path_name, done);
00499 path_name[base - file_name] = '\0';
00500 base++;
00501 }
00502
00503 DEBUG(10, ("recycle: fname = %s\n", file_name));
00504 DEBUG(10, ("recycle: fpath = %s\n", path_name));
00505 DEBUG(10, ("recycle: base = %s\n", base));
00506
00507 if (matchparam(recycle_exclude(handle), base)) {
00508 DEBUG(3, ("recycle: file %s is excluded \n", base));
00509 rc = SMB_VFS_NEXT_UNLINK(handle, file_name);
00510 goto done;
00511 }
00512
00513 if (matchdirparam(recycle_exclude_dir(handle), path_name)) {
00514 DEBUG(3, ("recycle: directory %s is excluded \n", path_name));
00515 rc = SMB_VFS_NEXT_UNLINK(handle, file_name);
00516 goto done;
00517 }
00518
00519 if (recycle_keep_dir_tree(handle) == True) {
00520 asprintf(&temp_name, "%s/%s", repository, path_name);
00521 } else {
00522 temp_name = SMB_STRDUP(repository);
00523 }
00524 ALLOC_CHECK(temp_name, done);
00525
00526 exist = recycle_directory_exist(handle, temp_name);
00527 if (exist) {
00528 DEBUG(10, ("recycle: Directory already exists\n"));
00529 } else {
00530 DEBUG(10, ("recycle: Creating directory %s\n", temp_name));
00531 if (recycle_create_dir(handle, temp_name) == False) {
00532 DEBUG(3, ("recycle: Could not create directory, purging %s...\n", file_name));
00533 rc = SMB_VFS_NEXT_UNLINK(handle, file_name);
00534 goto done;
00535 }
00536 }
00537
00538 asprintf(&final_name, "%s/%s", temp_name, base);
00539 ALLOC_CHECK(final_name, done);
00540 DEBUG(10, ("recycle: recycled file name: %s\n", final_name));
00541
00542
00543 if (recycle_file_exist(handle, final_name)) {
00544 if (recycle_versions(handle) == False || matchparam(recycle_noversions(handle), base) == True) {
00545 DEBUG(3, ("recycle: Removing old file %s from recycle bin\n", final_name));
00546 if (SMB_VFS_NEXT_UNLINK(handle, final_name) != 0) {
00547 DEBUG(1, ("recycle: Error deleting old file: %s\n", strerror(errno)));
00548 }
00549 }
00550 }
00551
00552
00553 i = 1;
00554 while (recycle_file_exist(handle, final_name)) {
00555 SAFE_FREE(final_name);
00556 asprintf(&final_name, "%s/Copy #%d of %s", temp_name, i++, base);
00557 }
00558
00559 DEBUG(10, ("recycle: Moving %s to %s\n", file_name, final_name));
00560 rc = SMB_VFS_NEXT_RENAME(handle, file_name, final_name);
00561 if (rc != 0) {
00562 DEBUG(3, ("recycle: Move error %d (%s), purging file %s (%s)\n", errno, strerror(errno), file_name, final_name));
00563 rc = SMB_VFS_NEXT_UNLINK(handle, file_name);
00564 goto done;
00565 }
00566
00567
00568 if (recycle_touch(handle) == True || recycle_touch_mtime(handle))
00569 recycle_do_touch(handle, final_name, recycle_touch_mtime(handle));
00570
00571 done:
00572 SAFE_FREE(path_name);
00573 SAFE_FREE(temp_name);
00574 SAFE_FREE(final_name);
00575 TALLOC_FREE(repository);
00576 return rc;
00577 }
00578
00579 NTSTATUS vfs_recycle_init(void);
00580 NTSTATUS vfs_recycle_init(void)
00581 {
00582 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "recycle", recycle_ops);
00583
00584 if (!NT_STATUS_IS_OK(ret))
00585 return ret;
00586
00587 vfs_recycle_debug_level = debug_add_class("recycle");
00588 if (vfs_recycle_debug_level == -1) {
00589 vfs_recycle_debug_level = DBGC_VFS;
00590 DEBUG(0, ("vfs_recycle: Couldn't register custom debugging class!\n"));
00591 } else {
00592 DEBUG(10, ("vfs_recycle: Debug class number of 'recycle': %d\n", vfs_recycle_debug_level));
00593 }
00594
00595 return ret;
00596 }