1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 25 * Copyright 2016 Martin Matuska. All rights reserved. 26 */ 27 28 #include <synch.h> 29 #include <pthread.h> 30 #include <unistd.h> 31 #include <string.h> 32 #include <strings.h> 33 #include <sys/errno.h> 34 #include <libzfs.h> 35 36 #include <smbsrv/libsmb.h> 37 #include <smbsrv/libsmbns.h> 38 #include <smbsrv/libmlsvc.h> 39 #include <smbsrv/smbinfo.h> 40 #include "smbd.h" 41 42 /* 43 * This file supports three basic functions that all use the 44 * the zfs_iter_snapshots function to get the snapshot info 45 * from ZFS. If the filesystem is not ZFS, the an error is sent 46 * to the caller (door functions in this case) with the count of 47 * zero in the case of smbd_vss_get_count. Each function 48 * is expecting a path that is the root of the dataset. 49 * The basic idea is to define a structure for the data and 50 * an iterator function that will be called for every snapshot 51 * in the dataset that was opened. The iterator function gets 52 * a zfs_handle_t(that needs to be closed) for the snapshot 53 * and a pointer to the structure of data defined passed to it. 54 * If the iterator function returns a non-zero value, no more 55 * snapshots will be processed. There is no guarantee in the 56 * order in which the snapshots are processed. 57 * 58 * The structure of this file is: 59 * Three structures that are used between the iterator functions 60 * and "main" functions 61 * The 3 "main" functions 62 * Support functions 63 * The 3 iterator functions 64 */ 65 66 /* 67 * The maximum number of snapshots returned per request. 68 */ 69 #define SMBD_VSS_SNAPSHOT_MAX 725 70 71 static void smbd_vss_time2gmttoken(time_t time, char *gmttoken); 72 static int smbd_vss_cmp_time(const void *a, const void *b); 73 static int smbd_vss_iterate_count(zfs_handle_t *zhp, void *data); 74 static int smbd_vss_iterate_get_uint64_date(zfs_handle_t *zhp, void *data); 75 static int smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data); 76 77 typedef struct smbd_vss_count { 78 int vc_count; 79 } smbd_vss_count_t; 80 81 /* 82 * gd_count how many @GMT tokens are expected 83 * gd_return_count how many @GMT tokens are being returned 84 * gd_gmt_array array of the @GMT token with max size of gd_count 85 */ 86 typedef struct smbd_vss_get_uint64_date { 87 int gd_count; 88 int gd_return_count; 89 uint64_t *gd_gmt_array; 90 } smbd_vss_get_uint64_date_t; 91 92 typedef struct smbd_vss_map_gmttoken { 93 time_t mg_snaptime; 94 char *mg_snapname; 95 } smbd_vss_map_gmttoken_t; 96 97 98 /* 99 * path - path of the dataset 100 * count - return value of the number of snapshots for the dataset 101 */ 102 int 103 smbd_vss_get_count(const char *path, uint32_t *count) 104 { 105 char dataset[MAXPATHLEN]; 106 libzfs_handle_t *libhd; 107 zfs_handle_t *zfshd; 108 smbd_vss_count_t vss_count; 109 110 bzero(&vss_count, sizeof (smbd_vss_count_t)); 111 *count = 0; 112 113 if ((libhd = libzfs_init()) == NULL) 114 return (-1); 115 116 if (smb_getdataset(libhd, path, dataset, MAXPATHLEN) != 0) { 117 libzfs_fini(libhd); 118 return (-1); 119 } 120 121 if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) { 122 libzfs_fini(libhd); 123 return (-1); 124 } 125 126 (void) zfs_iter_snapshots(zfshd, B_FALSE, smbd_vss_iterate_count, 127 (void *)&vss_count); 128 129 if (vss_count.vc_count > SMBD_VSS_SNAPSHOT_MAX) 130 vss_count.vc_count = SMBD_VSS_SNAPSHOT_MAX; 131 132 *count = vss_count.vc_count; 133 zfs_close(zfshd); 134 libzfs_fini(libhd); 135 return (0); 136 } 137 138 /* 139 * path - is the path of the dataset 140 * count - is the maxium number of GMT tokens allowed to be returned 141 * return_count - is how many should be returned 142 * num_gmttokens - how many gmttokens in gmttokenp (0 if error) 143 * gmttokenp - array of @GMT tokens (even if zero, elements still need 144 * to be freed) 145 */ 146 147 void 148 smbd_vss_get_snapshots(const char *path, uint32_t count, 149 uint32_t *return_count, uint32_t *num_gmttokens, char **gmttokenp) 150 { 151 char dataset[MAXPATHLEN]; 152 libzfs_handle_t *libhd; 153 zfs_handle_t *zfshd; 154 smbd_vss_get_uint64_date_t vss_uint64_date; 155 int i; 156 uint64_t *timep; 157 158 *return_count = 0; 159 *num_gmttokens = 0; 160 161 if (count == 0) 162 return; 163 164 if (count > SMBD_VSS_SNAPSHOT_MAX) 165 count = SMBD_VSS_SNAPSHOT_MAX; 166 167 vss_uint64_date.gd_count = count; 168 vss_uint64_date.gd_return_count = 0; 169 vss_uint64_date.gd_gmt_array = malloc(count * sizeof (uint64_t)); 170 171 if (vss_uint64_date.gd_gmt_array == NULL) 172 return; 173 174 if ((libhd = libzfs_init()) == NULL) { 175 free(vss_uint64_date.gd_gmt_array); 176 return; 177 } 178 179 if (smb_getdataset(libhd, path, dataset, MAXPATHLEN) != 0) { 180 free(vss_uint64_date.gd_gmt_array); 181 libzfs_fini(libhd); 182 return; 183 } 184 185 if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) { 186 free(vss_uint64_date.gd_gmt_array); 187 libzfs_fini(libhd); 188 return; 189 } 190 191 (void) zfs_iter_snapshots(zfshd, B_FALSE, 192 smbd_vss_iterate_get_uint64_date, (void *)&vss_uint64_date); 193 194 *num_gmttokens = vss_uint64_date.gd_return_count; 195 *return_count = vss_uint64_date.gd_return_count; 196 197 /* 198 * Sort the list since neither zfs nor the client sorts it. 199 */ 200 qsort((char *)vss_uint64_date.gd_gmt_array, 201 vss_uint64_date.gd_return_count, 202 sizeof (uint64_t), smbd_vss_cmp_time); 203 204 timep = vss_uint64_date.gd_gmt_array; 205 206 for (i = 0; i < vss_uint64_date.gd_return_count; i++) { 207 *gmttokenp = malloc(SMB_VSS_GMT_SIZE); 208 209 if (*gmttokenp) 210 smbd_vss_time2gmttoken(*timep, *gmttokenp); 211 else 212 vss_uint64_date.gd_return_count = 0; 213 214 timep++; 215 gmttokenp++; 216 } 217 218 free(vss_uint64_date.gd_gmt_array); 219 zfs_close(zfshd); 220 libzfs_fini(libhd); 221 } 222 223 static const char 224 smbd_vss_gmttoken_fmt[] = "@GMT-%Y.%m.%d-%H.%M.%S"; 225 226 /* 227 * path - path of the dataset for the operation 228 * gmttoken - the @GMT token to be looked up 229 * toktime - time_t used if gmttoken == NULL 230 * snapname - the snapshot name to be returned [MAXPATHLEN] 231 * 232 * Here we are going to get the snapshot name from the @GMT token 233 * The snapname returned by ZFS is : <dataset name>@<snapshot name> 234 * So we are going to make sure there is the @ symbol in 235 * the right place and then just return the snapshot name 236 */ 237 int 238 smbd_vss_map_gmttoken(const char *path, char *gmttoken, time_t toktime, 239 char *snapname) 240 { 241 char dataset[MAXPATHLEN]; 242 libzfs_handle_t *libhd; 243 zfs_handle_t *zfshd; 244 smbd_vss_map_gmttoken_t vss_map_gmttoken; 245 char *zsnap; 246 const char *lsnap; 247 struct tm tm; 248 249 if (gmttoken != NULL && *gmttoken == '@' && 250 strptime(gmttoken, smbd_vss_gmttoken_fmt, &tm) != NULL) { 251 toktime = timegm(&tm); 252 } 253 254 vss_map_gmttoken.mg_snaptime = toktime; 255 vss_map_gmttoken.mg_snapname = snapname; 256 *snapname = '\0'; 257 258 if ((libhd = libzfs_init()) == NULL) 259 return (-1); 260 261 if (smb_getdataset(libhd, path, dataset, MAXPATHLEN) != 0) { 262 libzfs_fini(libhd); 263 return (-1); 264 } 265 266 if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) { 267 libzfs_fini(libhd); 268 return (-1); 269 } 270 271 (void) zfs_iter_snapshots(zfshd, B_FALSE, smbd_vss_iterate_map_gmttoken, 272 (void *)&vss_map_gmttoken); 273 274 /* compare the zfs snapshot name and the local snap name */ 275 zsnap = snapname; 276 lsnap = dataset; 277 while ((*lsnap != '\0') && (*zsnap != '\0') && (*lsnap == *zsnap)) { 278 zsnap++; 279 lsnap++; 280 } 281 282 /* Now we should be passed the dataset name */ 283 if ((*zsnap == '@') && (*lsnap == '\0')) { 284 zsnap++; 285 (void) strlcpy(snapname, zsnap, MAXPATHLEN); 286 } else { 287 *snapname = '\0'; 288 } 289 290 zfs_close(zfshd); 291 libzfs_fini(libhd); 292 return (0); 293 } 294 295 static void 296 smbd_vss_time2gmttoken(time_t time, char *gmttoken) 297 { 298 struct tm t; 299 300 (void) gmtime_r(&time, &t); 301 302 (void) strftime(gmttoken, SMB_VSS_GMT_SIZE, 303 smbd_vss_gmttoken_fmt, &t); 304 } 305 306 static int 307 smbd_vss_cmp_time(const void *a, const void *b) 308 { 309 if (*(uint64_t *)a < *(uint64_t *)b) 310 return (1); 311 if (*(uint64_t *)a == *(uint64_t *)b) 312 return (0); 313 return (-1); 314 } 315 316 /* 317 * ZFS snapshot iterator to count snapshots. 318 * Note: libzfs expects us to close the handle. 319 * Return 0 to continue iterating or non-zreo to terminate the iteration. 320 */ 321 static int 322 smbd_vss_iterate_count(zfs_handle_t *zhp, void *data) 323 { 324 smbd_vss_count_t *vss_data = data; 325 326 if (vss_data->vc_count < SMBD_VSS_SNAPSHOT_MAX) { 327 vss_data->vc_count++; 328 zfs_close(zhp); 329 return (0); 330 } 331 332 zfs_close(zhp); 333 return (-1); 334 } 335 336 /* 337 * ZFS snapshot iterator to get snapshot creation time. 338 * Note: libzfs expects us to close the handle. 339 * Return 0 to continue iterating or non-zreo to terminate the iteration. 340 */ 341 static int 342 smbd_vss_iterate_get_uint64_date(zfs_handle_t *zhp, void *data) 343 { 344 smbd_vss_get_uint64_date_t *vss_data = data; 345 int count; 346 347 count = vss_data->gd_return_count; 348 349 if (count < vss_data->gd_count) { 350 vss_data->gd_gmt_array[count] = 351 zfs_prop_get_int(zhp, ZFS_PROP_CREATION); 352 vss_data->gd_return_count++; 353 zfs_close(zhp); 354 return (0); 355 } 356 357 zfs_close(zhp); 358 return (-1); 359 } 360 361 /* 362 * ZFS snapshot iterator to map a snapshot creation time to a token. 363 * Note: libzfs expects us to close the handle. 364 * Return 0 to continue iterating or non-zreo to terminate the iteration. 365 */ 366 static int 367 smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data) 368 { 369 smbd_vss_map_gmttoken_t *vss_data = data; 370 time_t time; 371 372 time = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION); 373 if (time == vss_data->mg_snaptime) { 374 (void) strlcpy(vss_data->mg_snapname, zfs_get_name(zhp), 375 MAXPATHLEN); 376 377 /* we found a match, do not process anymore snapshots */ 378 zfs_close(zhp); 379 return (-1); 380 } 381 382 zfs_close(zhp); 383 return (0); 384 } 385