| 1 |
mpagano |
1335 |
New mount option ("tz=UTC") for FAT (vfat/msdos) filesystems allowing |
| 2 |
|
|
timestamps to be in coordinated universal time (UTC) rather than |
| 3 |
|
|
local time in applications where doing this is advantageous (like |
| 4 |
|
|
digital cameras, etc.) |
| 5 |
|
|
|
| 6 |
|
|
Signed-off-by: Joe Peterson <joe@skyrush.com> |
| 7 |
|
|
Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> |
| 8 |
|
|
--- |
| 9 |
|
|
|
| 10 |
|
|
diff -puNr a/fs/fat/dir.c b/fs/fat/dir.c |
| 11 |
|
|
--- a/fs/fat/dir.c 2008-06-25 08:53:35.676901351 -0600 |
| 12 |
|
|
+++ b/fs/fat/dir.c 2008-06-26 12:58:56.096272279 -0600 |
| 13 |
|
|
@@ -1082,7 +1082,7 @@ int fat_alloc_new_dir(struct inode *dir, |
| 14 |
|
|
goto error_free; |
| 15 |
|
|
} |
| 16 |
|
|
|
| 17 |
|
|
- fat_date_unix2dos(ts->tv_sec, &time, &date); |
| 18 |
|
|
+ fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); |
| 19 |
|
|
|
| 20 |
|
|
de = (struct msdos_dir_entry *)bhs[0]->b_data; |
| 21 |
|
|
/* filling the new directory slots ("." and ".." entries) */ |
| 22 |
|
|
diff -puNr a/fs/fat/inode.c b/fs/fat/inode.c |
| 23 |
|
|
--- a/fs/fat/inode.c 2008-06-25 08:53:35.676901351 -0600 |
| 24 |
|
|
+++ b/fs/fat/inode.c 2008-06-26 14:57:07.087942764 -0600 |
| 25 |
|
|
@@ -382,17 +382,20 @@ static int fat_fill_inode(struct inode * |
| 26 |
|
|
inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) |
| 27 |
|
|
& ~((loff_t)sbi->cluster_size - 1)) >> 9; |
| 28 |
|
|
inode->i_mtime.tv_sec = |
| 29 |
|
|
- date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date)); |
| 30 |
|
|
+ date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date), |
| 31 |
|
|
+ sbi->options.tz_utc); |
| 32 |
|
|
inode->i_mtime.tv_nsec = 0; |
| 33 |
|
|
if (sbi->options.isvfat) { |
| 34 |
|
|
int secs = de->ctime_cs / 100; |
| 35 |
|
|
int csecs = de->ctime_cs % 100; |
| 36 |
|
|
inode->i_ctime.tv_sec = |
| 37 |
|
|
date_dos2unix(le16_to_cpu(de->ctime), |
| 38 |
|
|
- le16_to_cpu(de->cdate)) + secs; |
| 39 |
|
|
+ le16_to_cpu(de->cdate), |
| 40 |
|
|
+ sbi->options.tz_utc) + secs; |
| 41 |
|
|
inode->i_ctime.tv_nsec = csecs * 10000000; |
| 42 |
|
|
inode->i_atime.tv_sec = |
| 43 |
|
|
- date_dos2unix(0, le16_to_cpu(de->adate)); |
| 44 |
|
|
+ date_dos2unix(0, le16_to_cpu(de->adate), |
| 45 |
|
|
+ sbi->options.tz_utc); |
| 46 |
|
|
inode->i_atime.tv_nsec = 0; |
| 47 |
|
|
} else |
| 48 |
|
|
inode->i_ctime = inode->i_atime = inode->i_mtime; |
| 49 |
|
|
@@ -592,11 +595,14 @@ retry: |
| 50 |
|
|
raw_entry->attr = fat_attr(inode); |
| 51 |
|
|
raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart); |
| 52 |
|
|
raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); |
| 53 |
|
|
- fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, &raw_entry->date); |
| 54 |
|
|
+ fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, |
| 55 |
|
|
+ &raw_entry->date, sbi->options.tz_utc); |
| 56 |
|
|
if (sbi->options.isvfat) { |
| 57 |
|
|
__le16 atime; |
| 58 |
|
|
- fat_date_unix2dos(inode->i_ctime.tv_sec,&raw_entry->ctime,&raw_entry->cdate); |
| 59 |
|
|
- fat_date_unix2dos(inode->i_atime.tv_sec,&atime,&raw_entry->adate); |
| 60 |
|
|
+ fat_date_unix2dos(inode->i_ctime.tv_sec, &raw_entry->ctime, |
| 61 |
|
|
+ &raw_entry->cdate, sbi->options.tz_utc); |
| 62 |
|
|
+ fat_date_unix2dos(inode->i_atime.tv_sec, &atime, |
| 63 |
|
|
+ &raw_entry->adate, sbi->options.tz_utc); |
| 64 |
|
|
raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 + |
| 65 |
|
|
inode->i_ctime.tv_nsec / 10000000; |
| 66 |
|
|
} |
| 67 |
|
|
@@ -836,6 +842,8 @@ static int fat_show_options(struct seq_f |
| 68 |
|
|
} |
| 69 |
|
|
if (sbi->options.flush) |
| 70 |
|
|
seq_puts(m, ",flush"); |
| 71 |
|
|
+ if (opts->tz_utc) |
| 72 |
|
|
+ seq_puts(m, ",tz=UTC"); |
| 73 |
|
|
|
| 74 |
|
|
return 0; |
| 75 |
|
|
} |
| 76 |
|
|
@@ -848,7 +856,7 @@ enum { |
| 77 |
|
|
Opt_charset, Opt_shortname_lower, Opt_shortname_win95, |
| 78 |
|
|
Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, |
| 79 |
|
|
Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, |
| 80 |
|
|
- Opt_obsolate, Opt_flush, Opt_err, |
| 81 |
|
|
+ Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err, |
| 82 |
|
|
}; |
| 83 |
|
|
|
| 84 |
|
|
static match_table_t fat_tokens = { |
| 85 |
|
|
@@ -883,6 +891,7 @@ static match_table_t fat_tokens = { |
| 86 |
|
|
{Opt_obsolate, "cvf_options=%100s"}, |
| 87 |
|
|
{Opt_obsolate, "posix"}, |
| 88 |
|
|
{Opt_flush, "flush"}, |
| 89 |
|
|
+ {Opt_tz_utc, "tz=UTC"}, |
| 90 |
|
|
{Opt_err, NULL}, |
| 91 |
|
|
}; |
| 92 |
|
|
static match_table_t msdos_tokens = { |
| 93 |
|
|
@@ -947,6 +956,7 @@ static int parse_options(char *options, |
| 94 |
|
|
opts->utf8 = opts->unicode_xlate = 0; |
| 95 |
|
|
opts->numtail = 1; |
| 96 |
|
|
opts->usefree = opts->nocase = 0; |
| 97 |
|
|
+ opts->tz_utc = 0; |
| 98 |
|
|
*debug = 0; |
| 99 |
|
|
|
| 100 |
|
|
if (!options) |
| 101 |
|
|
@@ -1036,6 +1046,9 @@ static int parse_options(char *options, |
| 102 |
|
|
case Opt_flush: |
| 103 |
|
|
opts->flush = 1; |
| 104 |
|
|
break; |
| 105 |
|
|
+ case Opt_tz_utc: |
| 106 |
|
|
+ opts->tz_utc = 1; |
| 107 |
|
|
+ break; |
| 108 |
|
|
|
| 109 |
|
|
/* msdos specific */ |
| 110 |
|
|
case Opt_dots: |
| 111 |
|
|
diff -puNr a/fs/fat/misc.c b/fs/fat/misc.c |
| 112 |
|
|
--- a/fs/fat/misc.c 2008-06-25 08:53:35.676901351 -0600 |
| 113 |
|
|
+++ b/fs/fat/misc.c 2008-06-26 12:59:08.627376422 -0600 |
| 114 |
|
|
@@ -142,7 +142,7 @@ static int day_n[] = { |
| 115 |
|
|
}; |
| 116 |
|
|
|
| 117 |
|
|
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ |
| 118 |
|
|
-int date_dos2unix(unsigned short time, unsigned short date) |
| 119 |
|
|
+int date_dos2unix(unsigned short time, unsigned short date, int tz_utc) |
| 120 |
|
|
{ |
| 121 |
|
|
int month, year, secs; |
| 122 |
|
|
|
| 123 |
|
|
@@ -156,16 +156,18 @@ int date_dos2unix(unsigned short time, u |
| 124 |
|
|
((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && |
| 125 |
|
|
month < 2 ? 1 : 0)+3653); |
| 126 |
|
|
/* days since 1.1.70 plus 80's leap day */ |
| 127 |
|
|
- secs += sys_tz.tz_minuteswest*60; |
| 128 |
|
|
+ if (!tz_utc) |
| 129 |
|
|
+ secs += sys_tz.tz_minuteswest*60; |
| 130 |
|
|
return secs; |
| 131 |
|
|
} |
| 132 |
|
|
|
| 133 |
|
|
/* Convert linear UNIX date to a MS-DOS time/date pair. */ |
| 134 |
|
|
-void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date) |
| 135 |
|
|
+void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, int tz_utc) |
| 136 |
|
|
{ |
| 137 |
|
|
int day, year, nl_day, month; |
| 138 |
|
|
|
| 139 |
|
|
- unix_date -= sys_tz.tz_minuteswest*60; |
| 140 |
|
|
+ if (!tz_utc) |
| 141 |
|
|
+ unix_date -= sys_tz.tz_minuteswest*60; |
| 142 |
|
|
|
| 143 |
|
|
/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ |
| 144 |
|
|
if (unix_date < 315532800) |
| 145 |
|
|
diff -puNr a/fs/msdos/namei.c b/fs/msdos/namei.c |
| 146 |
|
|
--- a/fs/msdos/namei.c 2008-06-25 08:53:35.696902410 -0600 |
| 147 |
|
|
+++ b/fs/msdos/namei.c 2008-06-26 12:58:42.255055811 -0600 |
| 148 |
|
|
@@ -243,6 +243,7 @@ static int msdos_add_entry(struct inode |
| 149 |
|
|
int is_dir, int is_hid, int cluster, |
| 150 |
|
|
struct timespec *ts, struct fat_slot_info *sinfo) |
| 151 |
|
|
{ |
| 152 |
|
|
+ struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); |
| 153 |
|
|
struct msdos_dir_entry de; |
| 154 |
|
|
__le16 time, date; |
| 155 |
|
|
int err; |
| 156 |
|
|
@@ -252,7 +253,7 @@ static int msdos_add_entry(struct inode |
| 157 |
|
|
if (is_hid) |
| 158 |
|
|
de.attr |= ATTR_HIDDEN; |
| 159 |
|
|
de.lcase = 0; |
| 160 |
|
|
- fat_date_unix2dos(ts->tv_sec, &time, &date); |
| 161 |
|
|
+ fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); |
| 162 |
|
|
de.cdate = de.adate = 0; |
| 163 |
|
|
de.ctime = 0; |
| 164 |
|
|
de.ctime_cs = 0; |
| 165 |
|
|
diff -puNr a/fs/vfat/namei.c b/fs/vfat/namei.c |
| 166 |
|
|
--- a/fs/vfat/namei.c 2008-06-25 08:53:35.686902663 -0600 |
| 167 |
|
|
+++ b/fs/vfat/namei.c 2008-06-26 12:58:27.073724049 -0600 |
| 168 |
|
|
@@ -621,7 +621,7 @@ shortname: |
| 169 |
|
|
memcpy(de->name, msdos_name, MSDOS_NAME); |
| 170 |
|
|
de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; |
| 171 |
|
|
de->lcase = lcase; |
| 172 |
|
|
- fat_date_unix2dos(ts->tv_sec, &time, &date); |
| 173 |
|
|
+ fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); |
| 174 |
|
|
de->time = de->ctime = time; |
| 175 |
|
|
de->date = de->cdate = de->adate = date; |
| 176 |
|
|
de->ctime_cs = 0; |
| 177 |
|
|
diff -puNr a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h |
| 178 |
|
|
--- a/include/linux/msdos_fs.h 2008-06-25 08:53:17.435303045 -0600 |
| 179 |
|
|
+++ b/include/linux/msdos_fs.h 2008-06-26 14:58:40.626135522 -0600 |
| 180 |
|
|
@@ -205,7 +205,8 @@ struct fat_mount_options { |
| 181 |
|
|
atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */ |
| 182 |
|
|
flush:1, /* write things quickly */ |
| 183 |
|
|
nocase:1, /* Does this need case conversion? 0=need case conversion*/ |
| 184 |
|
|
- usefree:1; /* Use free_clusters for FAT32 */ |
| 185 |
|
|
+ usefree:1, /* Use free_clusters for FAT32 */ |
| 186 |
|
|
+ tz_utc:1; /* Filesystem timestamps are in UTC */ |
| 187 |
|
|
}; |
| 188 |
|
|
|
| 189 |
|
|
#define FAT_HASH_BITS 8 |
| 190 |
|
|
@@ -428,8 +429,9 @@ extern int fat_flush_inodes(struct super |
| 191 |
|
|
extern void fat_fs_panic(struct super_block *s, const char *fmt, ...); |
| 192 |
|
|
extern void fat_clusters_flush(struct super_block *sb); |
| 193 |
|
|
extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster); |
| 194 |
|
|
-extern int date_dos2unix(unsigned short time, unsigned short date); |
| 195 |
|
|
-extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date); |
| 196 |
|
|
+extern int date_dos2unix(unsigned short time, unsigned short date, int tz_utc); |
| 197 |
|
|
+extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, |
| 198 |
|
|
+ int tz_utc); |
| 199 |
|
|
extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); |
| 200 |
|
|
|
| 201 |
|
|
int fat_cache_init(void); |
| 202 |
|
|
|
| 203 |
|
|
diff -puN Documentation/filesystems/vfat.txt~utc-timestamp-option-for-fat-filesystems-fix Documentation/filesystems/vfat.txt |
| 204 |
|
|
--- a/Documentation/filesystems/vfat.txt~utc-timestamp-option-for-fat-filesystems-fix |
| 205 |
|
|
+++ a/Documentation/filesystems/vfat.txt |
| 206 |
|
|
@@ -96,6 +96,14 @@ shortname=lower|win95|winnt|mixed |
| 207 |
|
|
emulate the Windows 95 rule for create. |
| 208 |
|
|
Default setting is `lower'. |
| 209 |
|
|
|
| 210 |
|
|
+tz=UTC -- Interpret timestamps as UTC rather than local time. |
| 211 |
|
|
+ This option disables the conversion of timestamps |
| 212 |
|
|
+ between local time (as used by Windows on FAT) and UTC |
| 213 |
|
|
+ (which Linux uses internally). This is particuluarly |
| 214 |
|
|
+ useful when mounting devices (like digital cameras) |
| 215 |
|
|
+ that are set to UTC in order to avoid the pitfalls of |
| 216 |
|
|
+ local time. |
| 217 |
|
|
+ |
| 218 |
|
|
<bool>: 0,1,yes,no,true,false |
| 219 |
|
|
|
| 220 |
|
|
TODO |