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