1/*-
2 * Copyright (c) 2010-2012 Semihalf.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <assert.h>
31#include <stdarg.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <string.h>
36#include <fcntl.h>
37#include <errno.h>
38#include <sys/ioctl.h>
39#include <sys/stat.h>
40#include <sys/param.h>
41#include <sys/stdint.h>
42#include <sys/ucred.h>
43#include <sys/disk.h>
44#include <sys/mount.h>
45
46#include <fs/nandfs/nandfs_fs.h>
47#include <libnandfs.h>
48
49#define	NANDFS_IS_VALID		0x1
50#define	NANDFS_IS_OPENED	0x2
51#define	NANDFS_IS_OPENED_DEV	0x4
52#define	NANDFS_IS_ERROR		0x8
53
54#define DEBUG
55#undef DEBUG
56#ifdef DEBUG
57#define NANDFS_DEBUG(fmt, args...) do { \
58    printf("libnandfs:" fmt "\n", ##args); } while (0)
59#else
60#define NANDFS_DEBUG(fmt, args...)
61#endif
62
63#define	NANDFS_ASSERT_VALID(fs)		assert((fs)->n_flags & NANDFS_IS_VALID)
64#define	NANDFS_ASSERT_VALID_DEV(fs)	\
65	assert(((fs)->n_flags & (NANDFS_IS_VALID | NANDFS_IS_OPENED_DEV)) == \
66	    (NANDFS_IS_VALID | NANDFS_IS_OPENED_DEV))
67
68int
69nandfs_iserror(struct nandfs *fs)
70{
71
72	NANDFS_ASSERT_VALID(fs);
73
74	return (fs->n_flags & NANDFS_IS_ERROR);
75}
76
77const char *
78nandfs_errmsg(struct nandfs *fs)
79{
80
81	NANDFS_ASSERT_VALID(fs);
82
83	assert(nandfs_iserror(fs));
84	assert(fs->n_errmsg);
85	return (fs->n_errmsg);
86}
87
88static void
89nandfs_seterr(struct nandfs *fs, const char *fmt, ...)
90{
91	va_list ap;
92
93	va_start(ap, fmt);
94	vsnprintf(fs->n_errmsg, sizeof(fs->n_errmsg), fmt, ap);
95	va_end(ap);
96	fs->n_flags |= NANDFS_IS_ERROR;
97}
98
99const char *
100nandfs_dev(struct nandfs *fs)
101{
102
103	NANDFS_ASSERT_VALID(fs);
104	return (fs->n_dev);
105}
106
107void
108nandfs_init(struct nandfs *fs, const char *dir)
109{
110
111	snprintf(fs->n_ioc, sizeof(fs->n_ioc), "%s/%s", dir, ".");
112	fs->n_iocfd = -1;
113	fs->n_flags = NANDFS_IS_VALID;
114}
115
116void
117nandfs_destroy(struct nandfs *fs)
118{
119
120	assert(fs->n_iocfd == -1);
121	fs->n_flags &=
122	    ~(NANDFS_IS_ERROR | NANDFS_IS_VALID);
123	assert(fs->n_flags == 0);
124}
125
126int
127nandfs_open(struct nandfs *fs)
128{
129	struct nandfs_fsinfo fsinfo;
130
131	fs->n_flags |= NANDFS_IS_OPENED;
132
133	fs->n_iocfd = open(fs->n_ioc, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP |
134	    S_IWGRP | S_IROTH | S_IWOTH);
135	if (fs->n_iocfd == -1) {
136		nandfs_seterr(fs, "couldn't open %s: %s", fs->n_ioc,
137		    strerror(errno));
138		return (-1);
139	}
140
141	if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_FSINFO, &fsinfo) == -1) {
142		nandfs_seterr(fs, "couldn't fetch fsinfo: %s",
143		    strerror(errno));
144		return (-1);
145	}
146
147	memcpy(&fs->n_fsdata, &fsinfo.fs_fsdata, sizeof(fs->n_fsdata));
148	memcpy(&fs->n_sb, &fsinfo.fs_super, sizeof(fs->n_sb));
149	snprintf(fs->n_dev, sizeof(fs->n_dev), "%s", fsinfo.fs_dev);
150
151	return (0);
152}
153
154void
155nandfs_close(struct nandfs *fs)
156{
157
158	NANDFS_ASSERT_VALID(fs);
159	assert(fs->n_flags & NANDFS_IS_OPENED);
160
161	close(fs->n_iocfd);
162	fs->n_iocfd = -1;
163	fs->n_flags &= ~NANDFS_IS_OPENED;
164}
165
166int
167nandfs_get_cpstat(struct nandfs *fs, struct nandfs_cpstat *cpstat)
168{
169
170	NANDFS_ASSERT_VALID(fs);
171
172	if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_CPSTAT, cpstat) == -1) {
173		nandfs_seterr(fs, "ioctl NANDFS_IOCTL_GET_CPSTAT: %s",
174		    strerror(errno));
175		return (-1);
176	}
177
178	return (0);
179}
180
181static ssize_t
182nandfs_get_cpinfo(struct nandfs *fs, uint64_t cno, int mode,
183    struct nandfs_cpinfo *cpinfo, size_t nci)
184{
185	struct nandfs_argv args;
186
187	NANDFS_ASSERT_VALID(fs);
188
189	args.nv_base = (u_long)cpinfo;
190	args.nv_nmembs = nci;
191	args.nv_index = cno;
192	args.nv_flags = mode;
193
194	if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_CPINFO, &args) == -1) {
195		nandfs_seterr(fs, "ioctl NANDFS_IOCTL_GET_CPINFO: %s",
196		    strerror(errno));
197		return (-1);
198	}
199
200	return (args.nv_nmembs);
201}
202
203ssize_t
204nandfs_get_cp(struct nandfs *fs, uint64_t cno, struct nandfs_cpinfo *cpinfo,
205    size_t nci)
206{
207
208	return (nandfs_get_cpinfo(fs, cno, NANDFS_CHECKPOINT, cpinfo, nci));
209}
210
211ssize_t
212nandfs_get_snap(struct nandfs *fs, uint64_t cno, struct nandfs_cpinfo *cpinfo,
213    size_t nci)
214{
215
216	return (nandfs_get_cpinfo(fs, cno, NANDFS_SNAPSHOT, cpinfo, nci));
217}
218
219int
220nandfs_make_snap(struct nandfs *fs, uint64_t *cno)
221{
222
223	NANDFS_ASSERT_VALID(fs);
224
225	if (ioctl(fs->n_iocfd, NANDFS_IOCTL_MAKE_SNAP, cno) == -1) {
226		nandfs_seterr(fs, "ioctl NANDFS_IOCTL_MAKE_SNAP: %s",
227		    strerror(errno));
228		return (-1);
229	}
230
231	return (0);
232}
233
234int
235nandfs_delete_snap(struct nandfs *fs, uint64_t cno)
236{
237
238	NANDFS_ASSERT_VALID(fs);
239
240	if (ioctl(fs->n_iocfd, NANDFS_IOCTL_DELETE_SNAP, &cno) == -1) {
241		nandfs_seterr(fs, "ioctl NANDFS_IOCTL_DELETE_SNAP: %s",
242		    strerror(errno));
243		return (-1);
244	}
245
246	return (0);
247}
248