Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 1 Oct 2020 17:46:24 -0400
From:      Kurt Hackenberg <kh@panix.com>
To:        freebsd-questions@freebsd.org
Subject:   Re: Preserving target file's creation date
Message-ID:  <24438.20016.682769.47891@rain.home>
In-Reply-To: <20201001141721.5be6318e@gumby.homeunix.com>
References:  <202010010424.0914OZ9Y029194@sdf.org> <20201001072728.000004b6@seibercom.net> <20201001134113.13ff6d86.freebsd@edvax.de> <20201001141721.5be6318e@gumby.homeunix.com>

next in thread | previous in thread | raw e-mail | index | archive | help

--6IfbR40u5C
Content-Type: text/plain; charset=us-ascii
Content-Description: message body text
Content-Transfer-Encoding: 7bit

RW via freebsd-questions writes:

>There are four timestamps: atime, mtime, ctime and btime.
>
>The first two are data access and modification. ctime is when the
>metadata in the inode was last modified, the 'c' is for changed, not
>created.
>
>btime is time the file/inode was created (b for birth), but it's not
>portable.

Yes.  Historically, there were three file times: atime, mtime, ctime
(historically called creation time, though it's actually been inode
modification time for as long as I know of).

The fourth, birth time, was added much later, and I think only in
FreeBSD.  To maximize confusion, people sometimes call this creation
time, too.  Don't do that.

Somebody who has Linux handy could check what file times it has.  You
might look at what comes back from the system call stat().

>I just had a look at some files I recently copied with dump|restore and
>it had preserved all 4 times. I also found:
>
>cp -p preserved atime, mtime and btime.
>
>rsync -a preserved mtime and btime
>
>I find the last two results strange as cp(1) and rsync(1) don't claim
>to preserve btime. I'd be surprised if rsync even knows about it. 

I just found that cp without -p preserves birth time, as you would
hope, though ctime changes.

Perhaps some of these old utilities that run on many Unixes don't know
about the FreeBSD birth time.

I'll attach to this message a C program I wrote that displays all four
file times.  We'll see whether the mailing list allows the attachment.
(In the past this list has removed attachments of type text/x-csrc;
I'll make this one text/plain).

--6IfbR40u5C
Content-Type: text/plain;
	 name="ftime4.c"
Content-Description: ftime4.c
Content-Disposition: inline;
	 filename="ftime4.c"
Content-Transfer-Encoding: 7bit

/*
   FreeBSD program: for each file named on command line, display all
   four times.  The Unix file time traditionally called creation time
   has been, for as long as I remember, updated whenever the inode
   contents are modified.  FreeBSD adds a fourth time, "birth time"
   (aka creation time), which is when the file (inode) was created.

   This program can display the times as local time or UTC, with or
   without nanoseconds.  The two output formats (with or without
   nanoseconds) are substantially different.
*/


#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/stat.h>


typedef int BOOL;
#define TRUE 1
#define FALSE 0


static void qerror(const char *me, const char *thing)
{
    fprintf(stderr, "%s: %s: %s\n", me, thing, strerror(errno));
}

static void stringify(const struct timespec ts, const BOOL worldtime,
		      const BOOL nanoseconds, char *str)
{
    struct tm tm;

    (void)(worldtime ? gmtime_r : localtime_r)(&ts.tv_sec, &tm);
    if (nanoseconds)
	(void)sprintf(str, "%04d/%02d/%02d %02d:%02d:%02d.%09ld",
		      tm.tm_year + 1900,
		      tm.tm_mon + 1,
		      tm.tm_mday,
		      tm.tm_hour,
		      tm.tm_min,
		      tm.tm_sec,
		      ts.tv_nsec);
    else
    {
	(void)asctime_r(&tm, str);
	str[strlen(str) - 1] = '\0';
    }
    sprintf(&str[strlen(str)], " %s (%c%02ld:%02ld:%02ld)", tm.tm_zone,
	    (tm.tm_gmtoff >= 0L) ? '+' : '-',
	    labs(tm.tm_gmtoff) / 3600L,
	    labs(tm.tm_gmtoff) / 60L % 60L,
	    labs(tm.tm_gmtoff) % 60L);
}

int main(int argc, const char *argv[], const char *envp[])
{
    const char *me;
    BOOL worldtime = FALSE; /* display times as UTC */
    BOOL nanoseconds = FALSE; /* display times with nanoseconds */
    struct stat stuff;
    char bstr[100];
    char cstr[100];
    char mstr[100];
    char astr[100];
    int ai;			/* argv index */
    int aj;			/* argv[ai] (string) index */
    int err;

    if ((me = strrchr(argv[0], '/')) != NULL)
	me++;
    else
	me = argv[0];

    err = 0;

    for (ai = 1; ai < argc && (argv[ai])[0] == '-'; ai++)
	for (aj = 1; (argv[ai])[aj] != '\0'; aj++)
	    switch ((argv[ai])[aj])
	    {
	    case 'u':		/* UTC */
		worldtime = TRUE;
		break;
	    case 'l':		/* local time */
		worldtime = FALSE;
		break;
	    case 'n':		/* nanoseconds */
		nanoseconds = TRUE;
		break;
	    case 's':		/* seconds */
		nanoseconds = FALSE;
		break;
	    default:
		fprintf(stderr, "%s: unknown option '%c'\n", me, (argv[ai])[aj]);
		err = -1;
		break;
	    }

    if (ai < argc)
	do
	    if (stat(argv[ai], &stuff) == 0)
	    {
		stringify(stuff.st_birthtim, worldtime, nanoseconds, bstr);
		stringify(stuff.st_ctim, worldtime, nanoseconds, cstr);
		stringify(stuff.st_mtim, worldtime, nanoseconds, mstr);
		stringify(stuff.st_atim, worldtime, nanoseconds, astr);
		printf("%s   %s   %s   %s   %s\n",
		       bstr, cstr, mstr, astr, argv[ai]);
	    }
	    else
	    {
		qerror(me, argv[ai]);
		err = errno;
	    }
	while (++ai < argc);
    else
    {
	fprintf(stderr, "%s usage: %s [-ulns] file ...\n", me, me);
	err = -1;
    }

    return err;
}

--6IfbR40u5C--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?24438.20016.682769.47891>