Home | History | Annotate | Download | only in ufs
      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 
     54 static int ufs_snap_init_backfile(int  *, int, vnode_t ***, struct ufsvfs *);
     55 static void release_backing_vnodes(vnode_t ***, int);
     56 static int ufs_snap_find_candidates(void *, struct ufsvfs *, int);
     57 
     58 /*
     59  * Create a snapshot on a file system
     60  */
     61 int
     62 ufs_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 
    208 unlockout:
    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 
    230 static int
    231 ufs_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 
    266 static void
    267 release_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 
    280 static int
    281 ufs_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 
    352 errout:
    353 	return (error);
    354 }
    355 
    356 
    357 int
    358 ufs_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