/* * Append a mntent structure to the * current mount table. */ void write_mntent(mntent_t *mp, const char *mnttabname) { int retries = 0; FILE *mfp; if (!mtab_is_writable()) { return; } enfile: mfp = open_locked_mtab(mnttabname, "a", mp->mnt_dir); if (mfp) { mtab_stripnl(mp->mnt_opts); if (addmntent(mfp, mp)) plog(XLOG_ERROR, "Couldn't write %s: %m", mnttabname); if (fflush(mfp)) plog(XLOG_ERROR, "Couldn't flush %s: %m", mnttabname); (void) endmntent(mfp); } else { if (errno == ENFILE && retries < NFILE_RETRIES) { sleep(1); goto enfile; } plog(XLOG_ERROR, "setmntent(\"%s\", \"a\"): %m", mnttabname); } unlock_mntlist(); }
static int check_mtab(void) { int res = 0; if (!nomtab) { if (mtab_is_writable()) res++; else error(_("%s: cannot modify " MOUNTED ".\n" "Please remount the partition with -f option" " after making " MOUNTED " writable."), progname); } return res; }
/* * Code based on similar function in util-linux-2.12p/mount/mount.c * */ static void update_mtab_entry(char *spec, char *node, char *type, char *opts, int flags, int freq, int pass) { struct my_mntent mnt; mnt.mnt_fsname = canonicalize (spec); mnt.mnt_dir = canonicalize (node); mnt.mnt_type = type; mnt.mnt_opts = opts; mnt.mnt_freq = freq; mnt.mnt_passno = pass; /* We get chatty now rather than after the update to mtab since the mount succeeded, even if the write to /etc/mtab should fail. */ if (verbose) print_one (&mnt); if (!nomtab && mtab_is_writable()) { if (flags & MS_REMOUNT) update_mtab (mnt.mnt_dir, &mnt); else { mntFILE *mfp; lock_mtab(); mfp = my_setmntent(MOUNTED, "a+"); if (mfp == NULL || mfp->mntent_fp == NULL) { com_err(progname, OCFS2_ET_IO, "%s, %s", MOUNTED, strerror(errno)); } else { if ((my_addmntent (mfp, &mnt)) == 1) { com_err(progname, OCFS2_ET_IO, "%s, %s", MOUNTED, strerror(errno)); } } my_endmntent(mfp); unlock_mtab(); } } my_free(mnt.mnt_fsname); my_free(mnt.mnt_dir); }
/* * Write out a mount list */ void rewrite_mtab(mntlist *mp, const char *mnttabname) { FILE *mfp; int error = 0; char tmpname[64]; int retries; int tmpfd; char *cp; char mcp[128]; if (!mtab_is_writable()) { return; } /* * Concoct a temporary name in the same directory as the target mount * table so that rename() will work. */ xstrlcpy(mcp, mnttabname, sizeof(mcp)); cp = strrchr(mcp, '/'); if (cp) { memmove(tmpname, mcp, cp - mcp); tmpname[cp - mcp] = '\0'; } else { plog(XLOG_WARNING, "No '/' in mtab (%s), using \".\" as tmp directory", mnttabname); tmpname[0] = '.'; tmpname[1] = '\0'; } xstrlcat(tmpname, "/mtabXXXXXX", sizeof(tmpname)); retries = 0; enfile1: #ifdef HAVE_MKSTEMP tmpfd = mkstemp(tmpname); fchmod(tmpfd, 0644); #else /* not HAVE_MKSTEMP */ mktemp(tmpname); tmpfd = open(tmpname, O_RDWR | O_CREAT | O_TRUNC, 0644); #endif /* not HAVE_MKSTEMP */ if (tmpfd < 0) { if (errno == ENFILE && retries++ < NFILE_RETRIES) { sleep(1); goto enfile1; } plog(XLOG_ERROR, "%s: open: %m", tmpname); return; } if (close(tmpfd) < 0) plog(XLOG_ERROR, "Couldn't close tmp file descriptor: %m"); retries = 0; enfile2: mfp = setmntent(tmpname, "w"); if (!mfp) { if (errno == ENFILE && retries++ < NFILE_RETRIES) { sleep(1); goto enfile2; } plog(XLOG_ERROR, "setmntent(\"%s\", \"w\"): %m", tmpname); error = 1; goto out; } while (mp) { if (mp->mnt) { if (addmntent(mfp, mp->mnt)) { plog(XLOG_ERROR, "Can't write entry to %s", tmpname); error = 1; goto out; } } mp = mp->mnext; } /* * SunOS 4.1 manuals say that the return code from entmntent() * is always 1 and to treat as a void. That means we need to * call fflush() to make sure the new mtab file got written. */ if (fflush(mfp)) { plog(XLOG_ERROR, "flush new mtab file: %m"); error = 1; goto out; } (void) endmntent(mfp); /* * Rename temporary mtab to real mtab */ if (rename(tmpname, mnttabname) < 0) { plog(XLOG_ERROR, "rename %s to %s: %m", tmpname, mnttabname); error = 1; goto out; } out: if (error) (void) unlink(tmpname); }
void update_mtab (const char *dir, struct mntent *instead) { mntFILE *mfp, *mftmp; const char *fnam = MOUNTED; struct mntentchn mtabhead; /* dummy */ struct mntentchn *mc, *mc0, *absent = NULL; if (mtab_does_not_exist() || !mtab_is_writable()) return; lock_mtab(); /* having locked mtab, read it again */ mc0 = mc = &mtabhead; mc->nxt = mc->prev = NULL; mfp = nfs_setmntent(fnam, "r"); if (mfp == NULL || mfp->mntent_fp == NULL) { int errsv = errno; nfs_error (_("cannot open %s (%s) - mtab not updated"), fnam, strerror (errsv)); goto leave; } read_mntentchn(mfp, fnam, mc); /* find last occurrence of dir */ for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev) if (streq(mc->m.mnt_dir, dir)) break; if (mc && mc != mc0) { if (instead == NULL) { /* An umount - remove entry */ if (mc && mc != mc0) { mc->prev->nxt = mc->nxt; mc->nxt->prev = mc->prev; free(mc); } } else { /* A remount */ mc->m.mnt_opts = instead->mnt_opts; } } else if (instead) { /* not found, add a new entry */ absent = xmalloc(sizeof(*absent)); absent->m = *instead; absent->nxt = mc0; absent->prev = mc0->prev; mc0->prev = absent; if (mc0->nxt == NULL) mc0->nxt = absent; } /* write chain to mtemp */ mftmp = nfs_setmntent (MOUNTED_TEMP, "w"); if (mftmp == NULL || mftmp->mntent_fp == NULL) { int errsv = errno; nfs_error (_("cannot open %s (%s) - mtab not updated"), MOUNTED_TEMP, strerror (errsv)); goto leave; } for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { if (nfs_addmntent(mftmp, &(mc->m)) == 1) { int errsv = errno; die (EX_FILEIO, _("error writing %s: %s"), MOUNTED_TEMP, strerror (errsv)); } } #if 0 /* the chain might have strings copied from 'instead', * so we cannot safely free it. * And there is no need anyway because we are going to exit * shortly. So just don't call discard_mntentchn.... */ discard_mntentchn(mc0); #endif if (fchmod (fileno (mftmp->mntent_fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { int errsv = errno; nfs_error(_("%s: error changing mode of %s: %s"), progname, MOUNTED_TEMP, strerror (errsv)); } nfs_endmntent (mftmp); { /* * If mount is setuid and some non-root user mounts sth, * then mtab.tmp might get the group of this user. Copy uid/gid * from the present mtab before renaming. */ struct stat sbuf; if (stat (MOUNTED, &sbuf) == 0) { if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) { nfs_error(_("%s: error changing owner of %s: %s"), progname, MOUNTED_TEMP, strerror (errno)); } } } /* rename mtemp to mtab */ if (rename (MOUNTED_TEMP, MOUNTED) < 0) { int errsv = errno; nfs_error(_("%s: can't rename %s to %s: %s\n"), progname, MOUNTED_TEMP, MOUNTED, strerror(errsv)); } leave: unlock_mtab(); }
/* Umount a single device. Return a status code, so don't exit on a non-fatal error. We lock/unlock around each umount. */ static int umount_one (const char *spec, const char *node, const char *type, const char *opts) { int umnt_err, umnt_err2; int isroot; int res; /* Special case for root. As of 0.99pl10 we can (almost) unmount root; the kernel will remount it readonly so that we can carry on running afterwards. The readonly remount is illegal if any files are opened for writing at the time, so we can't update mtab for an unmount of root. As it is only really a remount, this doesn't matter too much. [sct May 29, 1993] */ isroot = (streq (node, "/") || streq (node, "root") || streq (node, "rootfs")); if (isroot) umount_nomtab++; #if HAVE_NFS /* Ignore any RPC errors, so that you can umount the filesystem if the server is down. */ if (strcasecmp(type, "nfs") == 0) nfs_umount_rpc_call(spec, opts); #endif umnt_err = umnt_err2 = 0; res = umount (node); if (res < 0) { umnt_err = errno; /* A device might have been mounted on a node that has since been deleted or renamed, so if node fails, also try spec. */ /* if (umnt_err == ENOENT || umnt_err == EINVAL) */ if (umnt_err != EBUSY && strcmp(node, spec)) { if (umount_verbose) printf ("could not umount %s - trying %s instead\n", node, spec); res = umount (spec); if (res < 0) umnt_err2 = errno; /* Do not complain about remote NFS mount points */ if (errno == ENOENT && index(spec, ':')) umnt_err2 = 0; } } if (res < 0 && remount && (umnt_err == EBUSY || umnt_err2 == EBUSY)) { /* Umount failed - let us try a remount */ res=mount(spec, node, NULL, MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); if (res == 0) { struct mntent remnt; fprintf(stderr, "umount: %s busy - remounted read-only\n", spec); remnt.mnt_type = remnt.mnt_fsname = NULL; remnt.mnt_dir = xstrdup(node); remnt.mnt_opts = "ro"; update_mtab(node, &remnt); return 0; } else if (errno != EBUSY) { /* hmm ... */ perror("remount"); fprintf(stderr, "umount: could not remount %s read-only\n", spec); } } if (res >= 0) { /* Umount succeeded, update mtab. */ if (umount_verbose) printf ("%s umounted\n", spec); if (!umount_nomtab && mtab_is_writable()) { struct mntentchn *mc; /* Special stuff for loop devices */ if ((mc = getmntfile (spec)) || (mc = getmntfile (node))) { char *opts; /* old style mtab line? */ if (streq(mc->mnt_type, "loop")) if (del_loop(spec)) goto fail; /* new style mtab line? */ opts = mc->mnt_opts ? xstrdup(mc->mnt_opts) : ""; for (opts = strtok (opts, ","); opts; opts = strtok (NULL, ",")) { if (!strncmp(opts, "loop=", 5)) { if (del_loop(opts+5)) goto fail; break; } } } else { /* maybe spec is a loop device? */ /* no del_loop() - just delete it from mtab */ if ((mc = getmntoptfile (spec)) != NULL) node = mc->mnt_dir; } /* Non-loop stuff */ update_mtab (node, NULL); } return 0; } fail: /* Umount or del_loop failed, complain, but don't die. */ if (!umount_nomtab) { /* remove obsolete entry */ if (umnt_err == EINVAL || umnt_err == ENOENT) update_mtab (node, NULL); } if (umnt_err2) complain(umnt_err2, spec); if (umnt_err && umnt_err != umnt_err2) complain(umnt_err, node); return 1; }