msdos filesystem and dates

msdos filesystem and dates

Post by Joe Peters » Tue, 21 Nov 1995 04:00:00



One interesting inconsistency that exists between the Linux/DOS worlds
is that of time stamps and the clock.  Whereas Linux (unix) always internally
keeps time in UTC (GMT) time, DOS keeps it in an arbitrary time (usually
set by users to local time).  Linux has conversions for the user's local
time, so the time stamps shown on files and the time output by the date
command can appear in local time.  Timezone and daylight savings time
information is also taken into account.

The problem occurs when the time changes from daylight to standard.  The
DOS (CMOS) clock does not switch over automatically, since it has no idea
about timezones, etc.  Since the Linux clock does switch automatically,
there is an ambiguity from 1AM to 2AM if you try to set the Linux clock
from the DOS clock (which happens at boot time via the clock program that
is run from the rc scripts).  Also, DOS time stamps that were made on files
modified during daylight time will be off by one hour when viewed from
Linux in an msdos filesystem.  This is because the Linux code in the kernel
does not know if daylight savings time was in effect when the stamp was
made.

Anyway, I have a solution to the whole dilema.  I set my DOS clock to UTC.
Now, of course, when I'm in DOS, the files will have stamps in UTC (5 hours
ahead in my eastern time zone).  Since I don't use DOS that much, this is
no big deal - I just have to remember that it is thinking UTC...  Another
benefit is that when the time changes twice a year, I don't have to change
the DOS (CMOS) clock - it's always correct.  Linux, however, does the right
thing and converts to the proper local time.  Cool!  If you do this, make
sure you also change the clock command in the rc scripts (in /etc/rc.d) to
use the "u" option to clock, so Linux knows DOS's clock is in UTC.

Now to the heart of the problem...  One remaining weirdness is that the kernel
code sort of assumes that the DOS file stamps are in local time, so it does
a correction back to what it thinks UTC is.  Note that this is what causes the
off-by-one-hour problem if the time stamp was in daylight local time, and you
are now looking from standard time (or vice versa).  There is an easy fix,
however.  If you remove this correction from the kernel source in the
routines date_dos2unix and date_unix2dos, your system will be completely
consistent, with the DOS time in UTC and the Linux time cool.

Here's the patch to /usr/src/linux/fs/msdos/misc.c:

*** misc.c~     Mon Oct 23 02:51:47 1995
--- misc.c      Wed Nov 15 19:59:36 1995
***************
*** 248,254 ****
            ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
            month < 2 ? 1 : 0)+3653);
                        /* days since 1.1.70 plus 80's leap day */
-       secs += sys_tz.tz_minuteswest*60;
        return secs;
  }

