New debugfs command: set_current_time

Generalize the time parsing code and move it to
util.c:string_to_time().  Add new command, set_current_time, which
sets the time used to set the filesystems's time fields.

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
diff --git a/debugfs/ChangeLog b/debugfs/ChangeLog
index dd96b58..f92dc80 100644
--- a/debugfs/ChangeLog
+++ b/debugfs/ChangeLog
@@ -1,3 +1,11 @@
+2005-09-24  Theodore Ts'o  <tytso@mit.edu>
+
+	* set_fields.c (parse_time), util.c (string_to_time), debugfs.c
+		(do_set_current_time): Generalize the time parsing code
+		and move it to util.c:string_to_time().  Add new command,
+		set_current_time, which sets the time used to set the
+		filesystems's time fields.
+
 2005-09-06  Theodore Ts'o  <tytso@mit.edu>
 
 	* set_fields.c: Fix set_inode_field so it can properly set the
diff --git a/debugfs/debug_cmds.ct b/debugfs/debug_cmds.ct
index 06e6fe1..a3d3e2d 100644
--- a/debugfs/debug_cmds.ct
+++ b/debugfs/debug_cmds.ct
@@ -148,5 +148,8 @@
 request	do_dump_unused, "Dump unused blocks",
 	dump_unused;
 
+request do_set_current_time, "Set current time to use when setting filesystme fields",
+	set_current_time;
+
 end;
 
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index c2baee9..20d1f4b 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -1295,7 +1295,8 @@
 	ext2fs_inode_alloc_stats2(current_fs, newfile, +1, 0);
 	memset(&inode, 0, sizeof(inode));
 	inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG;
-	inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
+	inode.i_atime = inode.i_ctime = inode.i_mtime = 
+		current_fs->now ? current_fs->now : time(0);
 	inode.i_links_count = 1;
 	inode.i_size = statbuf.st_size;
 	if (debugfs_write_new_inode(newfile, &inode, argv[0])) {
@@ -1382,7 +1383,8 @@
 	ext2fs_mark_ib_dirty(current_fs);
 	memset(&inode, 0, sizeof(inode));
 	inode.i_mode = mode;
-	inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
+	inode.i_atime = inode.i_ctime = inode.i_mtime = 
+		current_fs->now ? current_fs->now : time(0);
 	if ((major < 256) && (minor < 256)) {
 		inode.i_block[0] = major*256+minor;
 		inode.i_block[1] = 0;
@@ -1454,7 +1456,7 @@
 
 	if (debugfs_read_inode(inode, &inode_buf, 0))
 		return;
-	inode_buf.i_dtime = time(NULL);
+	inode_buf.i_dtime = current_fs->now ? current_fs->now : time(0);
 	if (debugfs_write_inode(inode, &inode_buf, 0))
 		return;
 	if (!ext2fs_inode_has_valid_blocks(&inode_buf))
@@ -1697,7 +1699,27 @@
 
 }
 
+void do_set_current_time(int argc, char *argv[])
+{
+	ext2_ino_t	ino;
+	unsigned long 	group, block, block_nr, offset;
+	time_t now;
 
+	if (common_args_process(argc, argv, 2, 2, argv[0],
+				"<time>", 0))
+		return;
+
+	now = string_to_time(argv[1]);
+	if (now == ((time_t) -1)) {
+		com_err(argv[0], 0, "Couldn't parse argument as a time: %s\n",
+			argv[1]);
+		return;
+
+	} else {
+		printf("Setting current time to %s\n", time_to_string(now));
+		current_fs->now = now;
+	}
+}
 
 static int source_file(const char *cmd_file, int sci_idx)
 {
diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h
index 56fa3a9..3081cce 100644
--- a/debugfs/debugfs.h
+++ b/debugfs/debugfs.h
@@ -31,6 +31,7 @@
 extern int check_fs_bitmaps(char *name);
 extern ext2_ino_t string_to_inode(char *str);
 extern char *time_to_string(__u32);
+extern time_t string_to_time(const char *);
 extern unsigned long parse_ulong(const char *str, const char *cmd,
 				 const char *descr, int *err);
 extern int strtoblk(const char *cmd, const char *str, blk_t *ret);
diff --git a/debugfs/lsdel.c b/debugfs/lsdel.c
index 71febd5..774b88e 100644
--- a/debugfs/lsdel.c
+++ b/debugfs/lsdel.c
@@ -81,7 +81,7 @@
 	int			i;
  	long			secs = 0;
  	char			*tmp;
-	time_t			now = time(0);
+	time_t			now = current_fs->now ? current_fs->now : time(0);
 	FILE			*out;
 	
 	if (common_args_process(argc, argv, 1, 2, "ls_deleted_inodes",
diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index ef933a1..188d016 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -278,33 +278,18 @@
 
 static errcode_t parse_time(struct field_set_info *info, char *arg)
 {
-	struct	tm	ts;
-	__u32		*ptr32;
+	__u32		*ptr32, t;
 
 	ptr32 = (__u32 *) info->ptr;
 
-	if (strcmp(arg, "now") == 0) {
-		*ptr32 = time(0);
-		return 0;
+	t = string_to_time(arg);
+
+	if (t == ((time_t) -1)) {
+		fprintf(stderr, "Couldn't parse '%s' for field %s.\n",
+			arg, info->name);
+		return EINVAL;
 	}
-	memset(&ts, 0, sizeof(ts));
-#ifdef HAVE_STRPTIME
-	strptime(arg, "%Y%m%d%H%M%S", &ts);
-#else
-	sscanf(arg, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
-	       &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
-	ts.tm_year -= 1900;
-	ts.tm_mon -= 1;
-	if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
-	    ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
-	    ts.tm_min > 59 || ts.tm_sec > 61)
-		ts.tm_mday = 0;
-#endif
-	if (ts.tm_mday == 0) {
-		/* Try it as an integer... */
-		return parse_uint(info, arg);
-	}
-	*ptr32 = mktime(&ts);
+	*ptr32 = t;
 	return 0;
 }
 
diff --git a/debugfs/util.c b/debugfs/util.c
index ce1a2f3..cbbc99b 100644
--- a/debugfs/util.c
+++ b/debugfs/util.c
@@ -202,6 +202,42 @@
 }
 
 /*
+ * Parse a string as a time.  Return ((time_t)-1) if the string
+ * doesn't appear to be a sane time.
+ */
+extern time_t string_to_time(const char *arg)
+{
+	struct	tm	ts;
+	unsigned long	ret;
+	char *tmp;
+
+	if (strcmp(arg, "now") == 0) {
+		return time(0);
+	}
+	memset(&ts, 0, sizeof(ts));
+#ifdef HAVE_STRPTIME
+	strptime(arg, "%Y%m%d%H%M%S", &ts);
+#else
+	sscanf(arg, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
+	       &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
+	ts.tm_year -= 1900;
+	ts.tm_mon -= 1;
+	if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
+	    ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
+	    ts.tm_min > 59 || ts.tm_sec > 61)
+		ts.tm_mday = 0;
+#endif
+	if (ts.tm_mday == 0) {
+		/* Try it as an integer... */
+
+		ret = strtoul(arg, &tmp, 0);
+		if (*tmp)
+			return ((time_t) -1);
+	}
+	return mktime(&ts);
+}
+
+/*
  * This function will convert a string to an unsigned long, printing
  * an error message if it fails, and returning success or failure in err.
  */