debugfs: add support to properly set and display extended timestamps

This code is partially derived from patches from David Turner to allow
debugfs to properly support extended timestamps.

Cc: David Turner <novalis@novalis.org>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
debian
Theodore Ts'o 2013-12-09 13:55:23 -05:00
parent 79ffbf251e
commit 188960ea4b
6 changed files with 88 additions and 41 deletions

View File

@ -866,27 +866,37 @@ void internal_dump_inode(FILE *out, const char *prefix,
if (is_large_inode && large_inode->i_extra_isize >= 24) {
fprintf(out, "%s ctime: 0x%08x:%08x -- %s", prefix,
inode->i_ctime, large_inode->i_ctime_extra,
time_to_string(inode->i_ctime));
inode_time_to_string(inode->i_ctime,
large_inode->i_ctime_extra));
fprintf(out, "%s atime: 0x%08x:%08x -- %s", prefix,
inode->i_atime, large_inode->i_atime_extra,
time_to_string(inode->i_atime));
inode_time_to_string(inode->i_atime,
large_inode->i_atime_extra));
fprintf(out, "%s mtime: 0x%08x:%08x -- %s", prefix,
inode->i_mtime, large_inode->i_mtime_extra,
time_to_string(inode->i_mtime));
inode_time_to_string(inode->i_mtime,
large_inode->i_mtime_extra));
fprintf(out, "%scrtime: 0x%08x:%08x -- %s", prefix,
large_inode->i_crtime, large_inode->i_crtime_extra,
time_to_string(large_inode->i_crtime));
inode_time_to_string(large_inode->i_crtime,
large_inode->i_crtime_extra));
if (inode->i_dtime)
fprintf(out, "%scrtime: 0x%08x:(%08x) -- %s", prefix,
large_inode->i_dtime, large_inode->i_ctime_extra,
inode_time_to_string(inode->i_dtime,
large_inode->i_ctime_extra));
} else {
fprintf(out, "%sctime: 0x%08x -- %s", prefix, inode->i_ctime,
time_to_string(inode->i_ctime));
time_to_string((__s32) inode->i_ctime));
fprintf(out, "%satime: 0x%08x -- %s", prefix, inode->i_atime,
time_to_string(inode->i_atime));
time_to_string((__s32) inode->i_atime));
fprintf(out, "%smtime: 0x%08x -- %s", prefix, inode->i_mtime,
time_to_string(inode->i_mtime));
time_to_string((__s32) inode->i_mtime));
if (inode->i_dtime)
fprintf(out, "%sdtime: 0x%08x -- %s", prefix,
inode->i_dtime,
time_to_string((__s32) inode->i_dtime));
}
if (inode->i_dtime)
fprintf(out, "%sdtime: 0x%08x -- %s", prefix, inode->i_dtime,
time_to_string(inode->i_dtime));
if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
internal_dump_inode_extra(out, prefix, inode_num,
(struct ext2_inode_large *) inode);
@ -2074,14 +2084,14 @@ err:
#ifndef READ_ONLY
void do_set_current_time(int argc, char *argv[])
{
time_t now;
__s64 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)) {
if (now == -1) {
com_err(argv[0], 0, "Couldn't parse argument as a time: %s\n",
argv[1]);
return;

View File

@ -36,8 +36,9 @@ extern int check_fs_not_open(char *name);
extern int check_fs_read_write(char *name);
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 char *inode_time_to_string(__u32 xtime, __u32 xtime_extra);
extern char *time_to_string(__s64);
extern __s64 string_to_time(const char *);
extern unsigned long parse_ulong(const char *str, const char *cmd,
const char *descr, int *err);
extern unsigned long long parse_ulonglong(const char *str, const char *cmd,
@ -188,7 +189,7 @@ extern void do_list_quota(int argc, char *argv[]);
extern void do_get_quota(int argc, char *argv[]);
/* util.c */
extern time_t string_to_time(const char *arg);
extern __s64 string_to_time(const char *arg);
errcode_t read_list(char *str, blk64_t **list, size_t *len);
/* xattrs.c */

View File

@ -170,7 +170,7 @@ void do_lsdel(int argc, char **argv)
delarray[num_delarray].mode = inode.i_mode;
delarray[num_delarray].uid = inode_uid(inode);
delarray[num_delarray].size = EXT2_I_SIZE(&inode);
delarray[num_delarray].dtime = inode.i_dtime;
delarray[num_delarray].dtime = (__s32) inode.i_dtime;
delarray[num_delarray].num_blocks = lsd.num_blocks;
delarray[num_delarray].free_blocks = lsd.free_blocks;
num_delarray++;

View File

@ -181,10 +181,14 @@ static struct field_set_info inode_fields[] = {
{ "uid", &set_inode.i_uid, &set_inode.osd2.linux2.l_i_uid_high,
2, parse_uint },
{ "size", &set_inode.i_size, &set_inode.i_size_high, 4, parse_uint },
{ "atime", &set_inode.i_atime, NULL, 4, parse_time },
{ "ctime", &set_inode.i_ctime, NULL, 4, parse_time },
{ "mtime", &set_inode.i_mtime, NULL, 4, parse_time },
{ "dtime", &set_inode.i_dtime, NULL, 4, parse_time },
{ "atime", &set_inode.i_atime, &set_inode.i_atime_extra,
4, parse_time },
{ "ctime", &set_inode.i_ctime, &set_inode.i_ctime_extra,
4, parse_time },
{ "mtime", &set_inode.i_mtime, &set_inode.i_mtime_extra,
4, parse_time },
{ "dtime", &set_inode.i_dtime, NULL,
4, parse_time },
{ "gid", &set_inode.i_gid, &set_inode.osd2.linux2.l_i_gid_high,
2, parse_uint },
{ "links_count", &set_inode.i_links_count, NULL, 2, parse_uint },
@ -216,14 +220,15 @@ static struct field_set_info inode_fields[] = {
{ "extra_isize", &set_inode.i_extra_isize, NULL,
2, parse_uint },
{ "ctime_extra", &set_inode.i_ctime_extra, NULL,
4, parse_uint },
4, parse_uint, FLAG_ALIAS },
{ "mtime_extra", &set_inode.i_mtime_extra, NULL,
4, parse_uint },
4, parse_uint, FLAG_ALIAS },
{ "atime_extra", &set_inode.i_atime_extra, NULL,
4, parse_uint },
{ "crtime", &set_inode.i_crtime, NULL, 4, parse_uint },
4, parse_uint, FLAG_ALIAS },
{ "crtime", &set_inode.i_crtime, &set_inode.i_crtime_extra,
4, parse_time },
{ "crtime_extra", &set_inode.i_crtime_extra, NULL,
4, parse_uint },
4, parse_uint, FLAG_ALIAS },
{ "bmap", NULL, NULL, 4, parse_bmap, FLAG_ARRAY },
{ 0, 0, 0, 0 }
};
@ -555,21 +560,31 @@ static errcode_t parse_string(struct field_set_info *info,
}
static errcode_t parse_time(struct field_set_info *info,
char *field EXT2FS_ATTR((unused)), char *arg)
char *field, char *arg)
{
time_t t;
__u32 *ptr32;
__s64 t;
__u32 t_low, t_high;
__u32 *ptr_low, *ptr_high;
int suffix = check_suffix(field);
ptr32 = (__u32 *) info->ptr;
if (check_suffix(field))
return parse_uint(info, field, arg);
ptr_low = (__u32 *) info->ptr;
ptr_high = (__u32 *) info->ptr2;
t = string_to_time(arg);
if (t == ((time_t) -1)) {
if (t == -1) {
fprintf(stderr, "Couldn't parse '%s' for field %s.\n",
arg, info->name);
return EINVAL;
}
*ptr32 = t;
t_low = (__u32) t;
t_high = ((t - (__s32)t) >> 32) & EXT4_EPOCH_MASK;
*ptr_low = t_low;
if (ptr_high)
*ptr_high = (*ptr_high & ~EXT4_EPOCH_MASK) | t_high;
return 0;
}

View File

@ -186,11 +186,19 @@ int check_fs_bitmaps(char *name)
return 0;
}
char *inode_time_to_string(__u32 xtime, __u32 xtime_extra)
{
__s64 t = (__s32) xtime;
t += (__s64) (xtime_extra & EXT4_EPOCH_MASK) << 32;
return time_to_string(t);
}
/*
* This function takes a __u32 time value and converts it to a string,
* This function takes a __s64 time value and converts it to a string,
* using ctime
*/
char *time_to_string(__u32 cl)
char *time_to_string(__s64 cl)
{
static int do_gmt = -1;
time_t t = (time_t) cl;
@ -211,10 +219,10 @@ char *time_to_string(__u32 cl)
* Parse a string as a time. Return ((time_t)-1) if the string
* doesn't appear to be a sane time.
*/
time_t string_to_time(const char *arg)
extern __s64 string_to_time(const char *arg)
{
struct tm ts;
time_t ret;
__s64 ret;
char *tmp;
if (strcmp(arg, "now") == 0) {
@ -224,14 +232,18 @@ time_t string_to_time(const char *arg)
/* interpret it as an integer */
arg++;
fallback:
ret = strtoul(arg, &tmp, 0);
ret = strtoll(arg+1, &tmp, 0);
if (*tmp)
return ((time_t) -1);
return -1;
return ret;
}
memset(&ts, 0, sizeof(ts));
#ifdef HAVE_STRPTIME
tmp = strptime(arg, "%Y%m%d%H%M%S", &ts);
if (tmp == NULL)
tmp = strptime(arg, "%Y%m%d%H%M", &ts);
if (tmp == NULL)
tmp = strptime(arg, "%Y%m%d", &ts);
if (tmp == NULL)
goto fallback;
#else
@ -240,9 +252,9 @@ time_t string_to_time(const char *arg)
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_mday <= 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
ts.tm_min > 59 || ts.tm_sec > 61)
ts.tm_mday = 0;
goto fallback;
#endif
ts.tm_isdst = -1;
/* strptime() may only update the specified fields, which does not
@ -260,8 +272,10 @@ time_t string_to_time(const char *arg)
((ts.tm_mon - (ts.tm_mon > 7)) / 2) -
2 * (ts.tm_mon > 1) + ts.tm_mday - 1;
ret = ts.tm_sec + ts.tm_min*60 + ts.tm_hour*3600 + ts.tm_yday*86400 +
(ts.tm_year-70)*31536000 + ((ts.tm_year-69)/4)*86400 -
((ts.tm_year-1)/100)*86400 + ((ts.tm_year+299)/400)*86400;
((__s64) ts.tm_year-70)*31536000 +
(((__s64) ts.tm_year-69)/4)*86400 -
(((__s64) ts.tm_year-1)/100)*86400 +
(((__s64) ts.tm_year+299)/400)*86400;
return ret;
}

View File

@ -1010,6 +1010,13 @@ struct ext2_dir_entry_tail {
EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
/*
* Constants for ext4's extended time encoding
*/
#define EXT4_EPOCH_BITS 2
#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
#define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS)
/*
* This structure is used for multiple mount protection. It is written
* into the block number saved in the s_mmp_block field in the superblock.