Patch to smbinfo tool (in cifs-utils) to allow listing previous versions (snapshots) of an SMB3 mounted share. Note that cifs.ko has a mount option "snapshot=" which can be used to mount the previous version of a share. Sample output: $ ./smbinfo list-snapshots /mnt/file Number of snapshots: 5 Number of snapshots returned: 5 Snapshot list: 1) @GMT-2019.03.27-20.52.19 2) @GMT-2019.03.27-20.50.20 3) @GMT-2019.03.16-22.25.54 4) @GMT-2019.03.16-22.22.28 5) @GMT-2018.10.12-08.30.11 -- Thanks, Steve
From dd4650114a9b31f210f359327735dcffdc8139d9 Mon Sep 17 00:00:00 2001 From: Steve French <stfrench@xxxxxxxxxxxxx> Date: Thu, 28 Mar 2019 04:58:19 -0500 Subject: [PATCH] smbinfo: Add ability to query snapshots (previous versions) Allow listing SMB3 snapshots (previous versions of shares) Signed-off-by: Steve French <stfrench@xxxxxxxxxxxxx> --- smbinfo.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/smbinfo.c b/smbinfo.c index 33fca95..8f6566a 100644 --- a/smbinfo.c +++ b/smbinfo.c @@ -89,6 +89,8 @@ usage(char *name) " Prints the security descriptor for a cifs file.\n" " quota:\n" " Prints the quota for a cifs file.\n" + " list-snapshots:\n" + " List the previous versions of the volume that backs this file.\n" " fsctl-getobjid:\n" " Prints the objectid of the file and GUID of the underlying volume.\n", name); @@ -966,6 +968,88 @@ quota(int f) free(qi); } + +struct smb_snapshot_array { + int32_t number_of_snapshots; + int32_t number_of_snapshots_returned; + int32_t snapshot_array_size; + char snapshot_data[0]; +}; + + +static void print_snapshots(struct smb_snapshot_array *psnap) +{ + int current_snapshot_entry = 0; + + printf("Number of snapshots: %d Number of snapshots returned: %d\n", + psnap->number_of_snapshots, + psnap->number_of_snapshots_returned); + printf("Snapshot list:"); + + for (int i = 0; i < psnap->snapshot_array_size; i++) { + if (psnap->snapshot_data[i] == '@') { + current_snapshot_entry++; + printf("\n%d) ", current_snapshot_entry); + } + printf("%c", psnap->snapshot_data[i]); + } + printf("\n"); +} + +#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array) + +static void +list_snapshots(int f) +{ + + struct smb_snapshot_array snap_inf; + struct smb_snapshot_array *buf; + + /* + * When first field in structure we pass in here is zero, cifs.ko can + * recognize that this is the first query and that it must set the SMB3 + * FSCTL response buffer size (in the request) to exactly 16 bytes + * (which is required by some servers to process the initial query) + */ + snap_inf.number_of_snapshots = 0; + snap_inf.number_of_snapshots_returned = 0; + snap_inf.snapshot_array_size = sizeof(struct smb_snapshot_array); + + /* Query the number of snapshots so we know how much to allocate */ + if (ioctl(f, CIFS_ENUMERATE_SNAPSHOTS, &snap_inf) < 0) { + fprintf(stderr, "Querying snapshots failed with %s\n", strerror(errno)); + exit(1); + } + + if (snap_inf.number_of_snapshots == 0) + return; + + /* Now that we know the size, query the list from the server */ + + /* Make sure the buf size is big enough even to handle unexpected server behavior */ + buf = malloc(snap_inf.snapshot_array_size + 300); + + if (buf == NULL) { + printf("Failed, out of memory.\n"); + exit(1); + } + /* + * first parm is non-zero which allows cifs.ko to recognize that this is + * the second query (it has to set response buf size larger) + */ + buf->number_of_snapshots = snap_inf.number_of_snapshots; + + buf->snapshot_array_size = snap_inf.snapshot_array_size + 200; + + if (ioctl(f, CIFS_ENUMERATE_SNAPSHOTS, buf) < 0) { + fprintf(stderr, "Querying snapshots failed with %s\n", strerror(errno)); + exit(1); + } + + print_snapshots(buf); + free(buf); +} + int main(int argc, char *argv[]) { int c; @@ -1016,6 +1100,8 @@ int main(int argc, char *argv[]) secdesc(f); else if (!strcmp(argv[optind], "quota")) quota(f); + else if (!strcmp(argv[optind], "list-snapshots")) + list_snapshots(f); else if (!strcmp(argv[1], "fsctl-getobjid")) fsctlgetobjid(f); else { -- 2.17.1
From dd4650114a9b31f210f359327735dcffdc8139d9 Mon Sep 17 00:00:00 2001 From: Steve French <stfrench@xxxxxxxxxxxxx> Date: Thu, 28 Mar 2019 04:58:19 -0500 Subject: [PATCH] smbinfo: Add ability to query snapshots (previous versions) Allow listing SMB3 snapshots (previous versions of shares) Signed-off-by: Steve French <stfrench@xxxxxxxxxxxxx> --- smbinfo.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/smbinfo.c b/smbinfo.c index 33fca95..8f6566a 100644 --- a/smbinfo.c +++ b/smbinfo.c @@ -89,6 +89,8 @@ usage(char *name) " Prints the security descriptor for a cifs file.\n" " quota:\n" " Prints the quota for a cifs file.\n" + " list-snapshots:\n" + " List the previous versions of the volume that backs this file.\n" " fsctl-getobjid:\n" " Prints the objectid of the file and GUID of the underlying volume.\n", name); @@ -966,6 +968,88 @@ quota(int f) free(qi); } + +struct smb_snapshot_array { + int32_t number_of_snapshots; + int32_t number_of_snapshots_returned; + int32_t snapshot_array_size; + char snapshot_data[0]; +}; + + +static void print_snapshots(struct smb_snapshot_array *psnap) +{ + int current_snapshot_entry = 0; + + printf("Number of snapshots: %d Number of snapshots returned: %d\n", + psnap->number_of_snapshots, + psnap->number_of_snapshots_returned); + printf("Snapshot list:"); + + for (int i = 0; i < psnap->snapshot_array_size; i++) { + if (psnap->snapshot_data[i] == '@') { + current_snapshot_entry++; + printf("\n%d) ", current_snapshot_entry); + } + printf("%c", psnap->snapshot_data[i]); + } + printf("\n"); +} + +#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array) + +static void +list_snapshots(int f) +{ + + struct smb_snapshot_array snap_inf; + struct smb_snapshot_array *buf; + + /* + * When first field in structure we pass in here is zero, cifs.ko can + * recognize that this is the first query and that it must set the SMB3 + * FSCTL response buffer size (in the request) to exactly 16 bytes + * (which is required by some servers to process the initial query) + */ + snap_inf.number_of_snapshots = 0; + snap_inf.number_of_snapshots_returned = 0; + snap_inf.snapshot_array_size = sizeof(struct smb_snapshot_array); + + /* Query the number of snapshots so we know how much to allocate */ + if (ioctl(f, CIFS_ENUMERATE_SNAPSHOTS, &snap_inf) < 0) { + fprintf(stderr, "Querying snapshots failed with %s\n", strerror(errno)); + exit(1); + } + + if (snap_inf.number_of_snapshots == 0) + return; + + /* Now that we know the size, query the list from the server */ + + /* Make sure the buf size is big enough even to handle unexpected server behavior */ + buf = malloc(snap_inf.snapshot_array_size + 300); + + if (buf == NULL) { + printf("Failed, out of memory.\n"); + exit(1); + } + /* + * first parm is non-zero which allows cifs.ko to recognize that this is + * the second query (it has to set response buf size larger) + */ + buf->number_of_snapshots = snap_inf.number_of_snapshots; + + buf->snapshot_array_size = snap_inf.snapshot_array_size + 200; + + if (ioctl(f, CIFS_ENUMERATE_SNAPSHOTS, buf) < 0) { + fprintf(stderr, "Querying snapshots failed with %s\n", strerror(errno)); + exit(1); + } + + print_snapshots(buf); + free(buf); +} + int main(int argc, char *argv[]) { int c; @@ -1016,6 +1100,8 @@ int main(int argc, char *argv[]) secdesc(f); else if (!strcmp(argv[optind], "quota")) quota(f); + else if (!strcmp(argv[optind], "list-snapshots")) + list_snapshots(f); else if (!strcmp(argv[1], "fsctl-getobjid")) fsctlgetobjid(f); else { -- 2.17.1