--- 248,253 ----
***************
*** 260,266 ****
  {
        int day,year,nl_day,month;

-       unix_date -= sys_tz.tz_minuteswest*60;
        *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
            (((unix_date/3600) % 24) << 11);
        day = unix_date/86400-3652;
--- 259,264 ----

I think that this should be configurable from the kernel config script of
course, rather than requiring a source hack every time one gets a new kernel.
What do others think?  I am not sure of the correct channel by which to
get this incorporated into Linus's code...  If anyone out there with
authority knows, and agrees with me, let me know!


 
 
 

msdos filesystem and dates

Post by Joe Peters » Wed, 22 Nov 1995 04:00:00





: In newsgroup: comp.os.linux.development.system
: >
: > I think that this should be configurable from the kernel config script of
: > course, rather than requiring a source hack every time one gets a new kernel.
: > What do others think?  I am not sure of the correct channel by which to
: > get this incorporated into Linus's code...  If anyone out there with
: > authority knows, and agrees with me, let me know!
: >

: Send a patch to Linus!  Anyway, this should be a mount option to the
: filesystem, not a kernel config option.

Agreed!  Yes, of course.  I'll look into hacking it in and making a patch for
Linus.  Is that how people usually submit suggestions for changes?  Forgive
my ignorance, but I haven't done much official kernel hacking as yet.

                                                Joe

 
 
 

msdos filesystem and dates

Post by Joe Smi » Tue, 28 Nov 1995 04:00:00



Quote:> One interesting inconsistency that exists between the Linux/DOS
> worlds is that of time stamps and the clock.  Whereas Linux (unix)
> always internally keeps time in UTC (GMT) time, DOS keeps it in an
> arbitrary time (usually set by users to local time).
> ...

Should anyone care excessively, there is a very nice replacement for
the DOS clock device that brings Unix-style sanity (;-) to the DOS
clock as well as fix several annoying bugs.  It's called 'clk360.zip'
(or some such) and lives at most DOS archive sites.  With your patch,
it ought to make file timestamps completely compatible between DOS and
Linux.

<Joe

--
 Joe Smith

 Department of Physiology
 Philadelphia, PA 19104

 
 
 

msdos filesystem and dates

Post by Joe Peters » Wed, 29 Nov 1995 04:00:00


For anyone who is interested, here is the patch.  It was made on Linux 1.3.41,
so if you have the version it should apply without a hitch...  To make use
of it, simply mount your msdos partition with the new "time=utc" option,
and the dos time stamps will be interpreted as Universal Time.

                                                Joe (j...@jump.com)

*** fs/msdos/inode.c.orig       Tue Nov 21 20:56:42 1995
--- fs/msdos/inode.c    Tue Nov 21 23:40:14 1995
***************
*** 2,7 ****
--- 2,17 ----
   *  linux/fs/msdos/inode.c
   *
   *  Written 1992,1993 by Werner Almesberger
+  *
+  *  Revisions:
+  *
+  *      Nov 21 1995    Joe Peterson <j...@jump.com>
+  *
+  *          Added "time" mount option that takes either
+  *          "utc" or "local" (default) as a value.
+  *          If set to "utc," it is assumed that the CMOS
+  *          clock (and therefore DOS timestamps) are in
+  *          Universal Coordinated Time (GMT).
   */

  #include <linux/module.h>
***************
*** 75,87 ****

  static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
      gid_t *gid,int *umask,int *debug,int *fat,int *quiet,
!       int *blksize, char *dotsOK)
  {
        char *this_char,*value;

        *check = 'n';
        *conversion = 'b';
        *dotsOK =0;
        *uid = current->uid;
        *gid = current->gid;
        *umask = current->fs->umask;
--- 85,98 ----

  static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
      gid_t *gid,int *umask,int *debug,int *fat,int *quiet,
!       int *blksize, char *dotsOK, int *utc)
  {
        char *this_char,*value;

        *check = 'n';
        *conversion = 'b';
        *dotsOK =0;
+       *utc = 0;
        *uid = current->uid;
        *gid = current->gid;
        *umask = current->fs->umask;
***************
*** 111,116 ****
--- 122,132 ----
                        else if (!strcmp(value,"no")) *dotsOK = 0;
                        else return 0;
                }
+               else if (!strcmp(this_char,"time") && value) {
+                       if (!strcmp(value,"utc")) *utc = 1;
+                       else if (!strcmp(value,"local")) *utc = 0;
+                       else return 0;
+               }
                else if (!strcmp(this_char,"uid")) {
                        if (!value || !*value)
                                return 0;
***************
*** 171,176 ****
--- 187,193 ----
        int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
        int debug,error,fat,quiet;
        char check,conversion,dotsOK;
+       int utc;
        uid_t uid;
        gid_t gid;
        int umask;
***************
*** 184,190 ****
                }
        }
        if (!parse_options((char *) data,&check,&conversion,&uid,&gid,&umask,
!           &debug,&fat,&quiet,&blksize,&dotsOK)
                || (blksize != 512 && blksize != 1024)) {
                sb->s_dev = 0;
                MOD_DEC_USE_COUNT;
--- 201,207 ----
                }
        }
        if (!parse_options((char *) data,&check,&conversion,&uid,&gid,&umask,
!           &debug,&fat,&quiet,&blksize,&dotsOK,&utc)
                || (blksize != 512 && blksize != 1024)) {
                sb->s_dev = 0;
                MOD_DEC_USE_COUNT;
***************
*** 297,302 ****
--- 314,320 ----
        MSDOS_SB(sb)->fs_umask = umask;
        MSDOS_SB(sb)->quiet = quiet;
        MSDOS_SB(sb)->dotsOK = dotsOK;
+       MSDOS_SB(sb)->utc = utc;
        MSDOS_SB(sb)->free_clusters = -1; /* don't know yet */
        MSDOS_SB(sb)->fat_wait = NULL;
        MSDOS_SB(sb)->fat_lock = 0;
***************
*** 435,441 ****
        inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
            inode->i_blksize*MSDOS_SB(inode->i_sb)->cluster_size;
        inode->i_mtime = inode->i_atime = inode->i_ctime =
!           date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date));
        brelse(bh);
  }

