Thursday, December 27, 2012

stat() nanosecond time resolution


$ stat foo
[..]
Access: 2012-12-27 15:07:08.020388663 +0300
Modify: 2012-12-27 15:07:08.020388663 +0300
Change: 2012-12-27 15:07:08.020388663 +0300


where does nanosecond precision come from?

man 2 stat

           struct stat {
               dev_t     st_dev;     /* ID of device containing file */
               ino_t     st_ino;     /* inode number */
               mode_t    st_mode;    /* protection */
               nlink_t   st_nlink;   /* number of hard links */
               uid_t     st_uid;     /* user ID of owner */
               gid_t     st_gid;     /* group ID of owner */
               dev_t     st_rdev;    /* device ID (if special file) */
               off_t     st_size;    /* total size, in bytes */
               blksize_t st_blksize; /* blocksize for file system I/O */
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
               time_t    st_atime;   /* time of last access */
               time_t    st_mtime;   /* time of last modification */
               time_t    st_ctime;   /* time of last status change */
           };


as we can see, stat struct contains ctime/mtime/atime, which are time_t, so it's quiet
unlikely to store nanosecond resolution.

The note part says:

Since kernel 2.5.48, the stat structure supports nanosecond resolution
for the three file timestamp fields.
Glibc exposes the nanosecond component
of each field using names of the form st_atim.tv_nsec if the  _BSD_SOURCE

or _SVID_SOURCE  feature  test  macro  is defined.   These  fields are specified in
POSIX.1-2008, and, starting
with version 2.12, glibc also exposes these field
names if _POSIX_C_SOURCE is defined with the value 200809L or
greater, or
_XOPEN_SOURCE is defined with the value 700 or greater.  If none of the
aforementioned macros are
defined, then the nanosecond values are exposed
with names of the form st_atimensec.  On file systems that do
not support
subsecond timestamps, the nanosecond fields are returned with the value 0.


GLIBC defines struct stat as

struct stat
[..]
  #if defined __USE_MISC || defined __USE_XOPEN2K8
      /* Nanosecond resolution timestamps are stored in a format
         equivalent to 'struct timespec'.  This is the type used
         whenever possible but the Unix namespace rules do not allow the
         identifier 'timespec' to appear in the header.
         Therefore we have to handle the use of this header in strictly
         standard-compliant sources special.  */

      struct timespec st_atim;          /* Time of last access.  */
      struct timespec st_mtim;          /* Time of last modification.  */
      struct timespec st_ctim;          /* Time of last status change.  */
  # define st_atime st_atim.tv_sec       /* Backward compatibility.  */
  # define st_mtime st_mtim.tv_sec
  # define st_ctime st_ctim.tv_sec
  #else
      __time_t st_atime;              
/* Time of last access.  */
      unsigned long int st_atimensec;    /* Nscecs of last access.  */
      __time_t st_mtime;               /* Time of last modification.  */
      unsigned long int st_mtimensec;    /* Nsecs of last modification.  */
      __time_t st_ctime;               /* Time of last status change.  */
      unsigned long int st_ctimensec;    /* Nsecs of last status change.  */
  #endif
[..]


where MISC or XOPEN2K8 defs are coming from

glibc/include/features.h

 #if (_POSIX_C_SOURCE - 0) >= 200809L
 # define __USE_XOPEN2K8       1                                                                       
 # undef  _ATFILE_SOURCE
 # define _ATFILE_SOURCE 1
 #endif


or

 #  if (_XOPEN_SOURCE - 0) >= 600
 #   if (_XOPEN_SOURCE - 0) >= 700
 #    define __USE_XOPEN2K8     1
 #    define __USE_XOPEN2K8XSI  1
 #   endif


 #if defined _BSD_SOURCE || defined _SVID_SOURCE
 # define __USE_MISC    1                                                                        
 #endif



So it seems, that 'if defined __USE_MISC || defined __USE_XOPEN2K8' or
'if defined _BSD_SOURCE' is sufficient. However, the real world code(tm) is a bit more
complicated. Here is a small part of core-utils' stat:


[..]
 /* STAT_TIMESPEC (ST, ST_XTIM) is the ST_XTIM member for *ST of type
    struct timespec, if available.  If not, then STAT_TIMESPEC_NS (ST,
    ST_XTIM) is the nanosecond component of the ST_XTIM member for *ST,
    if available.  ST_XTIM can be st_atim, st_ctim, st_mtim, or st_birthtim
    for access, status change, data modification, or birth (creation)
    time respectively.

    These macros are private to stat-time.h.  */

 #if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
 # ifdef TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC

 #  define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim)
 # else
 #  define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.tv_nsec)
 # endif
 #elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC

 # define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim##espec)
 #elif defined HAVE_STRUCT_STAT_ST_ATIMENSEC                                                                     
 # define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim##ensec)
 #elif defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC
 # define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.st__tim.tv_nsec)
 #endif


 /* Return the nanosecond component of *ST's status change time.  */
 static inline long int
 get_stat_ctime_ns
(struct stat const *st)
 {
 # if defined STAT_TIMESPEC
   return STAT_TIMESPEC (st, st_ctim).tv_nsec;
 # elif defined STAT_TIMESPEC_NS
   return STAT_TIMESPEC_NS (st, st_ctim);
 # else
   return 0;
 # endif
 }

[..]