Subject: kern/22717: Explicit time zone handling for MS-DOS file systems
To: None <gnats-bugs@gnats.netbsd.org>
From: None <michael.eriksson@era-t.ericsson.se>
List: netbsd-bugs
Date: 09/07/2003 23:56:29
>Number: 22717
>Category: kern
>Synopsis: Explicit time zone handling for MS-DOS file systems
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun Sep 07 21:57:00 UTC 2003
>Closed-Date:
>Last-Modified:
>Originator: Michael Eriksson
>Release: NetBSD 1.6X
>Organization:
Ericsson Research
>Environment:
>Description:
MS-DOS file systems, i.e., FAT, store time stamps in the local time
zone (in contrast to the Unix file systems which use UTC). For this
reason, the kernel adjusts the MS-DOS file stamps with rtc_offset,
with the undocumented presumption that the only use of MS-DOS file
systems is on machines which dual-boot operating systems which keeps
the real-time clock in local time.
Now, most digital cameras write the image files to flash-based FAT
file systems. When these file systems are mounted (with, e.g., a USB
flash reader) on machines that don't dual-boot W*ndows, the rtc_offset
hack goes from ugly to plain wrong.
>How-To-Repeat:
datan$ mount /dev/sd0e /flash
datan$ ls -l /flash
Note bogus time stamps (unless you happen to be in the UK winter time
zone).
>Fix:
The patch below adds a '-t' option to mount_msdos, to set the time
zone offset (in seconds) from UTC. If not set, the user's current time
zone is used. (Maybe a larger unit of time (minutes) should be used,
but seconds is most generic, and can also be used by a perfectionist
to adjust for, e.g., a camera that doesn't keep perfect time. :-))
Index: sys/fs/msdosfs/denode.h
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/denode.h,v
retrieving revision 1.3
diff -u -r1.3 denode.h
--- sys/fs/msdosfs/denode.h 2003/06/29 22:31:09 1.3
+++ sys/fs/msdosfs/denode.h 2003/09/07 21:21:46
@@ -221,18 +221,18 @@
#define VTODE(vp) ((struct denode *)(vp)->v_data)
#define DETOV(de) ((de)->de_vnode)
-#define DETIMES(dep, acc, mod, cre) \
+#define DETIMES(dep, acc, mod, cre, gmtoff) \
if ((dep)->de_flag & (DE_UPDATE | DE_CREATE | DE_ACCESS)) { \
(dep)->de_flag |= DE_MODIFIED; \
if ((dep)->de_flag & DE_UPDATE) { \
- unix2dostime((mod), &(dep)->de_MDate, &(dep)->de_MTime, NULL); \
+ unix2dostime((mod), gmtoff, &(dep)->de_MDate, &(dep)->de_MTime, NULL); \
(dep)->de_Attributes |= ATTR_ARCHIVE; \
} \
if (!((dep)->de_pmp->pm_flags & MSDOSFSMNT_NOWIN95)) { \
if ((dep)->de_flag & DE_ACCESS) \
- unix2dostime((acc), &(dep)->de_ADate, NULL, NULL); \
+ unix2dostime((acc), gmtoff, &(dep)->de_ADate, NULL, NULL); \
if ((dep)->de_flag & DE_CREATE) \
- unix2dostime((cre), &(dep)->de_CDate, &(dep)->de_CTime, &(dep)->de_CHun); \
+ unix2dostime((cre), gmtoff, &(dep)->de_CDate, &(dep)->de_CTime, &(dep)->de_CHun); \
} \
(dep)->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS); \
}
Index: sys/fs/msdosfs/direntry.h
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/direntry.h,v
retrieving revision 1.1
diff -u -r1.1 direntry.h
--- sys/fs/msdosfs/direntry.h 2002/12/26 12:31:33 1.1
+++ sys/fs/msdosfs/direntry.h 2003/09/07 21:21:46
@@ -119,9 +119,10 @@
#define DD_YEAR_SHIFT 9
#ifdef _KERNEL
-void unix2dostime __P((struct timespec *tsp, u_int16_t *ddp,
+void unix2dostime __P((struct timespec *tsp, int gmtoff, u_int16_t *ddp,
u_int16_t *dtp, u_int8_t *dhp));
-void dos2unixtime __P((u_int dd, u_int dt, u_int dh, struct timespec *tsp));
+void dos2unixtime __P((u_int dd, u_int dt, u_int dh, int gmtoff,
+ struct timespec *tsp));
int dos2unixfn __P((u_char dn[11], u_char *un, int lower));
int unix2dosfn __P((const u_char *un, u_char dn[12], int unlen,
u_int gen));
Index: sys/fs/msdosfs/msdosfs_conv.c
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfs_conv.c,v
retrieving revision 1.1
diff -u -r1.1 msdosfs_conv.c
--- sys/fs/msdosfs/msdosfs_conv.c 2002/12/26 12:31:34 1.1
+++ sys/fs/msdosfs/msdosfs_conv.c 2003/09/07 21:21:46
@@ -96,8 +96,9 @@
* file timestamps. The passed in unix time is assumed to be in GMT.
*/
void
-unix2dostime(tsp, ddp, dtp, dhp)
+unix2dostime(tsp, gmtoff, ddp, dtp, dhp)
struct timespec *tsp;
+ int gmtoff;
u_int16_t *ddp;
u_int16_t *dtp;
u_int8_t *dhp;
@@ -113,8 +114,7 @@
* If the time from the last conversion is the same as now, then
* skip the computations and use the saved result.
*/
- t = tsp->tv_sec - (rtc_offset * 60)
- /* +- daylight savings time correction */ ;
+ t = tsp->tv_sec + gmtoff; /* time zone correction */
t &= ~1;
if (lasttime != t) {
lasttime = t;
@@ -177,10 +177,11 @@
* not be too efficient.
*/
void
-dos2unixtime(dd, dt, dh, tsp)
+dos2unixtime(dd, dt, dh, gmtoff, tsp)
u_int dd;
u_int dt;
u_int dh;
+ int gmtoff;
struct timespec *tsp;
{
u_long seconds;
@@ -227,8 +228,8 @@
days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
}
- tsp->tv_sec = seconds + lastseconds + (rtc_offset * 60)
- /* -+ daylight savings time correction */ ;
+ tsp->tv_sec = seconds + lastseconds;
+ tsp->tv_sec -= gmtoff; /* time zone correction */
tsp->tv_nsec = (dh % 100) * 10000000;
}
Index: sys/fs/msdosfs/msdosfs_vfsops.c
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfs_vfsops.c,v
retrieving revision 1.8
diff -u -r1.8 msdosfs_vfsops.c
--- sys/fs/msdosfs/msdosfs_vfsops.c 2003/08/02 11:41:21 1.8
+++ sys/fs/msdosfs/msdosfs_vfsops.c 2003/09/07 21:21:46
@@ -147,6 +147,7 @@
pmp->pm_uid = argp->uid;
pmp->pm_mask = argp->mask & ALLPERMS;
pmp->pm_dirmask = argp->dirmask & ALLPERMS;
+ pmp->pm_gmtoff = argp->gmtoff;
pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT;
/*
Index: sys/fs/msdosfs/msdosfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfs_vnops.c,v
retrieving revision 1.6
diff -u -r1.6 msdosfs_vnops.c
--- sys/fs/msdosfs/msdosfs_vnops.c 2003/08/02 11:41:21 1.6
+++ sys/fs/msdosfs/msdosfs_vnops.c 2003/09/07 21:21:47
@@ -156,7 +156,7 @@
ndirent.de_pmp = pdep->de_pmp;
ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
TIMEVAL_TO_TIMESPEC(&time, &ts);
- DETIMES(&ndirent, &ts, &ts, &ts);
+ DETIMES(&ndirent, &ts, &ts, &ts, pdep->de_pmp->pm_gmtoff);
if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
goto bad;
if ((cnp->cn_flags & SAVESTART) == 0)
@@ -221,7 +221,7 @@
simple_lock(&vp->v_interlock);
if (vp->v_usecount > 1) {
TIMEVAL_TO_TIMESPEC(&time, &ts);
- DETIMES(dep, &ts, &ts, &ts);
+ DETIMES(dep, &ts, &ts, &ts, dep->de_pmp->pm_gmtoff);
}
simple_unlock(&vp->v_interlock);
return (0);
@@ -287,7 +287,7 @@
u_long fileid;
TIMEVAL_TO_TIMESPEC(&time, &ts);
- DETIMES(dep, &ts, &ts, &ts);
+ DETIMES(dep, &ts, &ts, &ts, pmp->pm_gmtoff);
vap->va_fsid = dep->de_dev;
/*
* The following computation of the fileid must be the same as that
@@ -316,10 +316,13 @@
vap->va_nlink = 1;
vap->va_rdev = 0;
vap->va_size = ap->a_vp->v_size;
- dos2unixtime(dep->de_MDate, dep->de_MTime, 0, &vap->va_mtime);
+ dos2unixtime(dep->de_MDate, dep->de_MTime, 0, pmp->pm_gmtoff,
+ &vap->va_mtime);
if (dep->de_pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
- dos2unixtime(dep->de_ADate, 0, 0, &vap->va_atime);
- dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun, &vap->va_ctime);
+ dos2unixtime(dep->de_ADate, 0, 0, pmp->pm_gmtoff,
+ &vap->va_atime);
+ dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun,
+ pmp->pm_gmtoff, &vap->va_ctime);
} else {
vap->va_atime = vap->va_mtime;
vap->va_ctime = vap->va_mtime;
@@ -398,9 +401,9 @@
return (error);
if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
vap->va_atime.tv_sec != VNOVAL)
- unix2dostime(&vap->va_atime, &dep->de_ADate, NULL, NULL);
+ unix2dostime(&vap->va_atime, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL);
if (vap->va_mtime.tv_sec != VNOVAL)
- unix2dostime(&vap->va_mtime, &dep->de_MDate, &dep->de_MTime, NULL);
+ unix2dostime(&vap->va_mtime, pmp->pm_gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
dep->de_Attributes |= ATTR_ARCHIVE;
dep->de_flag |= DE_MODIFIED;
de_changed = 1;
@@ -707,7 +710,7 @@
TIMEVAL_TO_TIMESPEC(&time, &ts);
DETIMES(dep,
ap->a_access ? ap->a_access : &ts,
- ap->a_modify ? ap->a_modify : &ts, &ts);
+ ap->a_modify ? ap->a_modify : &ts, &ts, dep->de_pmp->pm_gmtoff);
if ((dep->de_flag & DE_MODIFIED) == 0)
return (0);
dep->de_flag &= ~DE_MODIFIED;
@@ -1232,7 +1235,7 @@
ndirent.de_pmp = pmp;
ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
TIMEVAL_TO_TIMESPEC(&time, &ts);
- DETIMES(&ndirent, &ts, &ts, &ts);
+ DETIMES(&ndirent, &ts, &ts, &ts, pmp->pm_gmtoff);
/*
* Now fill the cluster with the "." and ".." entries. And write
Index: sys/fs/msdosfs/msdosfsmount.h
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfsmount.h,v
retrieving revision 1.3
diff -u -r1.3 msdosfsmount.h
--- sys/fs/msdosfs/msdosfsmount.h 2003/08/02 11:41:21 1.3
+++ sys/fs/msdosfs/msdosfsmount.h 2003/09/07 21:21:47
@@ -56,6 +56,7 @@
uid_t uid; /* uid that owns msdosfs files */
gid_t gid; /* gid that owns msdosfs files */
mode_t mask; /* mask to be applied for msdosfs perms */
+ int gmtoff; /* offset from UTC in seconds */
int flags; /* see below */
/* Following items added after versioning support */
@@ -102,6 +103,7 @@
for files */
mode_t pm_dirmask; /* mask to and with file protection bits
for directories */
+ int pm_gmtoff; /* offset from UTC in seconds */
struct vnode *pm_devvp; /* vnode for block device mntd */
struct bpb50 pm_bpb; /* BIOS parameter blk for this fs */
u_long pm_FATsecs; /* actual number of fat sectors */
Index: sbin/mount_msdos/mount_msdos.8
===================================================================
RCS file: /cvsroot/src/sbin/mount_msdos/mount_msdos.8,v
retrieving revision 1.27
diff -u -r1.27 mount_msdos.8
--- sbin/mount_msdos/mount_msdos.8 2003/08/02 11:43:21 1.27
+++ sbin/mount_msdos/mount_msdos.8 2003/09/07 21:21:47
@@ -45,6 +45,7 @@
.Op Fl g Ar gid
.Op Fl m Ar mask
.Op Fl M Ar mask
+.Op Fl t Ar gmtoff
.Op Fl s
.Op Fl l
.Op Fl 9
@@ -113,6 +114,11 @@
is used if it is supplied and
.Fl M
is omitted. See description of previous option for details.
+.It Fl t Ar gmtoff
+Set the time zone offset (in seconds) from UTC to
+.Ar gmtoff ,
+with positive values indicating east of the Prime Meridian.
+If not set, the user's current time zone will be used.
.It Fl s
Force behaviour to
ignore and not generate Win'95 long filenames.
Index: sbin/mount_msdos/mount_msdos.c
===================================================================
RCS file: /cvsroot/src/sbin/mount_msdos/mount_msdos.c,v
retrieving revision 1.30
diff -u -r1.30 mount_msdos.c
--- sbin/mount_msdos/mount_msdos.c 2003/08/02 11:42:20 1.30
+++ sbin/mount_msdos/mount_msdos.c 2003/09/07 21:21:47
@@ -51,6 +51,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include <util.h>
@@ -87,13 +88,15 @@
{
struct msdosfs_args args;
struct stat sb;
- int c, mntflags, set_gid, set_uid, set_mask, set_dirmask;
+ int c, mntflags, set_gid, set_uid, set_mask, set_dirmask, set_gmtoff;
char *dev, *dir, ndir[MAXPATHLEN+1];
+ time_t now;
+ struct tm *tm;
- mntflags = set_gid = set_uid = set_mask = set_dirmask = 0;
+ mntflags = set_gid = set_uid = set_mask = set_dirmask = set_gmtoff = 0;
(void)memset(&args, '\0', sizeof(args));
- while ((c = getopt(argc, argv, "Gsl9u:g:m:M:o:")) != -1) {
+ while ((c = getopt(argc, argv, "Gsl9u:g:m:M:o:t:")) != -1) {
switch (c) {
case 'G':
args.flags |= MSDOSFSMNT_GEMDOSFS;
@@ -126,6 +129,10 @@
case 'o':
getmntopts(optarg, mopts, &mntflags, 0);
break;
+ case 't':
+ args.gmtoff = atoi(optarg);
+ set_gmtoff = 1;
+ break;
case '?':
default:
usage();
@@ -174,6 +181,14 @@
args.mask = args.dirmask =
sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
}
+ }
+
+ if (!set_gmtoff) {
+ /* use user's time zone as default */
+ time(&now);
+ tm = localtime(&now);
+ args.gmtoff = tm->tm_gmtoff;
+
}
args.flags |= MSDOSFSMNT_VERSIONED;
args.version = MSDOSFSMNT_VERSION;
>Release-Note:
>Audit-Trail:
>Unformatted: