/* 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; }
FILE *fopen(const char *path, const char *mode) { uint64_t fd; uint8_t perm; FILE *stream; /* check mode */ if (mode[0] != 'a' && mode[0] != 'w' && mode[0] != 'r') { errno = EINVAL; return NULL; } /* attempt to find the file */ fd = io_find(path); /* check if the object is a directory or null */ if (fd && (fs_type(fd) & RP_TYPE_FILE) == 0) { errno = EISDIR; return NULL; } if (!fd) { /* create the file */ if (mode[0] == 'w' || mode[0] == 'a') { if (!io_cons(path, RP_TYPE_FILE)) { return NULL; } } else { errno = ENOENT; return NULL; } } perm = fs_perm(fd, gettuser()); /* check read permissions */ if (mode[0] == 'r' || mode[1] == '+') { if ((perm & PERM_READ) == 0) { errno = EACCES; return NULL; } } /* check write permissions */ if (mode[0] == 'w' || mode[0] == 'a' || mode[1] == '+') { if ((perm & PERM_WRITE) == 0) { errno = EACCES; return NULL; } } /* reset the file contents */ if (mode[0] == 'w') { reset(fd); } /* open a stream on the file */ stream = malloc(sizeof(FILE)); if (!stream) { errno = ENOMEM; return NULL; } stream->fd = fd; stream->mutex = false; stream->position = 0; stream->size = fs_size(fd); stream->buffer = NULL; stream->buffsize = 0; stream->buffpos = 0; stream->revbuf = EOF; stream->flags = FILE_NBF | FILE_READ; if (mode[0] == 'w' || mode[0] == 'a' || mode[1] == '+') { stream->flags |= FILE_WRITE; } /* position the stream properly */ if (mode[0] == 'a' && mode[1] != '+') { fseek(stream, 0, SEEK_END); } else { fseek(stream, 0, SEEK_SET); } return stream; }
/* Starts FS's fsck program on FS's device, returning the pid of the process. If an error is encountered, prints an error message and returns 0. Filesystems that need not be fscked at all also return 0 (but don't print an error message). */ static pid_t fs_start_fsck (struct fs *fs, int flags) { pid_t pid; char flags_buf[10]; char *argv[4], **argp = argv; struct fstype *type; error_t err = fs_type (fs, &type); assert_perror (err); /* Should already have been checked for. */ assert (type->program); *argp++ = type->program; if (flags & (FSCK_F_PREEN|FSCK_F_YES|FSCK_F_NO|FSCK_F_FORCE|FSCK_F_SILENT)) { char *p = flags_buf; *argp++ = flags_buf; *p++ = '-'; if (flags & FSCK_F_PREEN) *p++ = 'p'; if (flags & FSCK_F_YES) *p++ = 'y'; if (flags & FSCK_F_NO) *p++ = 'n'; if (flags & FSCK_F_FORCE) *p++ = 'f'; if (flags & FSCK_F_SILENT) *p++ = 's'; *p = '\0'; } *argp++ = fs->mntent.mnt_fsname; *argp = 0; if (flags & FSCK_F_DRYRUN) { char *argz; size_t argz_len; argz_create (argv, &argz, &argz_len); argz_stringify (argz, argz_len, ' '); puts (argz); free (argz); return 0; } pid = fork (); if (pid < 0) { error (0, errno, "fork"); return 0; } if (pid == 0) /* Child. */ { execv (type->program, argv); exit (FSCK_EX_EXEC); /* Exec failed. */ } if ((flags & FSCK_F_VERBOSE) || _debug) { char *argz; size_t argz_len; argz_create (argv, &argz, &argz_len); argz_stringify (argz, argz_len, ' '); fs_debug (fs, "Spawned pid %d: %s", pid, argz); if (flags & FSCK_F_VERBOSE) puts (argz); free (argz); } return pid; }