/* Shutdown the filesystem; flags are as for fsys_goaway. */ error_t netfs_shutdown (int flags) { error_t helper (struct node *node) { error_t err; mach_port_t control; err = fshelp_fetch_control (&node->transbox, &control); if (!err && (control != MACH_PORT_NULL)) { mutex_unlock (&node->lock); err = fsys_goaway (control, flags); mach_port_deallocate (mach_task_self (), control); mutex_lock (&node->lock); } else err = 0; if ((err == MIG_SERVER_DIED) || (err == MACH_SEND_INVALID_DEST)) err = 0; return err; }
/* Implement dir_unlink as described in <hurd/fs.defs>. */ kern_return_t diskfs_S_dir_unlink (struct protid *dircred, char *name) { struct node *dnp; struct node *np; struct dirstat *ds = alloca (diskfs_dirstat_size); error_t err; mach_port_t control = MACH_PORT_NULL; if (!dircred) return EOPNOTSUPP; dnp = dircred->po->np; if (diskfs_check_readonly ()) return EROFS; pthread_mutex_lock (&dnp->lock); err = diskfs_lookup (dnp, name, REMOVE, &np, ds, dircred); if (err == EAGAIN) err = EPERM; /* 1003.1-1996 5.5.1.4 */ if (err) { diskfs_drop_dirstat (dnp, ds); pthread_mutex_unlock (&dnp->lock); return err; } /* This isn't the BSD behavior, but it is Posix compliant and saves us on several race conditions.*/ if (S_ISDIR(np->dn_stat.st_mode)) { if (np == dnp) /* gotta catch '.' */ diskfs_nrele (np); else diskfs_nput (np); diskfs_drop_dirstat (dnp, ds); pthread_mutex_unlock (&dnp->lock); return EPERM; /* 1003.1-1996 5.5.1.4 */ } err = diskfs_dirremove (dnp, np, name, ds); if (diskfs_synchronous) diskfs_node_update (dnp, 1); if (err) { diskfs_nput (np); pthread_mutex_unlock (&dnp->lock); return err; } np->dn_stat.st_nlink--; np->dn_set_ctime = 1; if (diskfs_synchronous) diskfs_node_update (np, 1); if (np->dn_stat.st_nlink == 0) fshelp_fetch_control (&np->transbox, &control); /* This check is necessary because we might get here on an error while checking the mode on something which happens to be `.'. */ if (np == dnp) diskfs_nrele (np); else diskfs_nput (np); pthread_mutex_unlock (&dnp->lock); if (control) { fsys_goaway (control, FSYS_GOAWAY_UNLINK); mach_port_deallocate (mach_task_self (), control); } return err; }
/* Mount one filesystem. */ static error_t do_mount (struct fs *fs, int remount) { error_t err; char *fsopts, *o; size_t fsopts_len; char *mntopts; size_t mntopts_len; fsys_t mounted; inline void explain (const char *command) { if (verbose) { const char *o; printf ("%s %s", command, fs->mntent.mnt_dir); for (o = fsopts; o; o = argz_next (fsopts, fsopts_len, o)) printf (" %s", o); putchar ('\n'); } } err = fs_fsys (fs, &mounted); if (err) { error (0, err, "cannot determine if %s is already mounted", fs->mntent.mnt_fsname); return err; } /* Produce an argz of translator option arguments from the given FS's options and the command-line options. */ #define ARGZ(call) \ err = argz_##call; \ if (err) \ error (3, ENOMEM, "collecting mount options"); \ if (fs->mntent.mnt_opts) { /* Append the fstab options to any specified on the command line. */ ARGZ (create_sep (fs->mntent.mnt_opts, ',', &mntopts, &mntopts_len)); /* Remove the `noauto' and `bind' options, since they're for us not the filesystem. */ for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o)) { if (strcmp (o, MNTOPT_NOAUTO) == 0) argz_delete (&mntopts, &mntopts_len, o); if (strcmp (o, "bind") == 0) { fs->mntent.mnt_type = strdup ("firmlink"); if (!fs->mntent.mnt_type) error (3, ENOMEM, "failed to allocate memory"); argz_delete (&mntopts, &mntopts_len, o); } } ARGZ (append (&mntopts, &mntopts_len, options, options_len)); } else { mntopts = options; mntopts_len = options_len; } /* Convert the list of options into a list of switch arguments. */ fsopts = 0; fsopts_len = 0; for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o)) if (*o == '-') /* Allow letter opts `-o -r,-E', BSD style. */ { ARGZ (add (&fsopts, &fsopts_len, o)); } else if ((strcmp (o, "defaults") != 0) && (strlen (o) != 0)) { /* Prepend `--' to the option to make a long option switch, e.g. `--ro' or `--rsize=1024'. */ char arg[2 + strlen (o) + 1]; arg[0] = arg[1] = '-'; memcpy (&arg[2], o, sizeof arg - 2); ARGZ (add (&fsopts, &fsopts_len, arg)); } if (mntopts != options) free (mntopts); #undef ARGZ if (remount) { if (mounted == MACH_PORT_NULL) { error (0, 0, "%s not already mounted", fs->mntent.mnt_fsname); return EBUSY; } /* Send an RPC to request the new options, including --update. */ explain ("fsysopts"); err = fsys_set_options (mounted, fsopts, fsopts_len, 0); if (err) error (0, err, "cannot remount %s", fs->mntent.mnt_fsname); return err; } else { /* Error during file lookup; we use this to avoid duplicating error messages. */ error_t open_err = 0; /* The control port for any active translator we start up. */ fsys_t active_control; file_t node; /* Port to the underlying node. */ struct fstype *type; /* The callback to start_translator opens NODE as a side effect. */ error_t open_node (int flags, mach_port_t *underlying, mach_msg_type_name_t *underlying_type, task_t task, void *cookie) { node = file_name_lookup (fs->mntent.mnt_dir, flags | O_NOTRANS, 0666); if (node == MACH_PORT_NULL) { open_err = errno; return open_err; } *underlying = node; *underlying_type = MACH_MSG_TYPE_COPY_SEND; return 0; } /* Do not fail if there is an active translator if --fake is given. This mimics Linux mount utility more closely which just looks into the mtab file. */ if (mounted != MACH_PORT_NULL && !fake) { error (0, 0, "%s already mounted", fs->mntent.mnt_fsname); return EBUSY; } if (strcmp (fs->mntent.mnt_type, "auto") == 0) { #if HAVE_BLKID char *type = blkid_get_tag_value (NULL, "TYPE", fs->mntent.mnt_fsname); if (! type) { error (0, 0, "failed to detect file system type"); return EFTYPE; } else { if (strcmp (type, "vfat") == 0) fs->mntent.mnt_type = strdup ("fat"); else fs->mntent.mnt_type = strdup (type); if (! fs->mntent.mnt_type) error (3, ENOMEM, "failed to allocate memory"); } #else fs->mntent.mnt_type = strdup ("ext2"); if (! fs->mntent.mnt_type) error (3, ENOMEM, "failed to allocate memory"); #endif } err = fs_type (fs, &type); if (err) { error (0, err, "%s: cannot determine filesystem type", fs->mntent.mnt_fsname); return err; } if (type->program == 0) { error (0, 0, "%s: filesystem type `%s' unknown", fs->mntent.mnt_fsname, type->name); return EFTYPE; } /* Stick the translator program name in front of the option switches. */ err = argz_insert (&fsopts, &fsopts_len, fsopts, type->program); /* Now stick the device name on the end as the last argument. */ if (!err) err = argz_add (&fsopts, &fsopts_len, fs->mntent.mnt_fsname); if (err) error (3, ENOMEM, "collecting mount options"); /* Now we have a translator command line argz in FSOPTS. */ if (fake) { /* Fake the translator startup. */ mach_port_t underlying; mach_msg_type_name_t underlying_type; err = open_node (O_READ, &underlying, &underlying_type, 0, NULL); if (err) error (1, errno, "cannot mount on %s", fs->mntent.mnt_dir); mach_port_deallocate (mach_task_self (), underlying); /* See if the translator is at least executable. */ if (access(type->program, X_OK) == -1) error (1, errno, "can not execute %s", type->program); return 0; } explain ("settrans -a"); err = fshelp_start_translator (open_node, NULL, fsopts, fsopts, fsopts_len, timeout, &active_control); /* If ERR is due to a problem opening the translated node, we print that name, otherwise, the name of the translator. */ if (open_err) error (0, open_err, "cannot mount on %s", fs->mntent.mnt_dir); else if (err) error (0, err, "cannot start translator %s", fsopts); else { err = file_set_translator (node, 0, FS_TRANS_SET|FS_TRANS_EXCL, 0, 0, 0, active_control, MACH_MSG_TYPE_COPY_SEND); if (err == EBUSY) error (0, 0, "%s already mounted on", fs->mntent.mnt_dir); else if (err) error (0, err, "cannot set translator on %s", fs->mntent.mnt_dir); if (err) fsys_goaway (active_control, FSYS_GOAWAY_FORCE); mach_port_deallocate (mach_task_self (), active_control); } return err; }