1/*	$NetBSD$	*/
2
3/*-
4 * Copyright (c) 2003 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 <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <stdlib.h>
41#include <string.h>
42#include <time.h>
43#include <unistd.h>
44#include <util.h>
45
46#include <dev/fssvar.h>
47
48static int	vflag = 0;
49static int	xflag = 0;
50
51static void	config(int, char **);
52static void	unconfig(int, char **);
53static void	list(int, char **);
54__dead static void	usage(void);
55
56int
57main(int argc, char **argv)
58{
59	int ch;
60	void (*action)(int, char **);
61
62	action = config;
63
64	while ((ch = getopt(argc, argv, "cluvx")) != -1) {
65		switch (ch) {
66		case 'c':
67			action = config;
68			break;
69		case 'l':
70			action = list;
71			break;
72		case 'u':
73			action = unconfig;
74			break;
75		case 'v':
76			vflag++;
77			break;
78		case 'x':
79			xflag++;
80			break;
81		default:
82		case '?':
83			usage();
84			/* NOTREACHED */
85		}
86	}
87
88	argc -= optind;
89	argv += optind;
90
91	(*action)(argc, argv);
92
93	exit(0);
94}
95
96static void
97config(int argc, char **argv)
98{
99	int fd, isreg, istmp, ispersistent;
100	char full[64], path[MAXPATHLEN];
101	off_t bssize;
102	dev_t mountdev;
103	struct stat sbuf;
104	struct statvfs fsbuf;
105	struct fss_set fss;
106
107	if (argc < 3)
108		usage();
109
110	istmp = ispersistent = 0;
111
112	fss.fss_mount = argv[1];
113	fss.fss_bstore = argv[2];
114
115	if (statvfs(argv[1], &fsbuf) != 0 || stat(argv[1], &sbuf) != 0)
116		err(1, "stat %s", argv[1]);
117	mountdev = sbuf.st_dev;
118	if (stat(argv[2], &sbuf) == 0 &&
119	    S_ISREG(sbuf.st_mode) &&
120	    sbuf.st_dev == mountdev) {
121		if ((sbuf.st_flags & SF_SNAPSHOT) == 0)
122			errx(1, "%s: exists and is not a snapshot", argv[2]);
123		if (argc != 3)
124			usage();
125		isreg = ispersistent = 1;
126
127		goto configure;
128	}
129
130	if (argc > 5)
131		usage();
132
133	if (argc > 3)
134		fss.fss_csize = strsuftoll("cluster size", argv[3], 0, INT_MAX);
135	else
136		fss.fss_csize = 0;
137	if (argc > 4)
138		bssize = strsuftoll("bs size", argv[4], 0, LLONG_MAX);
139	else
140		bssize = (off_t)fsbuf.f_blocks*fsbuf.f_frsize;
141
142	/*
143	 * Create the backing store. If it is a directory, create a temporary
144	 * file and set the unlink flag.
145	 */
146	if ((fd = open(fss.fss_bstore, O_CREAT|O_TRUNC|O_WRONLY, 0600)) < 0) {
147		if (errno != EISDIR)
148			err(1, "create: %s", fss.fss_bstore);
149		snprintf(path, sizeof(path), "%s/XXXXXXXXXX", fss.fss_bstore);
150		if ((fd = mkstemp(path)) < 0)
151			err(1, "mkstemp: %s", path);
152		fss.fss_bstore = path;
153		istmp = 1;
154	}
155	if (fstat(fd, &sbuf) < 0)
156		err(1, "stat: %s", fss.fss_bstore);
157	if (!ispersistent && sbuf.st_dev == mountdev)
158		ispersistent = 1;
159	isreg = S_ISREG(sbuf.st_mode);
160	if (!ispersistent && isreg && ftruncate(fd, bssize) < 0)
161		err(1, "truncate %s", fss.fss_bstore);
162	close(fd);
163
164configure:
165	if ((fd = opendisk(argv[0], O_RDWR, full, sizeof(full), 0)) < 0) {
166		if (istmp)
167			unlink(fss.fss_bstore);
168		err(1, "open: %s", argv[0]);
169	}
170
171	fss.fss_flags = 0;
172	if ((xflag || istmp) && isreg)
173		fss.fss_flags |= FSS_UNLINK_ON_CREATE;
174
175	if (ioctl(fd, FSSIOCSET, &fss) < 0) {
176		if (istmp)
177			unlink(fss.fss_bstore);
178		err(1, "%s: FSSIOCSET", full);
179	}
180
181	if (vflag)
182		list(1, argv);
183}
184
185static void
186unconfig(int argc, char **argv)
187{
188	int fd;
189	char full[64];
190
191	if (argc != 1)
192		usage();
193
194	if (vflag)
195		list(1, argv);
196
197	if ((fd = opendisk(argv[0], O_RDWR, full, sizeof(full), 0)) < 0)
198		err(1, "open: %s", argv[0]);
199
200	if (ioctl(fd, FSSIOCCLR) < 0)
201		err(1, "%s: FSSIOCCLR", full);
202}
203
204static void
205list(int argc, char **argv)
206{
207	int n, fd, flags;
208	char *dev, path[64], full[64];
209	char clbuf[5], bsbuf[5], tmbuf[64];
210	time_t t;
211	struct fss_get fsg;
212
213	if (argc > 1)
214		usage();
215
216	if (argc > 0)
217		dev = argv[0];
218	else
219		dev = path;
220
221	for (n = 0; ; n++) {
222		if (argc == 0)
223			snprintf(path, sizeof(path), "fss%d", n);
224		if ((fd = opendisk(dev, O_RDONLY, full, sizeof(full), 0)) < 0) {
225			if (argc == 0 && (errno == ENOENT || errno == ENXIO))
226				break;
227			err(1, "open: %s", dev);
228		}
229
230		if (ioctl(fd, FSSIOFGET, &flags) < 0)
231			flags = 0;
232
233		if (ioctl(fd, FSSIOCGET, &fsg) < 0) {
234			if (errno == ENXIO)
235				printf("%s: not in use\n", dev);
236			else
237				err(1, "%s: FSSIOCGET", full);
238		} else if (vflag) {
239			humanize_number(clbuf, sizeof(clbuf),
240			    (int64_t)fsg.fsg_csize,
241			    "", HN_AUTOSCALE, HN_B|HN_NOSPACE);
242
243			humanize_number(bsbuf, sizeof(bsbuf),
244			    (int64_t)fsg.fsg_bs_size*fsg.fsg_csize,
245			    "", HN_AUTOSCALE, HN_B|HN_NOSPACE);
246
247			t = fsg.fsg_time.tv_sec;
248			strftime(tmbuf, sizeof(tmbuf), "%F %T", localtime(&t));
249
250			printf("%s: %s, taken %s", dev, fsg.fsg_mount, tmbuf);
251			if ((flags & FSS_UNCONFIG_ON_CLOSE) != 0)
252				printf(", unconfig on close");
253			if (fsg.fsg_csize == 0)
254				printf(", file system internal\n");
255			else
256				printf(", %"PRId64" cluster of %s, %s backup\n",
257				    fsg.fsg_mount_size, clbuf, bsbuf);
258		} else
259			printf("%s: %s\n", dev, fsg.fsg_mount);
260
261		close(fd);
262
263		if (argc > 0)
264			break;
265	}
266}
267
268static void
269usage(void)
270{
271	fprintf(stderr, "%s",
272	    "usage: fssconfig [-cxv] device path backup [cluster [size]]\n"
273	    "       fssconfig -u [-v] device\n"
274	    "       fssconfig -l [-v] [device]\n");
275	exit(1);
276}
277