[PATCH RFC v3] proto: read origin directory from comment, copy timestamps from origin for inodes.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Right now, when populating a filesystem with the prototype file,
generated inodes will have timestamps set at the creation time.

This change enables more accurate filesystem initialization by preserving
original file timestamps during inode creation rather than defaulting to
the current time.

This patch leverages the xfs_protofile "Descending path" comment in order to
carry the reference to the original files for files other than regular ones.

The origin path is parsed from the comments and only the first instance
is registered.

To do this, a little change to `parseproto()` has been made to keep
track of the current_path each file is in, in order to reconstruct the
original file path.

If the "Descending path" comment is not found, this will behave like the old
implementation, old files not created by `xfs_protofile` tool will simply skip
timestamp carry over.

[v1] -> [v2]
- remove changes to protofile spec
- ensure backward compatibility
[v2] -> [v3]
- use inode_set_[acm]time() as suggested
- avoid copying atime and ctime
  they are often problematic for reproducibility, and
  mtime is the important information to preserve anyway


Signed-off-by: Luca Di Maio <luca.dimaio1@xxxxxxxxx>
---
 mkfs/proto.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 116 insertions(+), 5 deletions(-)

diff --git a/mkfs/proto.c b/mkfs/proto.c
index 6dd3a20..df831c5 100644
--- a/mkfs/proto.c
+++ b/mkfs/proto.c
@@ -5,6 +5,7 @@
  */

 #include "libxfs.h"
 #include <sys/stat.h>
 #include <sys/xattr.h>
 #include <linux/xattr.h>
@@ -22,6 +23,7 @@ static int newregfile(char **pp, char **fname);
 static void rtinit(xfs_mount_t *mp);
 static long filesize(int fd);
 static int slashes_are_spaces;
