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