1/*
2 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
4 * Copyright (c) 2013 Steven Hartland. All rights reserved.
5 * Copyright (c) 2016 Martin Matuska. All rights reserved.
6 */
7
8/*
9 * BSD 3 Clause License
10 *
11 * Copyright (c) 2007, The Storage Networking Industry Association.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 	- Redistributions of source code must retain the above copyright
17 *	  notice, this list of conditions and the following disclaimer.
18 *
19 * 	- Redistributions in binary form must reproduce the above copyright
20 *	  notice, this list of conditions and the following disclaimer in
21 *	  the documentation and/or other materials provided with the
22 *	  distribution.
23 *
24 *	- Neither the name of The Storage Networking Industry Association (SNIA)
25 *	  nor the names of its contributors may be used to endorse or promote
26 *	  products derived from this software without specific prior written
27 *	  permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
30 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
33 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 * POSSIBILITY OF SUCH DAMAGE.
40 */
41
42#include <stdio.h>
43#include <string.h>
44#include "ndmpd.h"
45#include <libzfs.h>
46
47typedef struct snap_param {
48	char *snp_name;
49	boolean_t snp_found;
50} snap_param_t;
51
52static int cleanup_fd = -1;
53
54/*
55 * ndmp_has_backup
56 *
57 * Call backup function which looks for backup snapshot.
58 * This is a callback function used with zfs_iter_snapshots.
59 *
60 * Parameters:
61 *   zhp (input) - ZFS handle pointer
62 *   data (output) - 0 - no backup snapshot
63 *		     1 - has backup snapshot
64 *
65 * Returns:
66 *   0: on success
67 *  -1: otherwise
68 */
69static int
70ndmp_has_backup(zfs_handle_t *zhp, void *data)
71{
72	const char *name;
73	snap_param_t *chp = (snap_param_t *)data;
74
75	name = zfs_get_name(zhp);
76	if (name == NULL ||
77	    strstr(name, chp->snp_name) == NULL) {
78		zfs_close(zhp);
79		return (-1);
80	}
81
82	chp->snp_found = 1;
83	zfs_close(zhp);
84
85	return (0);
86}
87
88/*
89 * ndmp_has_backup_snapshot
90 *
91 * Returns TRUE if the volume has an active backup snapshot, otherwise,
92 * returns FALSE.
93 *
94 * Parameters:
95 *   volname (input) - name of the volume
96 *
97 * Returns:
98 *   0: on success
99 *  -1: otherwise
100 */
101static int
102ndmp_has_backup_snapshot(char *volname, char *jobname)
103{
104	zfs_handle_t *zhp;
105	snap_param_t snp;
106	char chname[ZFS_MAX_DATASET_NAME_LEN];
107
108	(void) mutex_lock(&zlib_mtx);
109	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
110		NDMP_LOG(LOG_ERR, "Cannot open snapshot %s.", volname);
111		(void) mutex_unlock(&zlib_mtx);
112		return (-1);
113	}
114
115	snp.snp_found = 0;
116	(void) snprintf(chname, ZFS_MAX_DATASET_NAME_LEN, "@%s", jobname);
117	snp.snp_name = chname;
118
119	(void) zfs_iter_snapshots(zhp, B_FALSE, ndmp_has_backup, &snp);
120	zfs_close(zhp);
121	(void) mutex_unlock(&zlib_mtx);
122
123	return (snp.snp_found);
124}
125
126/*
127 * ndmp_create_snapshot
128 *
129 * This function will parse the path to get the real volume name.
130 * It will then create a snapshot based on volume and job name.
131 * This function should be called before the NDMP backup is started.
132 *
133 * Parameters:
134 *   vol_name (input) - name of the volume
135 *
136 * Returns:
137 *   0: on success
138 *   -1: otherwise
139 */
140int
141ndmp_create_snapshot(char *vol_name, char *jname)
142{
143	char vol[ZFS_MAX_DATASET_NAME_LEN];
144
145	if (vol_name == 0 ||
146	    get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
147		return (0);
148
149	/*
150	 * If there is an old snapshot left from the previous
151	 * backup it could be stale one and it must be
152	 * removed before using it.
153	 */
154	if (ndmp_has_backup_snapshot(vol, jname))
155		(void) snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL);
156
157	return (snapshot_create(vol, jname, B_FALSE, B_TRUE));
158}
159
160/*
161 * ndmp_remove_snapshot
162 *
163 * This function will parse the path to get the real volume name.
164 * It will then remove the snapshot for that volume and job name.
165 * This function should be called after NDMP backup is finished.
166 *
167 * Parameters:
168 *   vol_name (input) - name of the volume
169 *
170 * Returns:
171 *   0: on success
172 *   -1: otherwise
173 */
174int
175ndmp_remove_snapshot(char *vol_name, char *jname)
176{
177	char vol[ZFS_MAX_DATASET_NAME_LEN];
178
179	if (vol_name == 0 ||
180	    get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
181		return (0);
182
183	return (snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL));
184}
185
186/*
187 * Put a hold on snapshot
188 */
189int
190snapshot_hold(char *volname, char *snapname, char *jname, boolean_t recursive)
191{
192	zfs_handle_t *zhp;
193	char *p;
194
195	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
196		NDMP_LOG(LOG_ERR, "Cannot open volume %s.", volname);
197		return (-1);
198	}
199
200	if (cleanup_fd == -1 && (cleanup_fd = open(ZFS_DEV,
201	    O_RDWR|O_EXCL)) < 0) {
202		NDMP_LOG(LOG_ERR, "Cannot open dev %d", errno);
203		zfs_close(zhp);
204		return (-1);
205	}
206
207	p = strchr(snapname, '@') + 1;
208	if (zfs_hold(zhp, p, jname, recursive, cleanup_fd) != 0) {
209		NDMP_LOG(LOG_ERR, "Cannot hold snapshot %s", p);
210		zfs_close(zhp);
211		return (-1);
212	}
213	zfs_close(zhp);
214	return (0);
215}
216
217int
218snapshot_release(char *volname, char *snapname, char *jname,
219    boolean_t recursive)
220{
221	zfs_handle_t *zhp;
222	char *p;
223	int rv = 0;
224
225	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
226		NDMP_LOG(LOG_ERR, "Cannot open volume %s", volname);
227		return (-1);
228	}
229
230	p = strchr(snapname, '@') + 1;
231	if (zfs_release(zhp, p, jname, recursive) != 0) {
232		NDMP_LOG(LOG_DEBUG, "Cannot release snapshot %s", p);
233		rv = -1;
234	}
235	if (cleanup_fd != -1) {
236		(void) close(cleanup_fd);
237		cleanup_fd = -1;
238	}
239	zfs_close(zhp);
240	return (rv);
241}
242
243/*
244 * Create a snapshot on the volume
245 */
246int
247snapshot_create(char *volname, char *jname, boolean_t recursive,
248    boolean_t hold)
249{
250	char snapname[ZFS_MAX_DATASET_NAME_LEN];
251	int rv;
252
253	if (!volname || !*volname)
254		return (-1);
255
256	(void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
257	    "%s@%s", volname, jname);
258
259	(void) mutex_lock(&zlib_mtx);
260	if ((rv = zfs_snapshot(zlibh, snapname, recursive, NULL))
261	    == -1) {
262		if (errno == EEXIST) {
263			(void) mutex_unlock(&zlib_mtx);
264			return (0);
265		}
266		NDMP_LOG(LOG_DEBUG,
267		    "snapshot_create: %s failed (err=%d): %s",
268		    snapname, errno, libzfs_error_description(zlibh));
269		(void) mutex_unlock(&zlib_mtx);
270		return (rv);
271	}
272	if (hold && snapshot_hold(volname, snapname, jname, recursive) != 0) {
273		NDMP_LOG(LOG_DEBUG,
274		    "snapshot_create: %s hold failed (err=%d): %s",
275		    snapname, errno, libzfs_error_description(zlibh));
276		(void) mutex_unlock(&zlib_mtx);
277		return (-1);
278	}
279
280	(void) mutex_unlock(&zlib_mtx);
281	return (0);
282}
283
284/*
285 * Remove and release the backup snapshot
286 */
287int
288snapshot_destroy(char *volname, char *jname, boolean_t recursive,
289    boolean_t hold, int *zfs_err)
290{
291	char snapname[ZFS_MAX_DATASET_NAME_LEN];
292	zfs_handle_t *zhp;
293	zfs_type_t ztype;
294	char *namep;
295	int err;
296
297	if (zfs_err)
298		*zfs_err = 0;
299
300	if (!volname || !*volname)
301		return (-1);
302
303	if (recursive) {
304		ztype = ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM;
305		namep = volname;
306	} else {
307		(void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
308		    "%s@%s", volname, jname);
309		namep = snapname;
310		ztype = ZFS_TYPE_SNAPSHOT;
311	}
312
313	(void) mutex_lock(&zlib_mtx);
314	if (hold &&
315	    snapshot_release(volname, namep, jname, recursive) != 0) {
316		NDMP_LOG(LOG_DEBUG,
317		    "snapshot_destroy: %s release failed (err=%d): %s",
318		    namep, errno, libzfs_error_description(zlibh));
319		(void) mutex_unlock(&zlib_mtx);
320		return (-1);
321	}
322
323	if ((zhp = zfs_open(zlibh, namep, ztype)) == NULL) {
324		NDMP_LOG(LOG_DEBUG, "snapshot_destroy: open %s failed",
325		    namep);
326		(void) mutex_unlock(&zlib_mtx);
327		return (-1);
328	}
329
330	if (recursive) {
331		err = zfs_destroy_snaps(zhp, jname, B_TRUE);
332	} else {
333		err = zfs_destroy(zhp, B_TRUE);
334	}
335
336	if (err) {
337		NDMP_LOG(LOG_ERR, "%s (recursive destroy: %d): %d; %s; %s",
338		    namep,
339		    recursive,
340		    libzfs_errno(zlibh),
341		    libzfs_error_action(zlibh),
342		    libzfs_error_description(zlibh));
343
344		if (zfs_err)
345			*zfs_err = err;
346	}
347
348	zfs_close(zhp);
349	(void) mutex_unlock(&zlib_mtx);
350
351	return (0);
352}
353