xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_chkpnt.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * BSD 3 Clause License
8  *
9  * Copyright (c) 2007, The Storage Networking Industry Association.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 	- Redistributions of source code must retain the above copyright
15  *	  notice, this list of conditions and the following disclaimer.
16  *
17  * 	- Redistributions in binary form must reproduce the above copyright
18  *	  notice, this list of conditions and the following disclaimer in
19  *	  the documentation and/or other materials provided with the
20  *	  distribution.
21  *
22  *	- Neither the name of The Storage Networking Industry Association (SNIA)
23  *	  nor the names of its contributors may be used to endorse or promote
24  *	  products derived from this software without specific prior written
25  *	  permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #include <stdio.h>
41 #include <string.h>
42 #include "ndmpd.h"
43 #include <libzfs.h>
44 
45 ndmp_chkpnt_vol_t *chkpnt_vols = NULL;
46 
47 typedef struct chkpnt_param {
48 	char *chp_name;
49 	boolean_t chp_found;
50 } chkpnt_param_t;
51 
52 /*
53  * ndmp_has_backup
54  *
55  * Call backup function which looks for backup snapshot.
56  * This is a callback function used with zfs_iter_snapshots.
57  *
58  * Parameters:
59  *   zhp (input) - ZFS handle pointer
60  *   data (output) - 0 - no backup snapshot
61  *		     1 - has backup snapshot
62  *
63  * Returns:
64  *   0: on success
65  *  -1: otherwise
66  */
67 static int
68 ndmp_has_backup(zfs_handle_t *zhp, void *data)
69 {
70 	const char *name;
71 	chkpnt_param_t *chp = (chkpnt_param_t *)data;
72 
73 	name = zfs_get_name(zhp);
74 	if (name == NULL ||
75 	    strstr(name, chp->chp_name) == NULL) {
76 		zfs_close(zhp);
77 		return (-1);
78 	}
79 
80 	chp->chp_found = 1;
81 	zfs_close(zhp);
82 
83 	return (0);
84 }
85 
86 /*
87  * ndmp_has_backup_chkpnt
88  *
89  * Returns TRUE if the volume has an active backup snapshot, otherwise,
90  * returns FALSE.
91  *
92  * Parameters:
93  *   volname (input) - name of the volume
94  *
95  * Returns:
96  *   0: on success
97  *  -1: otherwise
98  */
99 static int
100 ndmp_has_backup_chkpnt(char *volname, char *jobname)
101 {
102 	zfs_handle_t *zhp;
103 	chkpnt_param_t chkp;
104 	char chname[ZFS_MAXNAMELEN];
105 
106 	(void) mutex_lock(&zlib_mtx);
107 	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
108 		NDMP_LOG(LOG_ERR, "Cannot open checkpoint %s.", volname);
109 		(void) mutex_unlock(&zlib_mtx);
110 		return (-1);
111 	}
112 
113 	chkp.chp_found = 0;
114 	(void) snprintf(chname, ZFS_MAXNAMELEN, "@bk-%s", jobname);
115 	chkp.chp_name = chname;
116 
117 	(void) zfs_iter_snapshots(zhp, ndmp_has_backup, &chkp);
118 	zfs_close(zhp);
119 	(void) mutex_unlock(&zlib_mtx);
120 
121 	return (chkp.chp_found);
122 }
123 
124 
125 /*
126  * ndmp_add_chk_pnt_vol
127  *
128  * This function keep track of check points created by NDMP. Whenever the
129  * NDMP check points need to be created, this function should be called.
130  * If the value returned is bigger than 1, it indicates that the check point
131  * has already exists and should not be created.
132  *
133  * Parameters:
134  *   vol_name (input) - name of the volume
135  *
136  * Returns:
137  *   The number of existing snapshots
138  */
139 static unsigned int
140 ndmp_add_chk_pnt_vol(char *vol_name)
141 {
142 	ndmp_chkpnt_vol_t *new_chkpnt_vol;
143 
144 	for (new_chkpnt_vol = chkpnt_vols; new_chkpnt_vol != NULL;
145 	    new_chkpnt_vol = new_chkpnt_vol->cv_next) {
146 		if (strcmp(new_chkpnt_vol->cv_vol_name, vol_name) == 0) {
147 			new_chkpnt_vol->cv_count++;
148 			return (new_chkpnt_vol->cv_count);
149 		}
150 	}
151 
152 	new_chkpnt_vol = ndmp_malloc(sizeof (ndmp_chkpnt_vol_t));
153 	if (new_chkpnt_vol == NULL)
154 		return (0);
155 
156 	(void) memset(new_chkpnt_vol, 0, sizeof (ndmp_chkpnt_vol_t));
157 	(void) strlcpy(new_chkpnt_vol->cv_vol_name, vol_name,
158 	    sizeof (new_chkpnt_vol->cv_vol_name));
159 
160 	new_chkpnt_vol->cv_count++;
161 
162 	if (chkpnt_vols == NULL) {
163 		chkpnt_vols = new_chkpnt_vol;
164 	} else {
165 		new_chkpnt_vol->cv_next = chkpnt_vols;
166 		chkpnt_vols = new_chkpnt_vol;
167 	}
168 
169 	return (new_chkpnt_vol->cv_count);
170 }
171 
172 
173 /*
174  * ndmp_remove_chk_pnt_vol
175  *
176  * This function will decrement the usage counter belongs to the check point.
177  * Whenever a check point needs to be removed, this function should be
178  * called. When the return value is greater than zero, it indicates someone
179  * else is still using the check point and the check point should not be
180  * removed.
181  *
182  * Parameters:
183  *   vol_name (input) - name of the volume
184  *
185  * Returns:
186  *   The number of existing snapshots
187  */
188 static unsigned int
189 ndmp_remove_chk_pnt_vol(char *vol_name)
190 {
191 	ndmp_chkpnt_vol_t *new_chkpnt_vol, *pre_chkpnt_vol;
192 
193 	pre_chkpnt_vol = chkpnt_vols;
194 	for (new_chkpnt_vol = chkpnt_vols; new_chkpnt_vol != NULL;
195 	    new_chkpnt_vol = new_chkpnt_vol->cv_next) {
196 		if (strcmp(new_chkpnt_vol->cv_vol_name, vol_name) == 0) {
197 			new_chkpnt_vol->cv_count--;
198 
199 			if (new_chkpnt_vol->cv_count == 0) {
200 				if (pre_chkpnt_vol == new_chkpnt_vol &&
201 				    new_chkpnt_vol->cv_next == NULL)
202 					chkpnt_vols = NULL;
203 				else if (pre_chkpnt_vol == new_chkpnt_vol)
204 					chkpnt_vols = new_chkpnt_vol->cv_next;
205 				else
206 					pre_chkpnt_vol->cv_next =
207 					    new_chkpnt_vol->cv_next;
208 
209 				free(new_chkpnt_vol);
210 				return (0);
211 			}
212 			return (new_chkpnt_vol->cv_count);
213 		}
214 		if (new_chkpnt_vol != chkpnt_vols)
215 			pre_chkpnt_vol = pre_chkpnt_vol->cv_next;
216 	}
217 
218 	return (0);
219 }
220 
221 
222 
223 
224 /*
225  * ndmp_start_check_point
226  *
227  * This function will parse the path, vol_name, to get the real volume name.
228  * It will then check via ndmp_add_chk_pnt_vol to see if creating a check point
229  * for the volume is necessary. If it is, a checkpoint is created.
230  * This function should be called before the NDMP backup is started.
231  *
232  * Parameters:
233  *   vol_name (input) - name of the volume
234  *
235  * Returns:
236  *   0: on success
237  *   -1: otherwise
238  */
239 int
240 ndmp_start_check_point(char *vol_name, char *jname)
241 {
242 	int erc = 0;
243 	char vol[ZFS_MAXNAMELEN];
244 
245 	if (vol_name == 0 ||
246 	    get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
247 		return (0);
248 
249 	if (ndmp_add_chk_pnt_vol(vol) > 0) {
250 		/*
251 		 * If there is an old checkpoint left from the previous
252 		 * backup and the reference count of backup checkpoint of
253 		 * the volume is 1 after increasing it, it shows that the
254 		 * checkpoint on file system is a stale one and it must be
255 		 * removed before using it.
256 		 */
257 		if (ndmp_has_backup_chkpnt(vol, jname))
258 			(void) chkpnt_backup_successful(vol, jname);
259 		if ((erc = chkpnt_backup_prepare(vol, jname)) < 0)
260 			(void) ndmp_remove_chk_pnt_vol(vol);
261 	}
262 
263 	return (erc);
264 }
265 
266 /*
267  * ndmp_release_check_point
268  *
269  * This function will parse the path, vol_name, to get the real volume name.
270  * It will then check via ndmp_remove_chk_pnt_vol to see if removing a check
271  * point for the volume is necessary. If it is, a checkpoint is removed.
272  * This function should be called after NDMP backup is finished.
273  *
274  * Parameters:
275  *   vol_name (input) - name of the volume
276  *
277  * Returns:
278  *   0: on success
279  *   -1: otherwise
280  */
281 int
282 ndmp_release_check_point(char *vol_name, char *jname)
283 {
284 	int erc = 0;
285 	char vol[ZFS_MAXNAMELEN];
286 
287 	if (vol_name == 0 ||
288 	    get_zfsvolname(vol, sizeof (vol), vol_name))
289 		return (0);
290 
291 	if (ndmp_remove_chk_pnt_vol(vol) == 0)
292 		erc = chkpnt_backup_successful(vol, jname);
293 
294 	return (erc);
295 }
296