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