int umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) { int error; eintr: error = umount(mntdir, MNT_NOFORCE); if (error < 0) error = errno; switch (error) { case EINVAL: case ENOTBLK: plog(XLOG_WARNING, "unmount: %s is not mounted", mntdir); error = 0; /* Not really an error */ break; case ENOENT: plog(XLOG_ERROR, "mount point %s: %m", mntdir); break; case EINTR: /* not sure why this happens, but it does. ask kirk one day... */ dlog("%s: unmount: %m", mntdir); goto eintr; #ifdef MNT2_GEN_OPT_FORCE case EBUSY: case EIO: case ESTALE: /* caller determines if forced unmounts should be used */ if (unmount_flags & AMU_UMOUNT_FORCE) { error = umount2_fs(mntdir, unmount_flags); if ((error = umount2_fs(mntdir, unmount_flags)) < 0) error = errno; else return error; } /* fallthrough */ #endif /* MNT2_GEN_OPT_FORCE */ default: dlog("%s: unmount: %m", mntdir); break; } return error; }
/* * Initialize a top-level mount. In our case, if the user asked for * forced_unmounts, and the OS supports it, then we try forced/lazy unmounts * on any previous toplvl mounts. This is useful if a previous Amd died and * left behind toplvl mount points (this Amd will clean them up). * * WARNING: Don't use forced/lazy unmounts if you have another valid Amd * running, because this code WILL force those valid toplvl mount points to * be detached as well! */ static int amfs_toplvl_init(mntfs *mf) { int error = 0; #if (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) && (defined(HAVE_UVMOUNT) || defined(HAVE_UMOUNT2)) if (gopt.flags & CFM_FORCED_UNMOUNTS) { plog(XLOG_INFO, "amfs_toplvl_init: trying forced/lazy unmount of %s", mf->mf_mount); error = umount2_fs(mf->mf_mount, AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH); if (error) plog(XLOG_INFO, "amfs_toplvl_init: forced/lazy unmount failed: %m"); else dlog("amfs_toplvl_init: forced/lazy unmount succeeded"); } #endif /* (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) && (HAVE_UVMOUNT || HAVE_UMOUNT2) */ return error; }
int umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) { mntlist *mlist, *mp, *mp_save = NULL; int error = 0; #ifdef HAVE_LOOP_DEVICE char *opt, *xopts = NULL; char loopstr[] = "loop="; char *loopdev; #endif /* HAVE_LOOP_DEVICE */ unsigned int retries = 8; mp = mlist = read_mtab(mntdir, mnttabname); /* * Search the mount table looking for * the correct (ie last) matching entry */ while (mp) { if (STREQ(mp->mnt->mnt_dir, mntdir)) mp_save = mp; mp = mp->mnext; } if (!mp_save) { plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir); /* Assume it is already unmounted */ error = 0; goto out; } plog(XLOG_ERROR, "Trying unmount %s, umount_flags 0x%x", mp_save->mnt->mnt_dir, unmount_flags); dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir); #ifdef MOUNT_TABLE_ON_FILE /* * This unmount may hang leaving this process with an exclusive lock on * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount, * then lock mtab (again) and reread it and finally update it. */ unlock_mntlist(); #endif /* MOUNT_TABLE_ON_FILE */ again: #if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) /* * If user asked to try forced unmounts, then do a quick check to see if * the mount point is hung badly. If so, then try to detach it by * force; if the latter works, we're done. */ if (unmount_flags & AMU_UMOUNT_DETACH) { /* * Note: we pass both DETACH and FORCE flags, because umount2_fs below * (on Linux), should try FORCE before DETACH (the latter always * succeeds). */ error = umount2_fs(mp_save->mnt->mnt_dir, unmount_flags & (AMU_UMOUNT_DETACH|AMU_UMOUNT_FORCE)); } else #endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) */ error = UNMOUNT_TRAP(mp_save->mnt); /* Linux kernel can be sluggish for some reason */ if (error == EBUSY && retries--) { struct timespec tm = {0, 200000000}; nanosleep(&tm, NULL); goto again; } if (error < 0) { plog(XLOG_WARNING, "unmount(%s) failed: %m", mp_save->mnt->mnt_dir); switch ((error = errno)) { case EINVAL: case ENOTBLK: plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir); error = 0; /* Not really an error */ break; case ENOENT: /* * This could happen if the kernel insists on following symlinks * when we try to unmount a direct mountpoint. We need to propagate * the error up so that the top layers know it failed and don't * try to rmdir() the mountpoint or other silly things. */ plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); break; #if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE) case EBUSY: /* * Caller determines if forced unmounts should be used now (for * EBUSY). If caller asked to force an unmount, *and* the above * "trivial" unmount attempt failed with EBUSY, then try to force * the unmount. */ if (unmount_flags & AMU_UMOUNT_FORCE) { error = umount2_fs(mp_save->mnt->mnt_dir, unmount_flags & AMU_UMOUNT_FORCE); if (error < 0) { plog(XLOG_WARNING, "%s: unmount/force: %m", mp_save->mnt->mnt_dir); error = errno; } } break; #endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE) */ default: dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); break; } } else { dlog("unmount(%s) succeeded", mp_save->mnt->mnt_dir); } dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir); /* * If we are successful or there was an ENOENT, remove * the mount entry from the mtab file. */ if (error && error != ENOENT) goto out; #ifdef HAVE_LOOP_DEVICE /* look for loop=/dev/loopX in mnt_opts */ xopts = xstrdup(mp_save->mnt->mnt_opts); /* b/c strtok is destructive */ for (opt = strtok(xopts, ","); opt; opt = strtok(NULL, ",")) if (NSTREQ(opt, loopstr, sizeof(loopstr) - 1)) { loopdev = opt + sizeof(loopstr) - 1; if (delete_loop_device(loopdev) < 0) plog(XLOG_WARNING, "unmount() failed to release loop device %s: %m", loopdev); else plog(XLOG_INFO, "unmount() released loop device %s OK", loopdev); break; } if (xopts) XFREE(xopts); #endif /* HAVE_LOOP_DEVICE */ #ifdef MOUNT_TABLE_ON_FILE free_mntlist(mlist); mp = mlist = read_mtab(mntdir, mnttabname); /* * Search the mount table looking for * the correct (ie last) matching entry */ mp_save = NULL; while (mp) { if (STREQ(mp->mnt->mnt_dir, mntdir)) mp_save = mp; mp = mp->mnext; } if (mp_save) { mnt_free(mp_save->mnt); mp_save->mnt = NULL; rewrite_mtab(mlist, mnttabname); } #endif /* MOUNT_TABLE_ON_FILE */ out: free_mntlist(mlist); return error; }
int umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) { mntlist *mlist, *mp, *mp_save = NULL; int error = 0; mp = mlist = read_mtab(mntdir, mnttabname); /* * Search the mount table looking for * the correct (ie last) matching entry */ while (mp) { if (STREQ(mp->mnt->mnt_dir, mntdir)) mp_save = mp; mp = mp->mnext; } if (mp_save) { dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir); #ifdef MOUNT_TABLE_ON_FILE /* * This unmount may hang leaving this process with an exclusive lock on * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount, * then lock mtab (again) and reread it and finally update it. */ unlock_mntlist(); #endif /* MOUNT_TABLE_ON_FILE */ #ifdef NEED_AUTOFS_SPACE_HACK if (unmount_flags & AMU_UMOUNT_AUTOFS) { char *mnt_dir_save = mp_save->mnt->mnt_dir; mp_save->mnt->mnt_dir = autofs_strdup_space_hack(mnt_dir_save); error = UNMOUNT_TRAP(mp_save->mnt); XFREE(mp_save->mnt->mnt_dir); mp_save->mnt->mnt_dir = mnt_dir_save; } else #endif /* NEED_AUTOFS_SPACE_HACK */ error = UNMOUNT_TRAP(mp_save->mnt); if (error < 0) { switch ((error = errno)) { case EINVAL: case ENOTBLK: plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir); error = 0; /* Not really an error */ break; case ENOENT: /* * This could happen if the kernel insists on following symlinks * when we try to unmount a direct mountpoint. We need to propagate * the error up so that the top layers know it failed and don't * try to rmdir() the mountpoint or other silly things. */ plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); break; #if defined(MNT2_GEN_OPT_FORCE) && defined(HAVE_UVMOUNT) case EBUSY: case EIO: case ESTALE: /* caller determines if forced unmounts should be used */ if (unmount_flags & AMU_UMOUNT_FORCE) { error = umount2_fs(mntdir, unmount_flags); if (error < 0) error = errno; else break; /* all is OK */ } /* fallthrough */ #endif /* MNT2_GEN_OPT_FORCE && HAVE_UVMOUNT */ default: dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); break; } } dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir); if (!error) { #ifdef MOUNT_TABLE_ON_FILE free_mntlist(mlist); mp = mlist = read_mtab(mntdir, mnttabname); /* * Search the mount table looking for * the correct (ie last) matching entry */ mp_save = NULL; while (mp) { if (STREQ(mp->mnt->mnt_dir, mntdir)) mp_save = mp; mp = mp->mnext; } if (mp_save) { mnt_free(mp_save->mnt); mp_save->mnt = NULL; rewrite_mtab(mlist, mnttabname); } #endif /* MOUNT_TABLE_ON_FILE */ } } else { plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir); /* * Assume it is already unmounted */ error = 0; } /* end of "if (mp_save)" statement */ free_mntlist(mlist); return error; }