=== modified file 'source/modules/vfs_shadow_copy.c' --- source/modules/vfs_shadow_copy.c 2007-04-26 15:45:09 +0000 +++ source/modules/vfs_shadow_copy.c 2007-04-26 15:46:05 +0000 @@ -46,8 +46,52 @@ Note: Files must differ to be displayed via Windows Explorer! Directories are always displayed... + + In addition it is possible to specify additional parameters + that eliminate the need to utilize the @GMT- directories + or symbolic links. + + path + Specifies the path to the directory that contains the + snapshots. + + format + Specifies the format of the snapshot names in str[fp]time + notation except that $ characters are used in place of % + characters. + + subpath + Specifies the subdirectory under the snapshot that contains + all of the files for this shadow copy. + + localtime (boolean) + Treat the snapshot names as being in localtime instead of + the default of GMT. + + sort + Sorts the shadow copies, specified as "asc" or "desc". + The default is to leave them unsorted. + + Below is example usage for a single large filesystem mounted + at /home that contains all of the home directories. The + snapshots reside in /snapshots/home. + + [homes] + path = /home/%U + public = no + writable = yes + printable = no + vfs object = shadow_copy + shadow_copy: path = /snapshots/home + shadow_copy: subpath = %U + shadow_copy: format = $Y.$m.$d-$H.$M.$S + shadow_copy: sort = desc + shadow_copy: localtime = yes + */ +extern userdom_struct current_user_info; + static int vfs_shadow_copy_debug_level = DBGC_VFS; #undef DBGC_CLASS @@ -56,6 +100,11 @@ #define SHADOW_COPY_PREFIX "@GMT-" #define SHADOW_COPY_SAMPLE "@GMT-2004.02.18-15.44.00" +#define SHADOW_COPY_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S" +#define SHADOW_COPY_DEFAULT_FORMAT "@GMT-$Y.$m.$d-$H.$M.$S" +#define SHADOW_COPY_DEFAULT_PATH "." +#define SHADOW_COPY_DEFAULT_SUBPATH "." +#define SHADOW_COPY_DEFAULT_LOCALTIME (False) #define SHADOW_COPY_DEFAULT_SORT "" typedef struct { @@ -110,13 +159,146 @@ return False; } +static BOOL shadow_copy_snapshot_to_gmt(vfs_handle_struct *handle, const char *name, char *converted, int converted_length) +{ + char *cstr = NULL; + struct tm timestamp; + time_t timestamp_t; + + char *fmt = alloc_sub_basic(get_current_username(), current_user_info.domain, + lp_parm_const_string(SNUM(handle->conn), + "shadow_copy", "format", SHADOW_COPY_DEFAULT_FORMAT)); + char *tmp; + + if (fmt == NULL) { + DEBUG(0, ("shadow_copy_snapshot_to_gmt: alloc_sub_basic failed for format\n")); + return False; + } + + /* replace $ in the parameter with % */ + for (tmp = fmt; *tmp != '\0'; tmp++) { + if (*tmp == '$') { + *tmp = '%'; + } + } + + memset(×tamp, 0, sizeof(timestamp)); + cstr = strptime(name, fmt, ×tamp); + + if (cstr == NULL) { + DEBUG(10, ("shadow_copy_snapshot_to_gmt: no match %s: %s\n", fmt, name)); + SAFE_FREE(fmt); + return False; + } + + DEBUG(10, ("shadow_copy_snapshot_to_gmt: match %s: %s\n", fmt, name)); + if (lp_parm_bool(SNUM(handle->conn), "shadow_copy", "localtime", SHADOW_COPY_DEFAULT_LOCALTIME)) { + timestamp.tm_isdst = -1; + timestamp_t = mktime(×tamp); + gmtime_r(×tamp_t, ×tamp); + } + strftime(converted, converted_length, SHADOW_COPY_GMT_FORMAT, ×tamp); + SAFE_FREE(fmt); + return True; +} + +static BOOL shadow_copy_file_to_snapshot_path(vfs_handle_struct *handle, const char *path, char *converted, int converted_length) +{ + /* all conversions start with the initial portion of the path matching + * @GMT-xxxxxxx + * + * take the full string and pass it along to strptime */ + + char *cstr = NULL; + struct tm timestamp; + time_t timestamp_t; + char snapname[MAXPATHLEN]; + char *snappath = NULL; + char *subpath = NULL; + + char *fmt = alloc_sub_basic(get_current_username(), current_user_info.domain, + lp_parm_const_string(SNUM(handle->conn), + "shadow_copy", "format", SHADOW_COPY_DEFAULT_FORMAT)); + char *tmp; + + if (fmt == NULL) { + DEBUG(0, ("shadow_copy_file_to_snapshot_path: alloc_sub_basic failed for format\n")); + return False; + } + + /* replace $ in the parameter with % */ + for (tmp = fmt; *tmp != '\0'; tmp++) { + if (*tmp == '$') { + *tmp = '%'; + } + } + + memset(×tamp, 0, sizeof(timestamp)); + cstr = strptime(path, SHADOW_COPY_GMT_FORMAT, ×tamp); + + if (cstr == NULL) { + /* string doesn't match the required SHADOW_COPY_GMT_FORMAT so just + * return the original path */ + + strncpy(converted, path, converted_length); + SAFE_FREE(fmt); + return False; + } + + /* cstr is the remaining portion of the path after the @GMT-xxx */ + + if (lp_parm_bool(SNUM(handle->conn), "shadow_copy", "localtime", SHADOW_COPY_DEFAULT_LOCALTIME)) { + timestamp_t = timegm(×tamp); + localtime_r(×tamp_t, ×tamp); + } + strftime(snapname, MAXPATHLEN, fmt, ×tamp); + + snappath = alloc_sub_basic(get_current_username(), current_user_info.domain, + lp_parm_const_string(SNUM(handle->conn), + "shadow_copy", "path", SHADOW_COPY_DEFAULT_PATH)); + if (snappath == NULL) { + DEBUG(0, ("shadow_copy_file_to_snapshot_path: alloc_sub_basic failed for path\n")); + SAFE_FREE(fmt); + return False; + } + + subpath = alloc_sub_basic(get_current_username(), current_user_info.domain, + lp_parm_const_string(SNUM(handle->conn), + "shadow_copy", "subpath", SHADOW_COPY_DEFAULT_SUBPATH)); + if (subpath == NULL) { + DEBUG(0, ("shadow_copy_file_to_snapshot_path: alloc_sub_basic failed for subpath\n")); + SAFE_FREE(fmt); + SAFE_FREE(snappath); + return False; + } + + /* path/snap/subpath/filepath */ + snprintf(converted, converted_length, "%s/%s/%s/%s", + snappath, + snapname, + subpath, + cstr); + + SAFE_FREE(fmt); + SAFE_FREE(snappath); + SAFE_FREE(subpath); + return True; +} + static SMB_STRUCT_DIR *shadow_copy_opendir(vfs_handle_struct *handle, const char *fname, const char *mask, uint32 attr) { shadow_copy_Dir *dirp; - SMB_STRUCT_DIR *p = SMB_VFS_NEXT_OPENDIR(handle,fname,mask,attr); + SMB_STRUCT_DIR *p = NULL; + + char newpath[MAXPATHLEN]; + shadow_copy_file_to_snapshot_path(handle, fname, newpath, MAXPATHLEN); + + DEBUG(10,("shadow_copy_opendir: [%s] -> [%s]\n",fname,newpath)); + + p = SMB_VFS_NEXT_OPENDIR(handle,newpath,mask,attr); if (!p) { - DEBUG(0,("shadow_copy_opendir: SMB_VFS_NEXT_OPENDIR() failed for [%s]\n",fname)); + DEBUG(0,("shadow_copy_opendir: SMB_VFS_NEXT_OPENDIR() failed for [%s]\n",newpath)); return NULL; } @@ -203,15 +385,49 @@ return 0; } +static int shadow_copy_stat(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf) +{ + char newpath[MAXPATHLEN]; + + shadow_copy_file_to_snapshot_path(handle, fname, newpath, MAXPATHLEN); + DEBUG(10,("shadow_copy_stat: [%s] -> [%s]\n",fname,newpath)); + + return SMB_VFS_NEXT_STAT(handle, newpath, sbuf); +} + +static int shadow_copy_open(vfs_handle_struct *handle, const char *fname, files_struct *fsp, int flags, mode_t mode) +{ + char newpath[MAXPATHLEN]; + + shadow_copy_file_to_snapshot_path(handle, fname, newpath, MAXPATHLEN); + DEBUG(10,("shadow_copy_open: [%s] -> [%s]\n",fname,newpath)); + + return SMB_VFS_NEXT_OPEN(handle, newpath, fsp, flags, mode); +} + static int shadow_copy_get_shadow_copy_data(vfs_handle_struct *handle, files_struct *fsp, SHADOW_COPY_DATA *shadow_copy_data, BOOL labels) { - SMB_STRUCT_DIR *p = SMB_VFS_NEXT_OPENDIR(handle,fsp->conn->connectpath,NULL,0); + SMB_STRUCT_DIR *p = NULL; + char snapname[MAXPATHLEN]; + char *path = alloc_sub_basic(get_current_username(), current_user_info.domain, + lp_parm_const_string(SNUM(handle->conn), + "shadow_copy", "path", fsp->conn->connectpath)); + + if (path == NULL) { + DEBUG(0, ("shadow_copy_get_shadow_copy_data: alloc_sub_basic failed for path\n")); + return -1; + } + + DEBUG(10,("shadow_copy_get_shadow_copy_data: [%s]\n",path)); + + p = SMB_VFS_NEXT_OPENDIR(handle,path,NULL,0); shadow_copy_data->num_volumes = 0; shadow_copy_data->labels = NULL; if (!p) { - DEBUG(0,("shadow_copy_get_shadow_copy_data: SMB_VFS_NEXT_OPENDIR() failed for [%s]\n",fsp->conn->connectpath)); + DEBUG(0,("shadow_copy_get_shadow_copy_data: SMB_VFS_NEXT_OPENDIR() failed for [%s]\n",path)); + SAFE_FREE(path); return -1; } @@ -225,8 +441,8 @@ } /* */ - if (!shadow_copy_match_name(d->d_name)) { - DEBUG(10,("shadow_copy_get_shadow_copy_data: ignore [%s]\n",d->d_name)); + if (!shadow_copy_snapshot_to_gmt(handle, d->d_name, snapname, MAXPATHLEN)) { + DEBUG(6,("shadow_copy_get_shadow_copy_data: ignore [%s]\n",d->d_name)); continue; } @@ -243,10 +459,11 @@ if (tlabels == NULL) { DEBUG(0,("shadow_copy_get_shadow_copy_data: Out of memory\n")); SMB_VFS_NEXT_CLOSEDIR(handle,p); + SAFE_FREE(path); return -1; } - snprintf(tlabels[shadow_copy_data->num_volumes++], sizeof(*tlabels), "%s",d->d_name); + snprintf(tlabels[shadow_copy_data->num_volumes++], sizeof(*tlabels), "%s",snapname); shadow_copy_data->labels = tlabels; } @@ -254,6 +471,7 @@ shadow_copy_sort_data(handle, shadow_copy_data); SMB_VFS_NEXT_CLOSEDIR(handle,p); + SAFE_FREE(path); return 0; } @@ -267,6 +485,9 @@ {SMB_VFS_OP(shadow_copy_rewinddir), SMB_VFS_OP_REWINDDIR, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(shadow_copy_closedir), SMB_VFS_OP_CLOSEDIR, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy_get_shadow_copy_data), SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}