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
smbd_vss_get_count(const char * path,uint32_t * count)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
smbd_vss_get_snapshots(const char * path,uint32_t count,uint32_t * return_count,uint32_t * num_gmttokens,char ** gmttokenp)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
smbd_vss_map_gmttoken(const char * path,char * gmttoken,time_t toktime,char * snapname)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
smbd_vss_time2gmttoken(time_t time,char * gmttoken)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
smbd_vss_cmp_time(const void * a,const void * b)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
smbd_vss_iterate_count(zfs_handle_t * zhp,void * data)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
smbd_vss_iterate_get_uint64_date(zfs_handle_t * zhp,void * data)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
smbd_vss_iterate_map_gmttoken(zfs_handle_t * zhp,void * data)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