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