4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2006 Ricardo Correia.
23 * Use is subject to license terms.
26 #include <sys/debug.h>
27 #include <sys/mount.h>
28 #include <sys/types.h>
30 #include <sys/cmn_err.h>
38 #include "libsolkerncompat.h"
39 #include "zfs_ioctl.h"
40 #include "zfsfuse_socket.h"
42 #include "cmd_listener.h"
43 #include "fuse_listener.h"
46 #include "zfs_operations.h"
49 static int ioctl_fd = -1;
50 static int lock_fd = -1;
52 #define LOCKDIR "/var/lock/zfs"
53 #define LOCKFILE LOCKDIR "/zfs_lock"
55 boolean_t listener_thread_started = B_FALSE;
56 pthread_t listener_thread;
60 char * fuse_mount_options = NULL;
62 extern vfsops_t *zfs_vfsops;
63 extern int zfs_vfsinit(int fstype, char *name);
65 static int zfsfuse_do_locking(int in_child)
67 /* Ignores errors since the directory might already exist */
72 ASSERT(lock_fd == -1);
74 * before the fork, we create the file, truncating it, and locking the
77 lock_fd = creat(LOCKFILE, S_IRUSR | S_IWUSR);
82 * only if we /could/ lock all of the file,
83 * we shall lock just the first byte; this way
84 * we can let the daemon child process lock the
85 * remainder of the file after forking
87 if (0==lockf(lock_fd, F_TEST, 0))
88 return lockf(lock_fd, F_TLOCK, 1);
93 ASSERT(lock_fd != -1);
95 * after the fork, we instead try to lock only the region /after/ the
96 * first byte; the file /must/ already exist. Only in this way can we
97 * prevent races with locking before or after the daemonization
99 lock_fd = open(LOCKFILE, O_WRONLY);
103 ASSERT(-1 == lockf(lock_fd, F_TEST, 0)); /* assert that parent still has the lock on the first byte */
104 if (-1 == lseek(lock_fd, 1, SEEK_SET))
110 return lockf(lock_fd, F_TLOCK, 0);
114 void do_daemon(const char *pidfile)
119 if (0 == stat(pidfile, &dummy)) {
120 cmn_err(CE_WARN, "%s already exists; aborting.", pidfile);
126 * info gleaned from the web, notably
127 * http://www.enderunix.org/docs/eng/daemon.php
131 * http://sourceware.org/git/?p=glibc.git;a=blob;f=misc/daemon.c;h=7597ce9996d5fde1c4ba622e7881cf6e821a12b4;hb=HEAD
134 int forkres, devnull;
137 return; /* already a daemon */
142 cmn_err(CE_WARN, "Cannot fork (%s)", strerror(errno));
149 for (i=getdtablesize();i>=0;--i)
150 if ((lock_fd!=i) && (ioctl_fd!=i)) /* except for the lockfile and the comm socket */
151 close(i); /* close all descriptors */
153 /* allow for airtight lockfile semantics... */
156 tv.tv_usec = 200000; /* 0.2 seconds */
157 select(0, NULL, NULL, NULL, &tv);
159 VERIFY(0 == close(lock_fd));
164 /* child (daemon) continues */
165 setsid(); /* obtain a new process group */
166 VERIFY(0 == chdir("/")); /* change working directory */
167 umask(027); /* set newly created file permissions */
168 devnull=open("/dev/null",O_RDWR); /* handle standard I/O */
169 ASSERT(-1 != devnull);
170 dup2(devnull, 0); /* stdin */
171 dup2(devnull, 1); /* stdout */
172 dup2(devnull, 2); /* stderr */
177 * contrary to recommendation, do _not_ ignore SIGCHLD:
178 * it will break exec-ing subprocesses, e.g. for kstat mount and
179 * (presumably) nfs sharing!
181 * this will lead to really bad performance too
183 signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
184 signal(SIGTTOU,SIG_IGN);
185 signal(SIGTTIN,SIG_IGN);
188 if (0 != zfsfuse_do_locking(1))
190 cmn_err(CE_WARN, "Unexpected locking conflict (%s: %s)", strerror(errno), LOCKFILE);
195 FILE *f = fopen(pidfile, "w");
197 cmn_err(CE_WARN, "Error opening %s.", pidfile);
200 if (fprintf(f, "%d\n", getpid()) < 0) {
204 if (fclose(f) != 0) {
211 extern size_t stack_size;
213 int do_init_fusesocket()
215 if(zfsfuse_do_locking(0) != 0) {
216 cmn_err(CE_WARN, "Error locking " LOCKFILE ". Make sure there isn't another zfs-fuse process running and that you have appropriate permissions.");
220 ioctl_fd = zfsfuse_socket_create();
228 libsolkerncompat_init();
230 zfs_vfsinit(zfstype, NULL);
232 VERIFY(zfs_ioctl_init() == 0);
234 VERIFY(ioctl_fd != -1); // initialization moved to do_init_fusesocket
236 VERIFY(cmd_listener_init() == 0);
239 VERIFY(0 == pthread_attr_init(&attr));
241 pthread_attr_setstacksize(&attr,stack_size);
242 if(pthread_create(&listener_thread, &attr, listener_loop, (void *) &ioctl_fd) != 0) {
243 VERIFY(0 == pthread_attr_destroy(&attr));
244 cmn_err(CE_WARN, "Error creating listener thread.");
247 VERIFY(0 == pthread_attr_destroy(&attr));
249 listener_thread_started = B_TRUE;
251 return zfsfuse_listener_init();
256 if(listener_thread_started) {
257 exit_listener = B_TRUE;
258 if(pthread_join(listener_thread, NULL) != 0)
259 cmn_err(CE_WARN, "Error in pthread_join().");
262 zfsfuse_listener_exit();
266 zfsfuse_socket_close(ioctl_fd);
268 int ret = zfs_ioctl_fini();
270 cmn_err(CE_WARN, "Error %i in zfs_ioctl_fini().\n", ret);
272 libsolkerncompat_exit();
275 /* big_writes added if fuse 2.8 is detected at runtime */
276 /* other mount options are added if specified in the command line */
277 #define FUSE_OPTIONS "subtype=zfs,fsname=%s,allow_other,suid,dev%s" // ,big_writes"
280 uint32_t mounted = 0;
283 static int detect_fuseoption(const char* options, const char* option)
285 if ((!options) || (!option))
287 ASSERT(NULL == strchr(option, '%'));
290 VERIFY(asprintf(&spec, "%s%%n", option));
295 char* tmp = strdup(options);
296 for (char* tok=strtok(tmp, ","); tok && !detected; tok=strtok(NULL, ","))
297 if (sscanf(tok, spec, &pos) >= 0 && (-1!=pos))
304 fprintf(stderr, "detected: %s\n", option);
308 int do_mount(char *spec, char *dir, int mflag, char *opt)
312 vfs_t *vfs = kmem_zalloc(sizeof(vfs_t), KM_SLEEP);
316 VFS_INIT(vfs, zfs_vfsops, 0);
319 struct mounta uap = {
322 .flags = mflag | MS_SYSSPACE,
323 .fstype = "zfs-fuse",
327 .optlen = strlen(opt)
331 if ((ret = VFS_MOUNT(vfs, rootdir, &uap, kcred)) != 0) {
332 kmem_free(vfs, sizeof(vfs_t));
335 /* Actually, optptr is totally ignored by VFS_MOUNT.
336 * So we are going to pass this with fuse_mount_options if possible */
337 if (fuse_mount_options == NULL)
338 fuse_mount_options = "";
339 char real_opts[1024];
341 if (*fuse_mount_options)
342 strcat(real_opts,fuse_mount_options); // comes with a starting ,
344 sprintf(&real_opts[strlen(real_opts)],",%s",opt);
347 atomic_inc_32(&mounted);;
349 fprintf(stderr, "mounting %s\n", dir);
352 char *fuse_opts = NULL;
353 int has_default_perm = 0;
354 if (fuse_version() <= 27) {
355 if(asprintf(&fuse_opts, FUSE_OPTIONS, spec, real_opts) == -1) {
356 VERIFY(do_umount(vfs, B_FALSE) == 0);
360 if(asprintf(&fuse_opts, FUSE_OPTIONS ",big_writes", spec, real_opts) == -1) {
361 VERIFY(do_umount(vfs, B_FALSE) == 0);
366 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
368 if(fuse_opt_add_arg(&args, "") == -1 ||
369 fuse_opt_add_arg(&args, "-o") == -1 ||
370 fuse_opt_add_arg(&args, fuse_opts) == -1) {
371 fuse_opt_free_args(&args);
373 VERIFY(do_umount(vfs, B_FALSE) == 0);
376 has_default_perm = detect_fuseoption(fuse_opts,"default_permissions");
379 struct fuse_chan *ch = fuse_mount(dir, &args);
382 VERIFY(do_umount(vfs, B_FALSE) == 0);
386 if (has_default_perm)
387 vfs->fuse_attribute = FUSE_VFS_HAS_DEFAULT_PERM;
389 struct fuse_session *se = fuse_lowlevel_new(&args, &zfs_operations, sizeof(zfs_operations), vfs);
390 fuse_opt_free_args(&args);
393 VERIFY(do_umount(vfs, B_FALSE) == 0); /* ZFSFUSE: FIXME?? */
394 fuse_unmount(dir,ch);
398 fuse_session_add_chan(se, ch);
400 if(zfsfuse_newfs(dir, ch) != 0) {
401 fuse_session_destroy(se);
402 fuse_unmount(dir,ch);
409 int do_umount(vfs_t *vfs, boolean_t force)
411 VFS_SYNC(vfs, 0, kcred);
413 int ret = VFS_UNMOUNT(vfs, force ? MS_FORCE : 0, kcred);
417 ASSERT(force || vfs->vfs_count == 1);
421 fprintf(stderr, "mounted filesystems: %i\n", atomic_dec_32_nv(&mounted));