From 46e9517105a7c9cbb744bea0e71fb35c781a0a62 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 29 Mar 2019 03:05:55 -0500 Subject: [PATCH] smbinfo: Add ability to query snapshots (previous versions) "smbinfo list-snapshots" Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- smbinfo.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++ smbinfo.rst | 2 ++ 2 files changed, 89 insertions(+) diff --git a/smbinfo.c b/smbinfo.c index 33fca95..7cffb84 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,89 @@ 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) + +#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */ + +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 */ + + buf = malloc(snap_inf.snapshot_array_size + MIN_SNAPSHOT_ARRAY_SIZE); + + 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; + + 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 +1101,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 { diff --git a/smbinfo.rst b/smbinfo.rst index fd7f0ff..0c96050 100644 --- a/smbinfo.rst +++ b/smbinfo.rst @@ -64,6 +64,8 @@ COMMAND `fsctl-getobjid`: Prints the ObjectID +`list-snapshots`: Lists the previous versions of the volume that backs this file + `quota`: Print the quota for the volume in the form - SID Length - Change Time -- 2.17.1