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