--- 453,460 ----
        inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
            inode->i_blksize*MSDOS_SB(inode->i_sb)->cluster_size;
        inode->i_mtime = inode->i_atime = inode->i_ctime =
!           date_dos2unix(inode->i_sb,
!                         CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date));
        brelse(bh);
  }

***************
*** 467,473 ****
        raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
            MSDOS_I(inode)->i_attrs;
        raw_entry->start = CT_LE_L(MSDOS_I(inode)->i_start);
!       date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date);
        raw_entry->time = CT_LE_W(raw_entry->time);
        raw_entry->date = CT_LE_W(raw_entry->date);
        mark_buffer_dirty(bh, 1);
--- 486,493 ----
        raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
            MSDOS_I(inode)->i_attrs;
        raw_entry->start = CT_LE_L(MSDOS_I(inode)->i_start);
!       date_unix2dos(inode->i_sb,
!                     inode->i_mtime,&raw_entry->time,&raw_entry->date);
        raw_entry->time = CT_LE_W(raw_entry->time);
        raw_entry->date = CT_LE_W(raw_entry->date);
        mark_buffer_dirty(bh, 1);
*** fs/msdos/namei.c.orig       Tue Nov 21 20:57:54 1995
--- fs/msdos/namei.c    Tue Nov 21 22:02:02 1995
***************
*** 224,230 ****
        de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
        de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
        de->start = 0;
!       date_unix2dos(dir->i_mtime,&de->time,&de->date);
        de->size = 0;
        mark_buffer_dirty(bh, 1);
        if ((*result = iget(dir->i_sb,ino)) != NULL)
--- 224,230 ----
        de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
        de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
        de->start = 0;
!       date_unix2dos(dir->i_sb,dir->i_mtime,&de->time,&de->date);
        de->size = 0;
        mark_buffer_dirty(bh, 1);
        if ((*result = iget(dir->i_sb,ino)) != NULL)
*** fs/msdos/misc.c.orig        Fri Nov 10 01:08:18 1995
--- fs/msdos/misc.c     Tue Nov 21 23:33:38 1995
***************
*** 234,240 ****

  /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */

! int date_dos2unix(unsigned short time,unsigned short date)
  {
        int month,year,secs;

--- 234,241 ----

  /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */

! int date_dos2unix(struct super_block *sb,
!     unsigned short time,unsigned short date)
  {
        int month,year,secs;

***************
*** 244,262 ****
            ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
            month < 2 ? 1 : 0)+3653);
                        /* days since 1.1.70 plus 80's leap day */
!       secs += sys_tz.tz_minuteswest*60;
        return secs;
  }

  /* Convert linear UNIX date to a MS-DOS time/date pair. */

! void date_unix2dos(int unix_date,unsigned short *time,
!     unsigned short *date)
  {
        int day,year,nl_day,month;

!       unix_date -= sys_tz.tz_minuteswest*60;
        *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
            (((unix_date/3600) % 24) << 11);
        day = unix_date/86400-3652;
--- 245,265 ----
            ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
            month < 2 ? 1 : 0)+3653);
                        /* days since 1.1.70 plus 80's leap day */
