1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2013 Damian Bogel. All rights reserved.
14 */
15
16 #include <fcntl.h>
17 #include <libfsd.h>
18 #include <string.h>
19 #include <stropts.h>
20 #include <sys/debug.h>
21 #include <sys/errno.h>
22 #include <sys/fsd.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27
28 /*
29 * libfsd
30 * Library used to drive the fsd pseudo-device driver.
31 *
32 * 1. Usage.
33 * fsd_handle_t is used for every operation on the fsd driver. It is acquired by
34 * a call to fsd_open(). Every handle must be released by calling fsd_close().
35 *
36 * A handle is a structure that contains data needed to drive the fsd and get
37 * information about errors.
38 *
39 *
40 * Basics:
41 * A disturber is a hook for filesystem operations. There are different types of
42 * disturbers, but the property that connects them is that EVERY program that
43 * uses filesystem API should expect that kind of behaviour. Doing otherwise is
44 * a bug and could lead to serious problems.
45 * Omnipresent disturber is a hook that is being installed whenever a new vfs_t
46 * is mounted.
47 *
48 *
49 * Errors:
50 * In almost all the functions (except fsd_close()) return value equal to -1
51 * indicates that there was an error. Last error data is contained in the
52 * handle in two fields: fsd_errno and errno.
53 * fsd_errno could be one of EFSD_XXX. errno is nonzero if and only if fsd_errno
54 * is set to EFSD_CANT_OPEN_DRIVER or EFSD_CANT_OPEN_MOUNTPOINT.
55 * fsd_strerr() is used to get the error message from fsd_errno. errno should be
56 * treated according to Intro(2).
57 *
58 * Handle management:
59 * fsd_open()
60 * Returns a handle.
61 *
62 * fsd_close(handle)
63 * Destroys a handle.
64 *
65 *
66 * Enabling/disabling fsd:
67 * fsd_enable(handle)
68 * fsd_disable(handle)
69 * When fsd is enabled, it cannot be detached from the system. Otherwise,
70 * it could be and all the information that the fsd contains could be lost.
71 * The client should keep the fsd enabled whenever he needs to. No
72 * operations on fsd could be made if it's not enabled.
73 *
74 *
75 * Getting information:
76 * fsd_get_info(handle, info)
77 * Used to retrieve information such as:
78 * * whether fsd is enabled (system-wide)
79 * * whether there is an omnipresent disturber installed
80 * * omnipresent disturber's parameters
81 * * count of disturbers installed
82 *
83 * fsd_get_list(handle, fslist, count)
84 * Gets a list of disturbers installed. count is the maximum count of
85 * entries that could be returned by the function to the user-allocated
86 * buffer. count is changed to min(count, number of disturbers installed).
87 * There is no error returned if the number of disturbers installed exceeds
88 * the user-provided count. It just gets the maximal allowed amount of
89 * informaton.
90 *
91 * fsd_get_param(handle, path, param)
92 * Gets disturber parameters from a filesystem of the file pointed by the
93 * path.
94 *
95 *
96 * Installing/removing disturbers:
97 * fsd_disturb(handle, path, param)
98 * Installs (or changes, if a disturber on this filesystem already exists)
99 * a disturber on a filesystem of the file pointed by the path.
100 *
101 * fsd_disturb_omni(handle, param)
102 * Installs (or changes, if an omnipresent disturber already exists) an
103 * omnipresent disturber. Only one omnipresent disturber exists at a time.
104 *
105 * fsd_disturb_off(handle, path)
106 * fsd_disturb_omni_off(handle)
107 * Removes a disturber. It is guaranteed that after this function returns,
108 * the disturber won't change the behaviour of filesystem operations.
109 *
110 * 2. Multithreading.
111 * It is safe to use the libfsd API concurrently.
112 *
113 * Error information is encapsulated in a handle, so it's the client's job to
114 * properly share the handle between threads to preserve the consistency of
115 * error data within a handle.
116 */
117
118 extern int errno;
119
120 const char *
121 fsd_strerr(int e)
122 {
123 switch (e) {
124 case EFSD_NOERROR:
125 return ("no error");
126 case EFSD_BAD_PARAM:
127 return ("incorrect disturbance parameters");
128 case EFSD_INTERNAL:
129 return ("internal library error");
130 case EFSD_NOT_ENABLED:
131 return ("fsd is not enabled");
132 case EFSD_CANT_OPEN_DRIVER:
133 return ("cannot open fsd device");
134 case EFSD_CANT_OPEN_MOUNTPOINT:
135 return ("cannot open mountpoint");
136 case EFSD_ENTRY_NOT_FOUND:
137 return ("this filesystem is not being disturbed");
138 case EFSD_FAULT:
139 return ("bad pointer");
140 case EFSD_TOO_MANY_HOOKS:
141 return ("too many hooks");
142 case EFSD_UNKNOWN_ERROR:
143 default:
144 return ("unknown error");
145 }
146 }
147
148 static int
149 xlate_errno(int e)
150 {
151 switch (e) {
152 case 0:
153 return (0);
154 case (-1):
155 switch (errno) {
156 case 0:
157 return (EFSD_NOERROR);
158 case EFAULT:
159 return (EFSD_FAULT);
160 case ENOTTY:
161 return (EFSD_INTERNAL);
162 default:
163 return (EFSD_UNKNOWN_ERROR);
164 }
165 case ENOTACTIVE:
166 return (EFSD_NOT_ENABLED);
167 case ENOENT:
168 return (EFSD_ENTRY_NOT_FOUND);
169 case EINVAL:
170 return (EFSD_BAD_PARAM);
171 case EBADFD:
172 return (EFSD_INTERNAL);
173 case EAGAIN:
174 return (EFSD_TOO_MANY_HOOKS);
175 default:
176 return (EFSD_UNKNOWN_ERROR);
177 }
178 }
179
180 static int
181 ioctl_set_handle(fsd_handle_t *handle, int ioctlret)
182 {
183 handle->fsd_errno = xlate_errno(ioctlret);
184 handle->errno = 0;
185
186 if (handle->fsd_errno == 0)
187 return (0);
188 else
189 return (-1);
190 }
191
192 int
193 fsd_open(fsd_handle_t *handle)
194 {
195 if ((handle->fd = open(FSD_DEV_PATH, O_RDWR)) == -1) {
196 handle->fsd_errno = EFSD_CANT_OPEN_DRIVER;
197 handle->errno = errno;
198 return (-1);
199 }
200 return (0);
201 }
202
203 void
204 fsd_close(fsd_handle_t *handle)
205 {
206 (void) close(handle->fd);
207 handle->fd = -1;
208 }
209
210
211 int
212 fsd_enable(fsd_handle_t *handle)
213 {
214 return (ioctl_set_handle(handle, ioctl(handle->fd, FSD_ENABLE)));
215
216 }
217
218 int
219 fsd_disable(fsd_handle_t *handle)
220 {
221 return (ioctl_set_handle(handle, ioctl(handle->fd, FSD_DISABLE)));
222 }
223
224 int
225 fsd_disturb(fsd_handle_t *handle, const char *mnt_path, fsd_t *param)
226 {
227 fsd_ioc_t ioc;
228 int error;
229
230 (void) memcpy(&ioc.fsdioc_dis.fsdd_param, param,
231 sizeof (ioc.fsdioc_dis.fsdd_param));
232
233 if ((ioc.fsdioc_dis.fsdd_mnt = open(mnt_path, O_RDONLY)) == -1) {
234 handle->fsd_errno = EFSD_CANT_OPEN_MOUNTPOINT;
235 handle->errno = errno;
236 return (-1);
237 }
238
239 error = ioctl(handle->fd, FSD_DISTURB, &ioc);
240 (void) close(ioc.fsdioc_dis.fsdd_mnt);
241 return (ioctl_set_handle(handle, error));
242 }
243
244 int
245 fsd_disturb_off(fsd_handle_t *handle, const char *mnt_path)
246 {
247 fsd_ioc_t ioc;
248 int error;
249
250 if ((ioc.fsdioc_mnt = open(mnt_path, O_RDONLY)) == -1) {
251 handle->fsd_errno = EFSD_CANT_OPEN_MOUNTPOINT;
252 handle->errno = errno;
253 return (-1);
254 }
255
256 error = ioctl(handle->fd, FSD_DISTURB_OFF, &ioc);
257 (void) close(ioc.fsdioc_mnt);
258 return (ioctl_set_handle(handle, error));
259 }
260
261 int
262 fsd_disturb_omni(fsd_handle_t *handle, fsd_t *param)
263 {
264 fsd_ioc_t ioc;
265
266 (void) memcpy(&ioc.fsdioc_param, param, sizeof (ioc.fsdioc_param));
267
268 return (ioctl_set_handle(handle, ioctl(handle->fd, FSD_DISTURB_OMNI,
269 &ioc)));
270 }
271
272 int
273 fsd_disturb_omni_off(fsd_handle_t *handle)
274 {
275 return (ioctl_set_handle(handle, ioctl(handle->fd,
276 FSD_DISTURB_OMNI_OFF)));
277 }
278
279 int
280 fsd_get_param(fsd_handle_t *handle, const char *mnt_path, fsd_t *param)
281 {
282 fsd_ioc_t ioc;
283 int error;
284 int mntfd;
285
286 if ((ioc.fsdioc_mnt = open(mnt_path, O_RDONLY)) == -1) {
287 handle->fsd_errno = EFSD_CANT_OPEN_MOUNTPOINT;
288 handle->errno = errno;
289 return (-1);
290 }
291 mntfd = ioc.fsdioc_mnt;
292
293 error = ioctl(handle->fd, FSD_GET_PARAM, &ioc);
294 if (error == 0)
295 (void) memcpy(param, &ioc.fsdioc_param, sizeof (*param));
296
297 (void) close(mntfd);
298
299 return (ioctl_set_handle(handle, error));
300 }
301
302 int
303 fsd_get_info(fsd_handle_t *handle, fsd_info_t *info)
304 {
305 fsd_ioc_t ioc;
306 int error;
307
308 error = ioctl(handle->fd, FSD_GET_INFO, &ioc);
309 if (error == 0)
310 (void) memcpy(info, &ioc.fsdioc_info, sizeof (*info));
311
312 return (ioctl_set_handle(handle, error));
313 }
314
315 int
316 fsd_get_list(fsd_handle_t *handle, fsd_fs_t *fslist, int *count)
317 {
318 fsd_ioc_t ioc;
319 int error;
320
321 ioc.fsdioc_list.listp = (uintptr_t)fslist;
322 ioc.fsdioc_list.count = *count;
323
324 error = ioctl(handle->fd, FSD_GET_LIST, &ioc);
325 *count = ioc.fsdioc_list.count;
326 return (ioctl_set_handle(handle, error));
327 }