+static char *originpath = NULL;

 /*
  * Use this for block reservations needed for mkfs's conditions
@@ -142,6 +144,8 @@ getstr(
 	char	*p;
 	char	*rval;

+	static char comment[4096];
+
 	p = *pp;
 	while ((c = *p)) {
 		switch (c) {
@@ -152,8 +156,35 @@ getstr(
 			continue;
 		case ':':
 			p++;
-			while (*p++ != '\n')
-				;
+
+			// in case we didn't find the descending path yet, let's check
+			// every comment in order to find it. If found, originpath will
+			// not be empty and disable this behaviour for future comments.
+			if (originpath == NULL) {
+				int i = 0;
+				while (*p != '\n' && *p != '\0' && i < sizeof(comment) - 1) {
+					comment[i++] = *p++;
+				}
+				if (*p == '\n')
+					p++;
+
+				comment[i] = '\0';
+
+				// Check if comment contains "Descending path"
+				// we will use this information to refer to the original files
+				// in order to copy over information like timestamps.
+				char *path_pos = strstr(comment, "Descending path ");
+				if (path_pos != NULL) {
+					path_pos += strlen("Descending path ");
+					// Found it - set our originpath
+					originpath = strdup(path_pos);
+				}
+			} else {
+				// we don't need to check for descending path anymore, skip
+				// comments as old behaviour.
+				while (*p++ != '\n')
+					;
+			}
 			continue;
 		default:
 			rval = p;
@@ -262,6 +293,60 @@ writesymlink(
 	}
 }

+static void
+write_timestamps(
+	struct xfs_inode	*ip,
+	char *current_path,
+	char *name)
+{
+	int error;
+	struct stat statbuf;
+	struct timespec64 ts;
+	char *origin;
+	size_t origin_len;
+
+	// ignore timestamps in case of old prototype files or undefined
+	// descending directory
+	if (originpath == NULL) {
+		return;
+	}
+
+	origin_len = strlen(originpath) + strlen(current_path) + strlen(name) + 3;
+	origin = malloc(origin_len);
+	if (!origin) {
+		fail(_("error allocating origin name buffer"), errno);
+	}
+	snprintf(origin, origin_len, "%s/%s/%s", originpath, current_path, name);
+
+	// here for symlinks we use lstat so that we don't follow the symlink.
+	// we want the timestamp of the original symlink itself, not what it
+	// points to.
+	error = lstat(origin, &statbuf);
+	if (error < 0) {
+		fprintf(stderr, _("Warning: could not preserve timestamps for %s: %s\n"),
+				origin, strerror(errno));
+		free(origin);
+		return;
+	}
+
+	/* Copy timestamps from source file to destination inode.
+	*  In order to not be influenced by our own access timestamp,
+	*  we set atime and ctime to mtime of the source file.
+	*  Usually reproducible archives will delete or not register
+	*  atime and ctime, for example:
+	*     https://www.gnu.org/software/tar/manual/html_section/Reproducibility.html
+	**/
+	ts.tv_sec = statbuf.st_mtime;
+	ts.tv_nsec = statbuf.st_mtim.tv_nsec;
+	inode_set_atime_to_ts(VFS_I(ip), ts);
+	inode_set_ctime_to_ts(VFS_I(ip), ts);
+	inode_set_mtime_to_ts(VFS_I(ip), ts);
+
+	free(origin);
+	return;
+}
+
+
 static void
 writefile_range(
 	struct xfs_inode	*ip,
@@ -658,7 +743,8 @@ parseproto(
 	xfs_inode_t	*pip,
 	struct fsxattr	*fsxp,
 	char		**pp,
-	char		*name)
+	char		*name,
+	char		*current_path)
 {
 #define	IF_REGULAR	0
 #define	IF_RESERVED	1
@@ -878,6 +964,7 @@ parseproto(
 			libxfs_trans_log_inode(tp, pip, XFS_ILOG_CORE);
 		}
 		newdirectory(mp, tp, ip, pip);
+		write_timestamps(ip, current_path, name);
 		libxfs_trans_log_inode(tp, ip, flags);
 		error = -libxfs_trans_commit(tp);
 		if (error)
@@ -897,7 +984,24 @@ parseproto(
 					error);

 			rtinit(mp);
+			name = "";
+		}
+
+		char *path = NULL;
+		if (current_path != NULL && name != NULL) {
+			size_t path_len = strlen(current_path) + (name ? strlen(name) : 0) + 2;
+			path = malloc(path_len);
+			if (!path) {
+				fail(_("error allocating path name buffer"), errno);
+			}
+			snprintf(path, path_len, "%s/%s", current_path, name);
+		} else if (name != NULL) {
+			path = strdup(name);
+			if (!path) {
+				fail(_("error copying name into path buffer"), errno);
+			}
 		}
+
 		tp = NULL;
 		for (;;) {
 			name = getdirentname(pp);
@@ -905,14 +1009,17 @@ parseproto(
 				break;
 			if (strcmp(name, "$") == 0)
 				break;
-			parseproto(mp, ip, fsxp, pp, name);
+			parseproto(mp, ip, fsxp, pp, name, path);
 		}
 		libxfs_irele(ip);
+		if (path != NULL)
+			free(path);
 		return;
 	default:
 		ASSERT(0);
 		fail(_("Unknown format"), EINVAL);
 	}
+	write_timestamps(ip, current_path, name);
 	libxfs_trans_log_inode(tp, ip, flags);
 	error = -libxfs_trans_commit(tp);
 	if (error) {
@@ -924,6 +1031,7 @@ parseproto(
 	if (fmt == IF_REGULAR) {
 		writefile(ip, fname, fd);
 		writeattrs(ip, fname, fd);
+		write_timestamps(ip, current_path, name);
 		close(fd);
 	}
 	libxfs_irele(ip);
@@ -937,7 +1045,10 @@ parse_proto(
 	int		proto_slashes_are_spaces)
 {
 	slashes_are_spaces = proto_slashes_are_spaces;
-	parseproto(mp, NULL, fsx, pp, NULL);
+	parseproto(mp, NULL, fsx, pp, NULL, NULL);
+
+	if (originpath != NULL)
+		free(originpath);
 }

 /* Create a sb-rooted metadata file. */
--
2.49.0




[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux