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