1/* $NetBSD: snapshot.c,v 1.5 2010/04/11 08:23:51 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 * 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 */ 56int 57snap_open(char *file, char *backup, time_t *snap_date, char **snap_dev) 58{ 59 int i, n, fd, israw, fsinternal, dounlink; 60 char path[MAXPATHLEN], fss_dev[14], *cp; 61 dev_t mountdev; 62 struct fss_set fss; 63 struct fss_get fsg; 64 struct stat sb; 65 struct statvfs *mntbuf, *fs, fsb; 66 67 dounlink = 0; 68 fd = -1; 69 mntbuf = NULL; 70 71 /* 72 * Lookup the mount point. `file' is either a directory or a raw 73 * character device. 74 */ 75 if (lstat(file, &sb) < 0) 76 goto fail; 77 fss.fss_mount = NULL; 78 if (S_ISCHR(sb.st_mode)) { 79 if ((cp = strrchr(file, '/')) == NULL || cp[1] != 'r') { 80 errno = EINVAL; 81 goto fail; 82 } 83 snprintf(path, sizeof(path), "%.*s/%s", 84 (int)(cp - file), file, cp + 2); 85 n = getmntinfo(&mntbuf, MNT_NOWAIT); 86 for (fs = mntbuf, i = 0; i < n; i++, fs++) { 87 if (strcmp(fs->f_mntfromname, path) == 0) { 88 fss.fss_mount = fs->f_mntonname; 89 if (stat(fss.fss_mount, &sb) < 0) 90 goto fail; 91 break; 92 } 93 } 94 } else if (S_ISDIR(sb.st_mode)) 95 fss.fss_mount = file; 96 if (fss.fss_mount == NULL) { 97 errno = EINVAL; 98 goto fail; 99 } 100 fss.fss_bstore = backup ? backup : fss.fss_mount; 101 fss.fss_csize = 0; 102 mountdev = sb.st_dev; 103 104 /* 105 * Prepare the backing store. `backup' is either a raw device, 106 * a file or a directory. If it is a file, it must not exist. 107 */ 108 israw = 0; 109 if (stat(fss.fss_bstore, &sb) == 0) { 110 if (S_ISDIR(sb.st_mode)) { 111 snprintf(path, sizeof(path), 112 "%s/XXXXXXXXXX", fss.fss_bstore); 113 fd = mkstemp(path); 114 fss.fss_bstore = path; 115 dounlink = 1; 116 } else if (S_ISCHR(sb.st_mode)) { 117 fd = open(fss.fss_bstore, O_RDWR); 118 israw = 1; 119 } else 120 goto fail; 121 } else { 122 fd = open(fss.fss_bstore, O_CREAT|O_EXCL|O_WRONLY, 0600); 123 dounlink = 1; 124 } 125 if (fd < 0) 126 goto fail; 127 128 if (fstat(fd, &sb) < 0) 129 goto fail; 130 fsinternal = (!israw && sb.st_dev == mountdev); 131 132 /* 133 * If the backing store is a plain file and the snapshot 134 * is not file system internal, truncate to file system 135 * free space. 136 */ 137 if (!israw && !fsinternal) { 138 if (statvfs(fss.fss_bstore, &fsb) < 0) 139 goto fail; 140 if (ftruncate(fd, (off_t)fsb.f_frsize*fsb.f_bavail) < 0) 141 goto fail; 142 } 143 144 if (close(fd) < 0) 145 goto fail; 146 147 fss.fss_flags = FSS_UNCONFIG_ON_CLOSE; 148 if (dounlink) 149 fss.fss_flags |= FSS_UNLINK_ON_CREATE; 150 /* 151 * Create the snapshot on the first free snapshot device. 152 */ 153 for (i = 0; ; i++) { 154 snprintf(fss_dev, sizeof(fss_dev), "/dev/rfss%d", i); 155 if ((fd = open(fss_dev, O_RDWR, 0)) < 0) 156 goto fail; 157 158 if (ioctl(fd, FSSIOCSET, &fss) < 0) { 159 if (errno != EBUSY) 160 goto fail; 161 close(fd); 162 fd = -1; 163 continue; 164 } 165 dounlink = 0; 166 167 if (snap_dev != NULL) { 168 *snap_dev = strdup(fss_dev); 169 if (*snap_dev == NULL) { 170 ioctl(fd, FSSIOCCLR); 171 goto fail; 172 } 173 } 174 175 if (ioctl(fd, FSSIOCGET, &fsg) < 0) { 176 ioctl(fd, FSSIOCCLR); 177 goto fail; 178 } 179 180 if (mntbuf) 181 free(mntbuf); 182 if (snap_date != NULL) 183 *snap_date = fsg.fsg_time.tv_sec; 184 return fd; 185 } 186 187fail: 188 if (mntbuf) 189 free(mntbuf); 190 if (dounlink) 191 unlink(fss.fss_bstore); 192 if (fd >= 0) 193 close(fd); 194 195 return -1; 196} 197