=== 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(&timestamp, 0, sizeof(timestamp));
+	cstr = strptime(name, fmt, &timestamp);
+
+	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(&timestamp);
+		gmtime_r(&timestamp_t, &timestamp);
+	}
+	strftime(converted, converted_length, SHADOW_COPY_GMT_FORMAT, &timestamp);
+	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(&timestamp, 0, sizeof(timestamp));
+	cstr = strptime(path, SHADOW_COPY_GMT_FORMAT, &timestamp);
+
+	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(&timestamp);
+		localtime_r(&timestamp_t, &timestamp);
+	}
+	strftime(snapname, MAXPATHLEN, fmt, &timestamp);
+
+	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}