!       if (!MSDOS_SB(sb)->utc)
!           secs += sys_tz.tz_minuteswest*60;
        return secs;
  }

  /* Convert linear UNIX date to a MS-DOS time/date pair. */

! void date_unix2dos(struct super_block *sb,
!     int unix_date,unsigned short *time,unsigned short *date)
  {
        int day,year,nl_day,month;

!       if (!MSDOS_SB(sb)->utc)
!           unix_date -= sys_tz.tz_minuteswest*60;
        *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
            (((unix_date/3600) % 24) << 11);
        day = unix_date/86400-3652;
*** include/linux/msdos_fs.h.orig       Tue Nov 21 21:24:29 1995
--- include/linux/msdos_fs.h    Tue Nov 21 21:27:41 1995
***************
*** 124,131 ****
  extern void lock_fat(struct super_block *sb);
  extern void unlock_fat(struct super_block *sb);
  extern int msdos_add_cluster(struct inode *inode);
! extern int date_dos2unix(__u16 time, __u16 date);
! extern void date_unix2dos(int unix_date,__u16 *time, __u16 *date);
  extern int msdos_get_entry(struct inode *dir,loff_t *pos,struct buffer_head **bh,
      struct msdos_dir_entry **de);
  extern int msdos_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
--- 124,131 ----
  extern void lock_fat(struct super_block *sb);
  extern void unlock_fat(struct super_block *sb);
  extern int msdos_add_cluster(struct inode *inode);
! extern int date_dos2unix(struct super_block *sb, __u16 time, __u16 date);
! extern void date_unix2dos(struct super_block *sb, int unix_date, __u16 *time, __u16 *date);
  extern int msdos_get_entry(struct inode *dir,loff_t *pos,struct buffer_head **bh,
      struct msdos_dir_entry **de);
  extern int msdos_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
*** include/linux/msdos_fs_sb.h.orig    Tue Nov 21 20:51:26 1995
--- include/linux/msdos_fs_sb.h Tue Nov 21 21:23:17 1995
***************
*** 23,28 ****
--- 23,29 ----
        int prev_free; /* previously returned free cluster number */
        int free_clusters; /* -1 if undefined */
        char dotsOK;
+       int utc; /* true if file timestamps are in UTC, not local time */
  };

  #endif

 
 
 

1. Please read this if you have MSDOS date problems



Doh! I was just about to report the same problem -- I noticed a date
on a file in my DOS partition that I knew was wrong; upon further checking,
some files I wrote under DOS show up under Linux as being written on
Oct 7 1994 (i.e., 2 weeks in the future), despite the time and date
being correct under either OS.

Question: Why are the files on the MSDOS partition showing up with the
wrong dates anyway?  My clock is set to local time, and linux 'date'
displays the right time so I assume it understands this.  The local time
(not the GMT) is written by MSDOS (must be, since it doesn't know
anything about time zones!), so no translation is necessary.

If Linux is interpreting the times written for MSDOS files as GMT, then
even getting a working /sbin/clock doesn't completely fix the problem
(although it reduces the difference to a few hours, instead of several
days).  However, this doesn't seem right since I am west of GMT, so
my time is earlier than GMT, and if Linux is reading the correct
(local) dates but interpreting them as GMT, it should tell me that the
files were written _earlier_ than they were actually written, not later!

--
/dev/joe, living in the WRONG TIME ZONE.
   (old Saturday Night Live skit, you had to have seen it :-)

2. "Fatal server error" starting KDE

3. msdos fs doesn't get file dates right - bug?

4. Linux server with NT4.0WS, Win95

5. Please read this if you have MSDOS date problems

6. Any sucessful SCO NIS sites?

7. Matrox Mystique ands X.

8. PPP and Linux

9. 1.1.9x and msdos filesystem compile problems

10. fsck for msdos filesystem?

11. Mounting msdos filesystems and FAT32

12. MSDOS filesystem

13. Unable to mount msdos filesystem.