Blob Blame History Raw
From afccae852f231e8982276fa53614b3dc999b6bf8 Mon Sep 17 00:00:00 2001
From: Pavel Shilovsky <pshilov@microsoft.com>
Date: Wed, 3 Apr 2019 22:42:10 +0000
Subject: [PATCH 20/36] mount.cifs: detect GMT format of snapshot version

In order to provide an easy way to access snapshots a GMT
token string should be allowed as a "snapshot" mount option
argument, not SMB 100-nanoseconds time only. Detect if the
argument is in GMT format and convert it to SMB 100-nanoseconds
time before passing to the kernel.

Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
Reviewed-by: Paulo Alcantara <palcantara@suse.de>
(cherry picked from commit c52be345de22669c53a6ec41c28914183bf65d09)
Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
---
 mount.cifs.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 47 insertions(+), 7 deletions(-)

diff --git a/mount.cifs.c b/mount.cifs.c
index c6a1bd6..b3235e4 100644
--- a/mount.cifs.c
+++ b/mount.cifs.c
@@ -43,6 +43,7 @@
 #include <limits.h>
 #include <paths.h>
 #include <libgen.h>
+#include <time.h>
 #include <sys/mman.h>
 #include <sys/wait.h>
 #ifdef HAVE_SYS_FSUID_H
@@ -161,10 +162,16 @@
 #define OPT_BKUPUID    30
 #define OPT_BKUPGID    31
 #define OPT_NOFAIL     32
+#define OPT_SNAPSHOT   33
 
 #define MNT_TMP_FILE "/.mtab.cifs.XXXXXX"
 
-/* struct for holding parsed mount info for use by privleged process */
+#define GMT_NAME_LEN 24 /* length of a @GMT- name */
+#define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
+
+#define NTFS_TIME_OFFSET ((unsigned long long)(369*365 + 89) * 24 * 3600 * 10000000)
+
+/* struct for holding parsed mount info for use by privileged process */
 struct parsed_mount_info {
 	unsigned long flags;
 	char host[NI_MAXHOST + 1];
@@ -271,9 +278,9 @@ static int mount_usage(FILE * stream)
 	fprintf(stream,
 		"\n\tcache=<strict|none|loose>,nounix,cifsacl,sec=<authentication mechanism>,");
 	fprintf(stream,
-		"\n\tsign,seal,fsc,snapshot=<time>,nosharesock,persistenthandles,resilienthandles,");
+		"\n\tsign,seal,fsc,snapshot=<token|time>,nosharesock,persistenthandles,");
 	fprintf(stream,
-		"\n\trdma,vers=<smb_dialect>,cruid");
+		"\n\tresilienthandles,rdma,vers=<smb_dialect>,cruid");
 	fprintf(stream,
 		"\n\nOptions not needed for servers supporting CIFS Unix extensions");
 	fprintf(stream,
@@ -773,6 +780,8 @@ static int parse_opt_token(const char *token)
 		return OPT_NOFAIL;
 	if (strncmp(token, "x-", 2) == 0)
 		return OPT_IGNORE;
+	if (strncmp(token, "snapshot", 8) == 0)
+		return OPT_SNAPSHOT;
 
 	return OPT_ERROR;
 }
@@ -793,16 +802,19 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info)
 	int got_uid = 0;
 	int got_cruid = 0;
 	int got_gid = 0;
+	int got_snapshot = 0;
 	uid_t uid, cruid = 0, bkupuid = 0;
 	gid_t gid, bkupgid = 0;
 	char *ep;
 	struct passwd *pw;
 	struct group *gr;
 	/*
-	 * max 32-bit uint in decimal is 4294967295 which is 10 chars wide
-	 * +1 for NULL, and +1 for good measure
+	 * max 64-bit uint in decimal is 18446744073709551615 which is 20 chars
+	 * wide +1 for NULL, and +1 for good measure
 	 */
-	char txtbuf[12];
+	char txtbuf[22];
+	unsigned long long snapshot;
+	struct tm tm;
 
 	/* make sure we're starting from beginning */
 	out[0] = '\0';
@@ -1130,6 +1142,19 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info)
 		case OPT_NOFAIL:
 			parsed_info->nofail = 1;
 			goto nocopy;
+		case OPT_SNAPSHOT:
+			if (!value || !*value)
+				goto nocopy;
+			if (strncmp(value, "@GMT-", 5))
+				break;
+			if ((strlen(value) != GMT_NAME_LEN) ||
+			    (strptime(value, GMT_FORMAT, &tm) == NULL)) {
+				fprintf(stderr, "bad snapshot token\n");
+				return EX_USAGE;
+			}
+			snapshot = timegm(&tm) * 10000000 + NTFS_TIME_OFFSET;
+			got_snapshot = 1;
+			goto nocopy;
 		}
 
 		/* check size before copying option to buffer */
@@ -1225,7 +1250,7 @@ nocopy:
 	if (got_bkupgid) {
 		word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupgid);
 
-		/* comma + "backkupgid=" + terminating NULL == 12 */
+		/* comma + "backupgid=" + terminating NULL == 12 */
 		if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
 			fprintf(stderr, "Options string too long\n");
 			return EX_USAGE;
@@ -1237,6 +1262,21 @@ nocopy:
 		}
 		snprintf(out + out_len, word_len + 11, "backupgid=%s", txtbuf);
 	}
+	if (got_snapshot) {
+		word_len = snprintf(txtbuf, sizeof(txtbuf), "%llu", snapshot);
+
+		/* comma + "snapshot=" + terminating NULL == 11 */
+		if (out_len + word_len + 11 > MAX_OPTIONS_LEN) {
+			fprintf(stderr, "Options string too long\n");
+			return EX_USAGE;
+		}
+
+		if (out_len) {
+			strlcat(out, ",", MAX_OPTIONS_LEN);
+			out_len++;
+		}
+		snprintf(out + out_len, word_len + 11, "snapshot=%s", txtbuf);
+	}
 
 	return 0;
 }
-- 
1.8.3.1