1 /* $NetBSD: snapshot.c,v 1.4 2008/04/28 20:23:08 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Juergen Hannken-Illjes. 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 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/ioctl.h> 34 #include <sys/mount.h> 35 #include <sys/stat.h> 36 37 #include <dev/fssvar.h> 38 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include "snapshot.h" 47 48 /* 49 * Create a snapshot of the file system currently mounted on the first argument 50 * using the second argument as backing store and return an open file 51 * descriptor for the snapshot. If the second argument is NULL, use the first 52 * as backing store. If the third argument is not NULL, it gets the time the 53 * snapshot was created. If the fourth argument is not NULL, it gets the 54 * snapshot device path. 55 */ 56 int 57 snap_open(char *mountpoint, char *backup, time_t *snap_date, char **snap_dev) 58 { 59 int i, fd, israw, fsinternal, dounlink, flags; 60 char path[MAXPATHLEN], fss_dev[14]; 61 dev_t mountdev; 62 struct fss_set fss; 63 struct fss_get fsg; 64 struct stat sb; 65 struct statvfs fsb; 66 67 dounlink = 0; 68 fd = -1; 69 70 fss.fss_mount = mountpoint; 71 fss.fss_bstore = backup ? backup : fss.fss_mount; 72 fss.fss_csize = 0; 73 74 if (stat(fss.fss_mount, &sb) < 0) 75 goto fail; 76 mountdev = sb.st_dev; 77 78 /* 79 * Prepare the backing store. `backup' is either a raw device, 80 * a file or a directory. If it is a file, it must not exist. 81 */ 82 israw = 0; 83 if (stat(fss.fss_bstore, &sb) == 0) { 84 if (S_ISDIR(sb.st_mode)) { 85 snprintf(path, sizeof(path), 86 "%s/XXXXXXXXXX", fss.fss_bstore); 87 fd = mkstemp(path); 88 fss.fss_bstore = path; 89 dounlink = 1; 90 } else if (S_ISCHR(sb.st_mode)) { 91 fd = open(fss.fss_bstore, O_RDWR); 92 israw = 1; 93 } else 94 goto fail; 95 } else { 96 fd = open(fss.fss_bstore, O_CREAT|O_EXCL|O_WRONLY, 0600); 97 dounlink = 1; 98 } 99 if (fd < 0) 100 goto fail; 101 102 if (fstat(fd, &sb) < 0) 103 goto fail; 104 fsinternal = (!israw && sb.st_dev == mountdev); 105 106 /* 107 * If the backing store is a plain file and the snapshot 108 * is not file system internal, truncate to file system 109 * free space. 110 */ 111 if (!israw && !fsinternal) { 112 if (statvfs(fss.fss_bstore, &fsb) < 0) 113 goto fail; 114 if (ftruncate(fd, (off_t)fsb.f_frsize*fsb.f_bavail) < 0) 115 goto fail; 116 } 117 118 if (close(fd) < 0) 119 goto fail; 120 121 /* 122 * Create the snapshot on the first free snapshot device. 123 */ 124 for (i = 0; ; i++) { 125 snprintf(fss_dev, sizeof(fss_dev), "/dev/rfss%d", i); 126 if ((fd = open(fss_dev, O_RDWR, 0)) < 0) 127 goto fail; 128 129 if (ioctl(fd, FSSIOFGET, &flags) < 0) 130 goto fail; 131 132 if (ioctl(fd, FSSIOCSET, &fss) < 0) { 133 if (errno != EBUSY) 134 goto fail; 135 close(fd); 136 fd = -1; 137 continue; 138 } 139 140 if (snap_dev != NULL) { 141 *snap_dev = strdup(fss_dev); 142 if (*snap_dev == NULL) { 143 ioctl(fd, FSSIOCCLR); 144 goto fail; 145 } 146 } 147 148 flags |= FSS_UNCONFIG_ON_CLOSE; 149 if (ioctl(fd, FSSIOCGET, &fsg) < 0 || 150 ioctl(fd, FSSIOFSET, &flags) < 0 || 151 (!israw && unlink(fss.fss_bstore) < 0)) { 152 ioctl(fd, FSSIOCCLR); 153 goto fail; 154 } 155 156 if (snap_date != NULL) 157 *snap_date = fsg.fsg_time.tv_sec; 158 return fd; 159 } 160 161 fail: 162 if (dounlink) 163 unlink(fss.fss_bstore); 164 if (fd >= 0) 165 close(fd); 166 167 return -1; 168 } 169