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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <sys/systm.h>
30#include <sys/types.h>
31#include <sys/vnode.h>
32#include <sys/file.h>
33#include <sys/buf.h>
34#include <sys/ddi.h>
35#include <sys/errno.h>
36#include <sys/cmn_err.h>
37#include <sys/fs/ufs_inode.h>
38#include <sys/fs/ufs_filio.h>
39#include <sys/fs/ufs_snap.h>
40#include <sys/fssnap_if.h>
41#include <sys/sysmacros.h>
42#include <sys/modctl.h>
43#include <sys/fs/ufs_bio.h>
44#include <sys/debug.h>
45#include <sys/kmem.h>
46#include <sys/inttypes.h>
47#include <sys/vfs.h>
48#include <sys/disp.h>
49#include <sys/atomic.h>
50#include <sys/conf.h>
51#include <sys/param.h>
52#include <sys/policy.h>
53
54static int ufs_snap_init_backfile(int  *, int, vnode_t ***, struct ufsvfs *);
55static void release_backing_vnodes(vnode_t ***, int);
56static int ufs_snap_find_candidates(void *, struct ufsvfs *, int);
57
58/*
59 * Create a snapshot on a file system
60 */
61int
62ufs_snap_create(struct vnode *vp, struct fiosnapcreate_multi *fiosnapp,
63    cred_t *cr)
64{
65	int		error = 0;
66	struct ufsvfs	*ufsvfsp = VTOI(vp)->i_ufsvfs;
67	struct fs	*fs = ufsvfsp->vfs_fs;
68	vnode_t		**bfvpp = NULL;
69	struct lockfs	lf;
70	void		*snapid = NULL;
71
72	u_offset_t	nchunks;
73	uint_t		chunksize, fragsperchunk;
74
75	/*
76	 * Only privilege processes can create a snapshot for now.  This
77	 * would be better if it was based on the permissions of the device
78	 * file.
79	 */
80	if (secpolicy_fs_config(cr, ufsvfsp->vfs_vfs) != 0)
81		return (EPERM);
82
83	/*
84	 * There is no reason to make a snapshot of a read-only file system
85	 */
86	if (fs->fs_ronly) {
87		fiosnapp->error = FIOCOW_EREADONLY;
88		return (EROFS);
89	}
90
91	/*
92	 * Initialize the backing files to store old data.  This assumes any
93	 * preallocation and setup has been done already.
94	 * ufs_snap_init_backfile() allocates and returns a pointer to
95	 * a null-terminated array of vnodes in bfvpp.
96	 */
97	error = ufs_snap_init_backfile(fiosnapp->backfiledesc,
98	    fiosnapp->backfilecount, &bfvpp, ufsvfsp);
99	if (error) {
100		fiosnapp->error = FIOCOW_EBACKFILE;
101		return (error);
102	}
103
104	/*
105	 * File system must be write locked to prevent updates while
106	 * the snapshot is being established.
107	 */
108	if ((error = ufs_fiolfss(vp, &lf)) != 0) {
109		release_backing_vnodes(&bfvpp, fiosnapp->backfilecount);
110		return (error);
111	}
112
113	if (!LOCKFS_IS_ULOCK(&lf)) {
114		release_backing_vnodes(&bfvpp, fiosnapp->backfilecount);
115		fiosnapp->error = FIOCOW_EULOCK;
116		return (EINVAL);
117	}
118
119	lf.lf_lock = LOCKFS_WLOCK;
120	lf.lf_flags = 0;
121	lf.lf_comment = NULL;
122	if ((error = ufs_fiolfs(vp, &lf, 1)) != 0) {
123		release_backing_vnodes(&bfvpp, fiosnapp->backfilecount);
124		fiosnapp->error = FIOCOW_EWLOCK;
125		return (EINVAL);
126	}
127
128	/*
129	 * File system must be fairly consistent to enable snapshots
130	 */
131	if (fs->fs_clean != FSACTIVE &&
132	    fs->fs_clean != FSSTABLE &&
133	    fs->fs_clean != FSCLEAN &&
134	    fs->fs_clean != FSLOG) {
135		fiosnapp->error = FIOCOW_ECLEAN;
136		error = EINVAL;
137		goto unlockout;
138	}
139
140	/*
141	 * Only one snapshot is allowed per file system, so error if
142	 * a snapshot is already enabled.
143	 */
144	if (ufsvfsp->vfs_snapshot) {
145		fiosnapp->error = FIOCOW_EBUSY;
146		error = EBUSY;
147		goto unlockout;
148	}
149
150	/* Tell bio.c how to call our strategy routine.  XXX ugly hack */
151	if (bio_snapshot_strategy == NULL)
152		bio_snapshot_strategy =
153		    (void (*) (void *, buf_t *))fssnap_strategy;
154
155	/*
156	 * use chunk size that is passed in, or the file system
157	 * block size if it is zero.  For most cases, the file system
158	 * block size will be reasonably efficient.  A larger
159	 * chunksize uses less memory but may potentially induce more
160	 * I/O copying the larger chunks aside.
161	 */
162	if (fiosnapp->chunksize != 0)
163		chunksize = fiosnapp->chunksize;
164	else
165		chunksize = fs->fs_bsize * 4;
166
167
168	/*
169	 * compute the number of chunks in this whole file system.  Since
170	 * the UFS allocation bitmaps are in units of fragments, we first
171	 * compute the number of fragments per chunk.  Things work out
172	 * nicer if the chunk size is a power-of-two multiple of the
173	 * fragment size.
174	 */
175	if ((chunksize < fs->fs_fsize) || (chunksize % fs->fs_fsize != 0)) {
176		fiosnapp->error = FIOCOW_ECHUNKSZ;
177		error = EINVAL;
178		goto unlockout;
179	}
180	fragsperchunk = chunksize >> fs->fs_fshift;
181	nchunks = (fs->fs_size + fragsperchunk) / fragsperchunk;
182
183	/*
184	 * Create and initialize snapshot state and allocate/initialize
185	 * translation table.  This does the real work of taking the snapshot.
186	 */
187	snapid = fssnap_create(nchunks, chunksize, fiosnapp->maxsize, vp,
188	    fiosnapp->backfilecount, bfvpp, fiosnapp->backfilename,
189	    fiosnapp->backfilesize);
190	if (snapid == NULL) {
191		fiosnapp->error = FIOCOW_ECREATE;
192		error = EINVAL;
193		goto unlockout;
194	}
195
196	error = ufs_snap_find_candidates(snapid, ufsvfsp, chunksize);
197	fiosnapp->snapshotnumber = fssnap_create_done(snapid);
198
199	if (error) {
200		cmn_err(CE_WARN, "ufs_snap_create: failed scanning bitmaps, "
201		    "error = %d.", error);
202		fiosnapp->error = FIOCOW_EBITMAP;
203		goto unlockout;
204	}
205
206	ufsvfsp->vfs_snapshot = snapid;
207
208unlockout:
209	/*
210	 * Unlock the file system
211	 */
212	lf.lf_lock = LOCKFS_ULOCK;
213	lf.lf_flags = 0;
214	if ((ufs_fiolfs(vp, &lf, 1) != 0) && !error) {
215		fiosnapp->error = FIOCOW_ENOULOCK;
216		error = EINVAL;
217	} else {
218		fiosnapp->error = 0;
219	}
220
221	/* clean up the snapshot if an error occurred. */
222	if (error && snapid != NULL)
223		(void) fssnap_delete(&snapid);
224	else if (error && bfvpp != NULL)
225		release_backing_vnodes(&bfvpp, fiosnapp->backfilecount);
226
227	return (error);
228}
229
230static int
231ufs_snap_init_backfile(int *filedesc, int count, vnode_t ***vppp,
232    struct ufsvfs *ufsvfsp)
233{
234	file_t *fp;
235	vnode_t **vpp;
236	int i;
237
238	vpp = (vnode_t **)kmem_zalloc((count  + 1) * sizeof (vnode_t *),
239	    KM_SLEEP);
240	*vppp = vpp;
241	for (i = 0; i < count; i++) {
242		if ((fp = getf(*filedesc)) == NULL) {
243			release_backing_vnodes(vppp, count);
244			*vppp = NULL;
245			return (EBADF);
246		}
247
248		ASSERT(fp->f_vnode != NULL);
249		VN_HOLD(fp->f_vnode);
250
251		*vpp = fp->f_vnode;
252		releasef(*filedesc);
253		filedesc++;
254
255		/* make sure the backing file is on a different file system */
256		if ((*vpp)->v_vfsp == ufsvfsp->vfs_vfs) {
257			release_backing_vnodes(vppp, count);
258			*vppp = NULL;
259			return (EINVAL);
260		}
261		vpp++;
262	}
263	return (0);
264}
265
266static void
267release_backing_vnodes(vnode_t ***bvppp, int count)
268{
269	vnode_t **vpp;
270
271	vpp = *bvppp;
272	while (*vpp) {
273		VN_RELE(*vpp);
274		*vpp++ = NULL;
275	}
276	kmem_free(*bvppp, (count + 1) * sizeof (vnode_t *));
277	*bvppp = NULL;
278}
279
280static int
281ufs_snap_find_candidates(void *snapid, struct ufsvfs *ufsvfsp, int chunksize)
282{
283	struct fs	*fs = ufsvfsp->vfs_fs;
284	struct buf	*cgbp;	/* cylinder group buffer */
285	struct cg	*cgp;	/* cylinder group data */
286	ulong_t		cg;
287	ulong_t		cgbase;
288	ulong_t		chunk;
289	uchar_t		*blksfree;
290
291	ulong_t		curfrag;
292	int		error = 0;
293
294	/*
295	 * read through each ufs cylinder group and fetch the fragment
296	 * allocation bitmap.  UFS indicates a fragment is allocated by
297	 * a zero bit (not a one bit) in the fragment offset.
298	 */
299	cgbase = 0LL;
300	for (cg = 0; cg < fs->fs_ncg; cg++) {
301		/* read the cylinder group in */
302		cgbp = BREAD(ufsvfsp->vfs_dev,
303		    (daddr_t)fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize);
304		if ((error = geterror(cgbp)) != 0) {
305			brelse(cgbp);
306			goto errout;
307		}
308		cgp = cgbp->b_un.b_cg;
309
310		/* check the magic number */
311		if (cgp->cg_magic != CG_MAGIC) {
312			cmn_err(CE_WARN, "ufs_snap_find_candidates: cg %lu "
313			    "magic number (0x%x) does not match expected "
314			    "magic number (0x%x)", cg, cgp->cg_magic, CG_MAGIC);
315			error = EIO;
316			goto errout;
317		}
318
319		blksfree = cg_blksfree(cgp);
320
321		/*
322		 * go through the allocation bitmap and set the
323		 * corresponding bit in the candidate map.
324		 */
325		for (curfrag = 0; curfrag < cgp->cg_ndblk; curfrag++) {
326			if (isclr(blksfree, curfrag)) {
327				/*
328				 * this assumes chunksize is a multiple of
329				 * the fragment size
330				 */
331				chunk = (ulong_t)((cgbase + curfrag) /
332				    (chunksize >> fs->fs_fshift));
333
334				fssnap_set_candidate(snapid, chunk);
335				/*
336				 * no need to scan the rest of this chunk since
337				 * it is already marked, so skip to the next
338				 */
339				curfrag += ((chunksize >> fs->fs_fshift) -
340				    ((cgbase + curfrag) %
341				    (chunksize >> fs->fs_fshift))) - 1;
342			}
343		}
344
345		cgbase += cgp->cg_ndblk;
346		ASSERT(cgbase <= fs->fs_size);
347		brelse(cgbp);
348	} /* cylinder group loop */
349
350	ASSERT(cgbase == fs->fs_size);
351
352errout:
353	return (error);
354}
355
356
357int
358ufs_snap_delete(struct vnode *vp, struct fiosnapdelete *fiosnapp, cred_t *cr)
359{
360	struct ufsvfs	*ufsvfsp = VTOI(vp)->i_ufsvfs;
361	struct fs	*fs = ufsvfsp->vfs_fs;
362
363	/*
364	 * Initialize fields in the user's buffer
365	 */
366	fiosnapp->error = 0;
367
368	/*
369	 * No snapshot exists, we're done.
370	 */
371	if (ufsvfsp->vfs_snapshot == NULL)
372		return (ENOENT);
373
374	/*
375	 * must have sufficient privileges.
376	 */
377	if (secpolicy_fs_config(cr, ufsvfsp->vfs_vfs) != 0)
378		return (EPERM);
379
380	/*
381	 * Readonly file system
382	 */
383	if (fs->fs_ronly) {
384		fiosnapp->error = FIOCOW_EREADONLY;
385		return (EROFS);
386	}
387
388	/* free the data structures and clear the vfs_snapshot field. */
389	fiosnapp->snapshotnumber = fssnap_delete(&ufsvfsp->vfs_snapshot);
390
391	if (fiosnapp->snapshotnumber == -1)
392		return (EINVAL);
393
394	return (0);
395}
396