1 /* $NetBSD: snapshot.c,v 1.3 2006/10/26 20:02:30 hannken 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE 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 <sys/param.h> 40 #include <sys/ioctl.h> 41 #include <sys/mount.h> 42 #include <sys/stat.h> 43 44 #include <dev/fssvar.h> 45 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 53 #include "snapshot.h" 54 55 /* 56 * Create a snapshot of the file system currently mounted on the first argument 57 * using the second argument as backing store and return an open file 58 * descriptor for the snapshot. If the second argument is NULL, use the first 59 * as backing store. If the third argument is not NULL, it gets the time the 60 * snapshot was created. If the fourth argument is not NULL, it gets the 61 * snapshot device path. 62 */ 63 int 64 snap_open(char *mountpoint, char *backup, time_t *snap_date, char **snap_dev) 65 { 66 int i, fd, israw, fsinternal, dounlink, flags; 67 char path[MAXPATHLEN], fss_dev[14]; 68 dev_t mountdev; 69 struct fss_set fss; 70 struct fss_get fsg; 71 struct stat sb; 72 struct statvfs fsb; 73 74 dounlink = 0; 75 fd = -1; 76 77 fss.fss_mount = mountpoint; 78 fss.fss_bstore = backup ? backup : fss.fss_mount; 79 fss.fss_csize = 0; 80 81 if (stat(fss.fss_mount, &sb) < 0) 82 goto fail; 83 mountdev = sb.st_dev; 84 85 /* 86 * Prepare the backing store. `backup' is either a raw device, 87 * a file or a directory. If it is a file, it must not exist. 88 */ 89 israw = 0; 90 if (stat(fss.fss_bstore, &sb) == 0) { 91 if (S_ISDIR(sb.st_mode)) { 92 snprintf(path, sizeof(path), 93 "%s/XXXXXXXXXX", fss.fss_bstore); 94 fd = mkstemp(path); 95 fss.fss_bstore = path; 96 dounlink = 1; 97 } else if (S_ISCHR(sb.st_mode)) { 98 fd = open(fss.fss_bstore, O_RDWR); 99 israw = 1; 100 } else 101 goto fail; 102 } else { 103 fd = open(fss.fss_bstore, O_CREAT|O_EXCL|O_WRONLY, 0600); 104 dounlink = 1; 105 } 106 if (fd < 0) 107 goto fail; 108 109 if (fstat(fd, &sb) < 0) 110 goto fail; 111 fsinternal = (!israw && sb.st_dev == mountdev); 112 113 /* 114 * If the backing store is a plain file and the snapshot 115 * is not file system internal, truncate to file system 116 * free space. 117 */ 118 if (!israw && !fsinternal) { 119 if (statvfs(fss.fss_bstore, &fsb) < 0) 120 goto fail; 121 if (ftruncate(fd, (off_t)fsb.f_frsize*fsb.f_bavail) < 0) 122 goto fail; 123 } 124 125 if (close(fd) < 0) 126 goto fail; 127 128 /* 129 * Create the snapshot on the first free snapshot device. 130 */ 131 for (i = 0; ; i++) { 132 snprintf(fss_dev, sizeof(fss_dev), "/dev/rfss%d", i); 133 if ((fd = open(fss_dev, O_RDWR, 0)) < 0) 134 goto fail; 135 136 if (ioctl(fd, FSSIOFGET, &flags) < 0) 137 goto fail; 138 139 if (ioctl(fd, FSSIOCSET, &fss) < 0) { 140 if (errno != EBUSY) 141 goto fail; 142 close(fd); 143 fd = -1; 144 continue; 145 } 146 147 if (snap_dev != NULL) { 148 *snap_dev = strdup(fss_dev); 149 if (*snap_dev == NULL) { 150 ioctl(fd, FSSIOCCLR); 151 goto fail; 152 } 153 } 154 155 flags |= FSS_UNCONFIG_ON_CLOSE; 156 if (ioctl(fd, FSSIOCGET, &fsg) < 0 || 157 ioctl(fd, FSSIOFSET, &flags) < 0 || 158 (!israw && unlink(fss.fss_bstore) < 0)) { 159 ioctl(fd, FSSIOCCLR); 160 goto fail; 161 } 162 163 if (snap_date != NULL) 164 *snap_date = fsg.fsg_time.tv_sec; 165 return fd; 166 } 167 168 fail: 169 if (dounlink) 170 unlink(fss.fss_bstore); 171 if (fd >= 0) 172 close(fd); 173 174 return -1; 175 } 176