1 /* $NetBSD: umount_linux.c,v 1.1.1.3 2015/01/17 16:34:16 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2014 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * 38 * File: am-utils/conf/umount/umount_linux.c 39 * 40 */ 41 42 /* 43 * Linux method of unmounting filesystems: if umount(2) failed with EIO or 44 * ESTALE, try umount2(2) with MNT_FORCE+MNT_DETACH. 45 */ 46 47 #ifdef HAVE_CONFIG_H 48 # include <config.h> 49 #endif /* HAVE_CONFIG_H */ 50 #include <am_defs.h> 51 #include <amu.h> 52 53 54 int 55 umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) 56 { 57 mntlist *mlist, *mp, *mp_save = NULL; 58 int error = 0; 59 #ifdef HAVE_LOOP_DEVICE 60 char *opt, *xopts = NULL; 61 char loopstr[] = "loop="; 62 char *loopdev; 63 #endif /* HAVE_LOOP_DEVICE */ 64 unsigned int retries = 8; 65 66 mp = mlist = read_mtab(mntdir, mnttabname); 67 68 /* 69 * Search the mount table looking for 70 * the correct (ie last) matching entry 71 */ 72 while (mp) { 73 if (STREQ(mp->mnt->mnt_dir, mntdir)) 74 mp_save = mp; 75 mp = mp->mnext; 76 } 77 78 if (!mp_save) { 79 plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir); 80 /* Assume it is already unmounted */ 81 error = 0; 82 goto out; 83 } 84 85 plog(XLOG_ERROR, "Trying unmount %s, umount_flags 0x%x", mp_save->mnt->mnt_dir, unmount_flags); 86 dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir); 87 88 #ifdef MOUNT_TABLE_ON_FILE 89 /* 90 * This unmount may hang leaving this process with an exclusive lock on 91 * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount, 92 * then lock mtab (again) and reread it and finally update it. 93 */ 94 unlock_mntlist(); 95 #endif /* MOUNT_TABLE_ON_FILE */ 96 97 again: 98 #if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) 99 /* 100 * If user asked to try forced unmounts, then do a quick check to see if 101 * the mount point is hung badly. If so, then try to detach it by 102 * force; if the latter works, we're done. 103 */ 104 if (unmount_flags & AMU_UMOUNT_DETACH) { 105 /* 106 * Note: we pass both DETACH and FORCE flags, because umount2_fs below 107 * (on Linux), should try FORCE before DETACH (the latter always 108 * succeeds). 109 */ 110 error = umount2_fs(mp_save->mnt->mnt_dir, 111 unmount_flags & (AMU_UMOUNT_DETACH|AMU_UMOUNT_FORCE)); 112 } else 113 #endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) */ 114 error = UNMOUNT_TRAP(mp_save->mnt); 115 116 /* Linux kernel can be sluggish for some reason */ 117 if (error == EBUSY && retries--) { 118 struct timespec tm = {0, 200000000}; 119 nanosleep(&tm, NULL); 120 goto again; 121 } 122 123 if (error < 0) { 124 plog(XLOG_WARNING, "unmount(%s) failed: %m", mp_save->mnt->mnt_dir); 125 switch ((error = errno)) { 126 case EINVAL: 127 case ENOTBLK: 128 plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir); 129 error = 0; /* Not really an error */ 130 break; 131 132 case ENOENT: 133 /* 134 * This could happen if the kernel insists on following symlinks 135 * when we try to unmount a direct mountpoint. We need to propagate 136 * the error up so that the top layers know it failed and don't 137 * try to rmdir() the mountpoint or other silly things. 138 */ 139 plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); 140 break; 141 142 #if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE) 143 case EBUSY: 144 /* 145 * Caller determines if forced unmounts should be used now (for 146 * EBUSY). If caller asked to force an unmount, *and* the above 147 * "trivial" unmount attempt failed with EBUSY, then try to force 148 * the unmount. 149 */ 150 if (unmount_flags & AMU_UMOUNT_FORCE) { 151 error = umount2_fs(mp_save->mnt->mnt_dir, 152 unmount_flags & AMU_UMOUNT_FORCE); 153 if (error < 0) { 154 plog(XLOG_WARNING, "%s: unmount/force: %m", 155 mp_save->mnt->mnt_dir); 156 error = errno; 157 } 158 } 159 break; 160 #endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE) */ 161 162 default: 163 dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); 164 break; 165 } 166 } else { 167 dlog("unmount(%s) succeeded", mp_save->mnt->mnt_dir); 168 } 169 dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir); 170 171 /* 172 * If we are successful or there was an ENOENT, remove 173 * the mount entry from the mtab file. 174 */ 175 if (error && error != ENOENT) 176 goto out; 177 178 #ifdef HAVE_LOOP_DEVICE 179 /* look for loop=/dev/loopX in mnt_opts */ 180 xopts = xstrdup(mp_save->mnt->mnt_opts); /* b/c strtok is destructive */ 181 for (opt = strtok(xopts, ","); opt; opt = strtok(NULL, ",")) 182 if (NSTREQ(opt, loopstr, sizeof(loopstr) - 1)) { 183 loopdev = opt + sizeof(loopstr) - 1; 184 if (delete_loop_device(loopdev) < 0) 185 plog(XLOG_WARNING, "unmount() failed to release loop device %s: %m", loopdev); 186 else 187 plog(XLOG_INFO, "unmount() released loop device %s OK", loopdev); 188 break; 189 } 190 if (xopts) 191 XFREE(xopts); 192 #endif /* HAVE_LOOP_DEVICE */ 193 194 #ifdef MOUNT_TABLE_ON_FILE 195 free_mntlist(mlist); 196 mp = mlist = read_mtab(mntdir, mnttabname); 197 198 /* 199 * Search the mount table looking for 200 * the correct (ie last) matching entry 201 */ 202 mp_save = NULL; 203 while (mp) { 204 if (STREQ(mp->mnt->mnt_dir, mntdir)) 205 mp_save = mp; 206 mp = mp->mnext; 207 } 208 209 if (mp_save) { 210 mnt_free(mp_save->mnt); 211 mp_save->mnt = NULL; 212 rewrite_mtab(mlist, mnttabname); 213 } 214 #endif /* MOUNT_TABLE_ON_FILE */ 215 216 out: 217 free_mntlist(mlist); 218 219 return error; 220 } 221 222 223 #if defined(HAVE_UMOUNT2) && (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) 224 /* 225 * Force unmount, no questions asked, without touching mnttab file. Try 226 * detach first because it is safer: will remove the hung mnt point without 227 * affecting hung applications. "Force" is more risky: it will cause the 228 * kernel to return EIO to applications stuck on a stat(2) of Amd. 229 */ 230 int 231 umount2_fs(const char *mntdir, u_int unmount_flags) 232 { 233 int error = 0; 234 235 #ifdef MNT2_GEN_OPT_DETACH 236 if (unmount_flags & AMU_UMOUNT_DETACH) { 237 error = umount2(mntdir, MNT2_GEN_OPT_DETACH); 238 if (error < 0 && (errno == EINVAL || errno == ENOENT)) 239 error = 0; /* ignore EINVAL/ENOENT */ 240 if (error < 0) { /* don't try FORCE if detach succeeded */ 241 plog(XLOG_WARNING, "%s: unmount/detach: %m", mntdir); 242 /* fall through to try "force" (if flag specified) */ 243 } else { 244 dlog("%s: unmount/detach: OK", mntdir); 245 return error; 246 } 247 } 248 #endif /* MNT2_GEN_OPT_DETACH */ 249 250 #ifdef MNT2_GEN_OPT_FORCE 251 if (unmount_flags & AMU_UMOUNT_FORCE) { 252 plog(XLOG_INFO, "umount2_fs: trying unmount/forced on %s", mntdir); 253 error = umount2(mntdir, MNT2_GEN_OPT_FORCE); 254 if (error < 0 && (errno == EINVAL || errno == ENOENT)) 255 error = 0; /* ignore EINVAL/ENOENT */ 256 if (error < 0) 257 plog(XLOG_WARNING, "%s: unmount/force: %m", mntdir); 258 else 259 dlog("%s: unmount/force: OK", mntdir); 260 /* fall through to return whatever error we got (if any) */ 261 } 262 #endif /* MNT2_GEN_OPT_FORCE */ 263 264 return error; 265 } 266 #endif /* HAVE_UMOUNT2 && (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) */ 267