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