xref: /netbsd-src/external/bsd/am-utils/dist/conf/umount/umount_linux.c (revision 8bae5d409deb915cf7c8f0539fae22ff2cb8a313)
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
umount_fs(char * mntdir,const char * mnttabname,u_int unmount_flags)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
umount2_fs(const char * mntdir,u_int unmount_flags)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