Print this page
NEX-13374 NDMP should be able to backup unmounted ZFS filesystems
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5801 Snapshots left over after failed backups
Reviewed by: Rick Mesta <rick.mesta@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Revert "NEX-5801 Snapshots left over after failed backups"
This reverts commit f182fb95f09036db71fbfc6f0a6b90469b761f21.
NEX-5801 Snapshots left over after failed backups
Reviewed by: Rick Mesta <rick.mesta@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-2911 NDMP logging should use syslog and is too chatty
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/ndmpd/ndmp/ndmpd_chkpnt.c
+++ new/usr/src/cmd/ndmpd/ndmp/ndmpd_chkpnt.c
1 1 /*
2 2 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3 3 * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
4 4 * Copyright (c) 2013 Steven Hartland. All rights reserved.
5 5 * Copyright (c) 2016 Martin Matuska. All rights reserved.
6 + * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
6 7 */
7 8
8 9 /*
9 10 * BSD 3 Clause License
10 11 *
11 12 * Copyright (c) 2007, The Storage Networking Industry Association.
12 13 *
13 14 * Redistribution and use in source and binary forms, with or without
14 15 * modification, are permitted provided that the following conditions
15 16 * are met:
16 17 * - Redistributions of source code must retain the above copyright
17 18 * notice, this list of conditions and the following disclaimer.
18 19 *
19 20 * - Redistributions in binary form must reproduce the above copyright
20 21 * notice, this list of conditions and the following disclaimer in
21 22 * the documentation and/or other materials provided with the
22 23 * distribution.
23 24 *
24 25 * - Neither the name of The Storage Networking Industry Association (SNIA)
25 26 * nor the names of its contributors may be used to endorse or promote
26 27 * products derived from this software without specific prior written
27 28 * permission.
28 29 *
29 30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
30 31 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
↓ open down ↓ |
16 lines elided |
↑ open up ↑ |
32 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
33 34 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34 35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36 37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37 38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 40 * POSSIBILITY OF SUCH DAMAGE.
40 41 */
41 42
43 +#include <syslog.h>
42 44 #include <stdio.h>
43 45 #include <string.h>
46 +#include <sys/mount.h>
44 47 #include "ndmpd.h"
45 48 #include <libzfs.h>
46 49
47 -typedef struct snap_param {
48 - char *snp_name;
49 - boolean_t snp_found;
50 -} snap_param_t;
51 -
52 -static int cleanup_fd = -1;
53 -
54 50 /*
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 - */
69 -static int
70 -ndmp_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 - */
101 -static int
102 -ndmp_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 - */
140 -int
141 -ndmp_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 - */
174 -int
175 -ndmp_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 51 * Put a hold on snapshot
188 52 */
189 53 int
190 -snapshot_hold(char *volname, char *snapname, char *jname, boolean_t recursive)
54 +snapshot_hold(char *volname, char *snapname, char *jname)
191 55 {
192 56 zfs_handle_t *zhp;
193 57 char *p;
194 58
195 59 if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
196 - NDMP_LOG(LOG_ERR, "Cannot open volume %s.", volname);
60 + syslog(LOG_ERR, "Cannot open volume %s.", volname);
197 61 return (-1);
198 62 }
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 63 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);
64 + /*
65 + * The -1 tells the lower levels there are no snapshots
66 + * to clean up.
67 + */
68 + if (zfs_hold(zhp, p, jname, B_FALSE, -1) != 0) {
69 + syslog(LOG_ERR, "Cannot hold snapshot %s", p);
210 70 zfs_close(zhp);
211 71 return (-1);
212 72 }
213 73 zfs_close(zhp);
214 74 return (0);
215 75 }
216 76
217 77 int
218 -snapshot_release(char *volname, char *snapname, char *jname,
219 - boolean_t recursive)
78 +snapshot_release(char *volname, char *snapname, char *jname)
220 79 {
221 80 zfs_handle_t *zhp;
222 81 char *p;
223 82 int rv = 0;
224 83
225 84 if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
226 - NDMP_LOG(LOG_ERR, "Cannot open volume %s", volname);
85 + syslog(LOG_ERR, "Cannot open volume %s", volname);
227 86 return (-1);
228 87 }
229 88
230 89 p = strchr(snapname, '@') + 1;
231 - if (zfs_release(zhp, p, jname, recursive) != 0) {
232 - NDMP_LOG(LOG_DEBUG, "Cannot release snapshot %s", p);
90 + if (zfs_release(zhp, p, jname, B_FALSE) != 0) {
91 + syslog(LOG_DEBUG, "Cannot release snapshot %s", p);
233 92 rv = -1;
234 93 }
235 - if (cleanup_fd != -1) {
236 - (void) close(cleanup_fd);
237 - cleanup_fd = -1;
238 - }
239 94 zfs_close(zhp);
240 95 return (rv);
241 96 }
242 97
243 98 /*
244 - * Create a snapshot on the volume
99 + * Create a snapshot, put a hold on it, clone it, and mount it in a
100 + * well known location for so the backup process can traverse its
101 + * directory tree structure.
245 102 */
246 103 int
247 -snapshot_create(char *volname, char *jname, boolean_t recursive,
248 - boolean_t hold)
104 +backup_dataset_create(ndmp_lbr_params_t *nlp)
249 105 {
250 - char snapname[ZFS_MAX_DATASET_NAME_LEN];
106 + char zpoolname[ZFS_MAX_DATASET_NAME_LEN];
107 + char *slash;
251 108 int rv;
252 109
253 - if (!volname || !*volname)
110 + if (nlp == NULL) {
254 111 return (-1);
112 + }
255 113
256 - (void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
257 - "%s@%s", volname, jname);
114 + (void) strlcpy(zpoolname, nlp->nlp_vol, sizeof (zpoolname));
115 + /*
116 + * Pull out the pool name component from the volname
117 + * to use it to build snapshot and clone names.
118 + */
119 + slash = strchr(zpoolname, '/');
120 + if (slash != NULL) {
121 + *slash = '\0';
122 + }
258 123
124 + (void) snprintf(nlp->nlp_clonename, sizeof (nlp->nlp_clonename),
125 + "%s/%s", zpoolname, nlp->nlp_job_name);
126 +
259 127 (void) mutex_lock(&zlib_mtx);
260 - if ((rv = zfs_snapshot(zlibh, snapname, recursive, NULL))
261 - == -1) {
262 - if (errno == EEXIST) {
128 +
129 + /*
130 + * If "checkpoint" is not enabled, create the normal
131 + * snapshot and continue normal backup. If it is
132 + * enabled, the "checkpoint" name has been already set
133 + * so we just have to clone it.
134 + */
135 + if (!NLP_ISCHKPNTED(nlp)) {
136 + (void) snprintf(nlp->nlp_snapname, sizeof (nlp->nlp_snapname),
137 + "%s@%s", nlp->nlp_vol, nlp->nlp_job_name);
138 +
139 + if ((rv = zfs_snapshot(zlibh, nlp->nlp_snapname,
140 + B_FALSE, NULL)) != 0) {
141 + if (errno == EEXIST) {
142 + (void) mutex_unlock(&zlib_mtx);
143 + return (0);
144 + }
145 + syslog(LOG_ERR,
146 + "backup_dataset_create: %s failed (err=%d): %s",
147 + nlp->nlp_snapname, errno,
148 + libzfs_error_description(zlibh));
263 149 (void) mutex_unlock(&zlib_mtx);
264 - return (0);
150 + return (rv);
265 151 }
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);
152 + if (snapshot_hold(nlp->nlp_vol,
153 + nlp->nlp_snapname, NDMP_RCF_BASENAME) != 0) {
154 + syslog(LOG_DEBUG,
155 + "backup_dataset_create: %s "
156 + "hold failed (err=%d): %s",
157 + nlp->nlp_snapname,
158 + errno, libzfs_error_description(zlibh));
159 + (void) mutex_unlock(&zlib_mtx);
160 + return (-1);
161 + }
162 + syslog(LOG_DEBUG,
163 + "Using %s NdmpBackup snapshot for backup",
164 + nlp->nlp_snapname);
165 +
271 166 }
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));
167 +
168 + if (ndmp_clone_snapshot(nlp) != 0) {
169 + syslog(LOG_ERR,
170 + "backup_dataset_create: %s clone failed (err=%d): %s",
171 + nlp->nlp_snapname, errno, libzfs_error_description(zlibh));
276 172 (void) mutex_unlock(&zlib_mtx);
277 173 return (-1);
278 174 }
279 -
280 175 (void) mutex_unlock(&zlib_mtx);
281 176 return (0);
282 177 }
283 178
284 179 /*
285 - * Remove and release the backup snapshot
180 + * Unmount, release, and destroy the snapshot created for backup.
286 181 */
287 182 int
288 -snapshot_destroy(char *volname, char *jname, boolean_t recursive,
289 - boolean_t hold, int *zfs_err)
183 +backup_dataset_destroy(ndmp_lbr_params_t *nlp)
290 184 {
291 - char snapname[ZFS_MAX_DATASET_NAME_LEN];
292 - zfs_handle_t *zhp;
293 - zfs_type_t ztype;
294 - char *namep;
185 + char zpoolname[ZFS_MAX_DATASET_NAME_LEN];
186 + char *slash;
187 + zfs_handle_t *vol_zhp;
188 + zfs_handle_t *cln_zhp;
295 189 int err;
190 + int rv = 0;
296 191
297 - if (zfs_err)
298 - *zfs_err = 0;
299 -
300 - if (!volname || !*volname)
192 + if (nlp == NULL) {
193 + syslog(LOG_DEBUG,
194 + "nlp NULL in backup_dataset_destroy");
301 195 return (-1);
196 + }
302 197
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;
198 + (void) strlcpy(zpoolname, nlp->nlp_vol, sizeof (zpoolname));
199 + slash = strchr(zpoolname, '/');
200 + if (slash != NULL) {
201 + *slash = '\0';
311 202 }
312 203
204 + if (!NLP_ISCHKPNTED(nlp)) {
205 + (void) snprintf(nlp->nlp_snapname, sizeof (nlp->nlp_snapname),
206 + "%s@%s", nlp->nlp_vol, nlp->nlp_job_name);
207 + }
208 +
209 +
210 + syslog(LOG_DEBUG, "Snapname in backup_dataset_destroy is [%s]",
211 + nlp->nlp_snapname);
212 +
213 + /*
214 + * Destroy using this sequence
215 + * zfs release <volume>@<jname>
216 + * zfs destroy <pool>/<jname>
217 + * zfs destroy <pool>/<volume>@<jname>
218 + */
313 219 (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));
220 +
221 + /*
222 + * Release the normal snapshot but don't try to
223 + * release if it's a "checkpoint" because the hold
224 + * wasn't put on it to begin with.
225 + */
226 + if (!NLP_ISCHKPNTED(nlp)) {
227 + if (snapshot_release(nlp->nlp_vol,
228 + nlp->nlp_snapname, NDMP_RCF_BASENAME) != 0) {
229 + syslog(LOG_DEBUG,
230 + "backup_dataset_destroy: %s "
231 + "release failed (err=%d): %s",
232 + nlp->nlp_clonename, errno,
233 + libzfs_error_description(zlibh));
234 + (void) mutex_unlock(&zlib_mtx);
235 + return (-1);
236 + }
237 + } else {
238 + syslog(LOG_DEBUG, "Checkpointed dataset not held "
239 + "will not release [%s]", nlp->nlp_snapname);
240 + }
241 +
242 + /*
243 + * Open the clone to get descriptor
244 + */
245 + if ((cln_zhp = zfs_open(zlibh, nlp->nlp_clonename,
246 + ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM)) == NULL) {
247 + syslog(LOG_ERR,
248 + "backup_dataset_destroy: open %s failed",
249 + nlp->nlp_clonename);
319 250 (void) mutex_unlock(&zlib_mtx);
320 251 return (-1);
321 252 }
322 253
323 - if ((zhp = zfs_open(zlibh, namep, ztype)) == NULL) {
324 - NDMP_LOG(LOG_DEBUG, "snapshot_destroy: open %s failed",
325 - namep);
254 + /*
255 + * Open the mounted clone to get descriptor for unmount
256 + */
257 + if ((vol_zhp = zfs_open(zlibh, nlp->nlp_vol,
258 + ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM)) == NULL) {
259 + syslog(LOG_ERR,
260 + "backup_dataset_destroy: open %s failed [while trying "
261 + "to destroy]", nlp->nlp_vol);
262 + zfs_close(cln_zhp);
326 263 (void) mutex_unlock(&zlib_mtx);
327 264 return (-1);
328 265 }
329 266
330 - if (recursive) {
331 - err = zfs_destroy_snaps(zhp, jname, B_TRUE);
332 - } else {
333 - err = zfs_destroy(zhp, B_TRUE);
267 + /*
268 + * This unmounts the clone which was just traversed for backup
269 + */
270 + if ((err = zfs_unmount(cln_zhp, NULL, 0)) != 0) {
271 + syslog(LOG_INFO, "failed to unmount [%s]", nlp->nlp_clonename);
272 + rv = -1;
273 + goto _out;
334 274 }
335 275
276 + /*
277 + * This destroys the clone
278 + */
279 + err = zfs_destroy(cln_zhp, B_TRUE);
336 280 if (err) {
337 - NDMP_LOG(LOG_ERR, "%s (recursive destroy: %d): %d; %s; %s",
338 - namep,
339 - recursive,
281 + syslog(LOG_ERR, "%s destroy: %d; %s; %s",
282 + nlp->nlp_clonename,
340 283 libzfs_errno(zlibh),
341 284 libzfs_error_action(zlibh),
342 285 libzfs_error_description(zlibh));
286 + rv = -1;
287 + goto _out;
288 + }
343 289
344 - if (zfs_err)
345 - *zfs_err = err;
290 + /*
291 + * This destroys the snapshot of the current backup - but,
292 + * don't destroy it if it is an "checkpoint" from AutoSync
293 + * or HPR.
294 + */
295 + if (!NLP_ISCHKPNTED(nlp)) {
296 + if ((err = zfs_destroy_snaps(vol_zhp,
297 + nlp->nlp_job_name, B_TRUE))) {
298 + syslog(LOG_ERR, "%s destroy: %d; %s; %s",
299 + nlp->nlp_job_name,
300 + libzfs_errno(zlibh),
301 + libzfs_error_action(zlibh),
302 + libzfs_error_description(zlibh));
303 + rv = -1;
304 + syslog(LOG_DEBUG, "Destroy [%s]", nlp->nlp_snapname);
305 + goto _out;
306 + }
307 + } else {
308 + syslog(LOG_DEBUG, "Checkpointed checkpoint will not destroy [%s]",
309 + nlp->nlp_snapname);
346 310 }
347 311
348 - zfs_close(zhp);
312 +_out:
313 + zfs_close(vol_zhp);
314 + zfs_close(cln_zhp);
349 315 (void) mutex_unlock(&zlib_mtx);
350 316
351 - return (0);
317 + /*
318 + * The zfs_clone() call will have mounted the snapshot
319 + * in the file system at this point - so clean it up.
320 + */
321 + if (rv == 0) {
322 + if (rmdir(nlp->nlp_mountpoint) != 0) {
323 + syslog(LOG_ERR,
324 + "Failed to remove mount point [%s]",
325 + nlp->nlp_mountpoint);
326 + return (-1);
327 + }
328 + }
329 +
330 + return (rv);
352 331 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX