1 /* $NetBSD: mtab_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/mtab/mtab_linux.c
39 *
40 */
41
42 /* This file was adapted by Red Hat for Linux from mtab_file.c */
43
44 /*
45 * The locking code must be kept in sync with that used
46 * by the mount command in util-linux, otherwise you'll
47 * end with with race conditions leading to a corrupt
48 * /etc/mtab, particularly when AutoFS is used on same
49 * machine as AMD.
50 */
51
52 #ifdef HAVE_CONFIG_H
53 # include <config.h>
54 #endif /* HAVE_CONFIG_H */
55 #include <am_defs.h>
56 #include <amu.h>
57
58 #define NFILE_RETRIES 10 /* number of retries (seconds) */
59 #define LOCK_TIMEOUT 10
60
61 #ifdef MOUNT_TABLE_ON_FILE
62
63 # define PROC_MOUNTS "/proc/mounts"
64
65 static FILE *mnt_file = NULL;
66 /* Information about mtab. ------------------------------------*/
67 static int have_mtab_info = 0;
68 static int var_mtab_does_not_exist = 0;
69 static int var_mtab_is_a_symlink = 0;
70 /* Flag for already existing lock file. */
71 static int we_created_lockfile = 0;
72 static int lockfile_fd = -1;
73
74
75 static void
get_mtab_info(void)76 get_mtab_info(void)
77 {
78 struct stat mtab_stat;
79
80 if (!have_mtab_info) {
81 if (lstat(MOUNTED, &mtab_stat))
82 var_mtab_does_not_exist = 1;
83 else if (S_ISLNK(mtab_stat.st_mode))
84 var_mtab_is_a_symlink = 1;
85 have_mtab_info = 1;
86 }
87 }
88
89
90 static int
mtab_is_a_symlink(void)91 mtab_is_a_symlink(void)
92 {
93 get_mtab_info();
94 return var_mtab_is_a_symlink;
95 }
96
97
98 static int
mtab_is_writable()99 mtab_is_writable()
100 {
101 static int ret = -1;
102
103 /*
104 * Should we write to /etc/mtab upon an update? Probably not if it is a
105 * symlink to /proc/mounts, since that would create a file /proc/mounts in
106 * case the proc filesystem is not mounted.
107 */
108 if (mtab_is_a_symlink())
109 return 0;
110
111 if (ret == -1) {
112 int fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
113 if (fd >= 0) {
114 close(fd);
115 ret = 1;
116 } else
117 ret = 0;
118 }
119 return ret;
120 }
121
122
123 static void
setlkw_timeout(int sig)124 setlkw_timeout(int sig)
125 {
126 /* nothing, fcntl will fail anyway */
127 }
128
129
130 /*
131 * Create the lock file.
132 * The lock file will be removed if we catch a signal or when we exit.
133 *
134 * The old code here used flock on a lock file /etc/mtab~ and deleted
135 * this lock file afterwards. However, as rgooch remarks, that has a
136 * race: a second mount may be waiting on the lock and proceed as
137 * soon as the lock file is deleted by the first mount, and immediately
138 * afterwards a third mount comes, creates a new /etc/mtab~, applies
139 * flock to that, and also proceeds, so that the second and third mount
140 * now both are scribbling in /etc/mtab.
141 * The new code uses a link() instead of a creat(), where we proceed
142 * only if it was us that created the lock, and hence we always have
143 * to delete the lock afterwards. Now the use of flock() is in principle
144 * superfluous, but avoids an arbitrary sleep().
145 */
146
147 /*
148 * Where does the link point to? Obvious choices are mtab and mtab~~.
149 * HJLu points out that the latter leads to races. Right now we use
150 * mtab~.<pid> instead.
151 */
152 #define MOUNTED_LOCK "/etc/mtab~"
153 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
154
155 int
lock_mtab(void)156 lock_mtab(void)
157 {
158 int tries = 100000, i;
159 char *linktargetfile;
160 size_t l;
161 int rc = 1;
162
163 /*
164 * Redhat's original code set a signal handler called "handler()" for all
165 * non-ALRM signals. The handler called unlock_mntlist(), plog'ed the
166 * signal name, and then exit(1)! Never, ever, exit() from inside a
167 * utility function. This messed up Amd's careful signal-handling code,
168 * and caused Amd to abort uncleanly only any other "innocent" signal
169 * (even simple SIGUSR1), leaving behind a hung Amd mnt point. That code
170 * should have at least restored the signal handlers' states upon a
171 * successful mtab unlocking. Anyway, that handler was unnecessary,
172 * because will call unlock_mntlist() properly anyway on exit.
173 */
174 setup_sighandler(SIGALRM, setlkw_timeout);
175
176 /* somewhat clumsy, but some ancient systems do not have snprintf() */
177 /* use 20 as upper bound for the length of %d output */
178 l = strlen(MOUNTLOCK_LINKTARGET) + 20;
179 linktargetfile = xmalloc(l);
180 xsnprintf(linktargetfile, l, MOUNTLOCK_LINKTARGET, getpid());
181
182 i = open(linktargetfile, O_WRONLY|O_CREAT, 0);
183 if (i < 0) {
184 int errsv = errno;
185 /*
186 * linktargetfile does not exist (as a file) and we cannot create
187 * it. Read-only filesystem? Too many files open in the system?
188 * Filesystem full?
189 */
190 plog(XLOG_ERROR, "%s: can't create lock file %s: %s "
191 "(use -n flag to override)", __func__,
192 linktargetfile, strerror(errsv));
193 goto error;
194 }
195 close(i);
196
197
198 /* Repeat until it was us who made the link */
199 while (!we_created_lockfile) {
200 struct flock flock;
201 int errsv, j;
202
203 j = link(linktargetfile, MOUNTED_LOCK);
204 errsv = errno;
205
206 if (j < 0 && errsv != EEXIST) {
207 (void) unlink(linktargetfile);
208 plog(XLOG_ERROR, "can't link lock file %s: %s ",
209 MOUNTED_LOCK, strerror(errsv));
210 rc = 0;
211 goto error;
212 }
213
214 lockfile_fd = open(MOUNTED_LOCK, O_WRONLY);
215 if (lockfile_fd < 0) {
216 int errsv = errno;
217 /* Strange... Maybe the file was just deleted? */
218 if (errno == ENOENT && tries-- > 0) {
219 if (tries % 200 == 0)
220 usleep(30);
221 continue;
222 }
223 (void) unlink(linktargetfile);
224 plog(XLOG_ERROR,"%s: can't open lock file %s: %s ", __func__,
225 MOUNTED_LOCK, strerror(errsv));
226 rc = 0;
227 goto error;
228 }
229
230 flock.l_type = F_WRLCK;
231 flock.l_whence = SEEK_SET;
232 flock.l_start = 0;
233 flock.l_len = 0;
234
235 if (j == 0) {
236 /* We made the link. Now claim the lock. */
237 if (fcntl(lockfile_fd, F_SETLK, &flock) == -1) {
238 int errsv = errno;
239 plog(XLOG_ERROR, "%s: Can't lock lock file %s: %s", __func__,
240 MOUNTED_LOCK, strerror(errsv));
241 /* proceed, since it was us who created the lockfile anyway */
242 }
243 we_created_lockfile = 1;
244 (void) unlink(linktargetfile);
245 } else {
246 static int tries = 0;
247
248 /* Someone else made the link. Wait. */
249 alarm(LOCK_TIMEOUT);
250
251 if (fcntl(lockfile_fd, F_SETLKW, &flock) == -1) {
252 int errsv = errno;
253 (void) unlink(linktargetfile);
254 plog(XLOG_ERROR, "%s: can't lock lock file %s: %s", __func__,
255 MOUNTED_LOCK, (errno == EINTR) ?
256 "timed out" : strerror(errsv));
257 rc = 0;
258 goto error;
259 }
260 alarm(0);
261 /*
262 * Limit the number of iterations - maybe there
263 * still is some old /etc/mtab~
264 */
265 ++tries;
266 if (tries % 200 == 0)
267 usleep(30);
268 if (tries > 100000) {
269 (void) unlink(linktargetfile);
270 close(lockfile_fd);
271 plog(XLOG_ERROR,
272 "%s: Cannot create link %s; Perhaps there is a stale lock file?",
273 __func__, MOUNTED_LOCK);
274 rc = 0;
275 goto error;
276 }
277 close(lockfile_fd);
278 }
279 }
280
281 error:
282 XFREE(linktargetfile);
283
284 return rc;
285 }
286
287
288 static FILE *
open_locked_mtab(const char * mnttabname,char * mode,char * fs)289 open_locked_mtab(const char *mnttabname, char *mode, char *fs)
290 {
291 FILE *mfp = NULL;
292
293 if (mnt_file) {
294 dlog("Forced close on %s in read_mtab", mnttabname);
295 endmntent(mnt_file);
296 mnt_file = NULL;
297 }
298
299 if (!mtab_is_a_symlink() &&
300 !lock_mtab()) {
301 plog(XLOG_ERROR, "%s: Couldn't lock mtab", __func__);
302 return 0;
303 }
304
305 mfp = setmntent((char *)mnttabname, mode);
306 if (!mfp) {
307 plog(XLOG_ERROR, "%s: setmntent(\"%s\", \"%s\"): %m", __func__, mnttabname,
308 mode);
309 return 0;
310 }
311 return mfp;
312 }
313
314
315 /*
316 * Unlock the mount table
317 */
318 void
unlock_mntlist(void)319 unlock_mntlist(void)
320 {
321 if (mnt_file || we_created_lockfile)
322 dlog("unlock_mntlist: releasing");
323 if (mnt_file) {
324 endmntent(mnt_file);
325 mnt_file = NULL;
326 }
327 if (we_created_lockfile) {
328 close(lockfile_fd);
329 lockfile_fd = -1;
330 unlink(MOUNTED_LOCK);
331 we_created_lockfile = 0;
332 }
333 }
334
335
336 /*
337 * Write out a mount list
338 */
339 void
rewrite_mtab(mntlist * mp,const char * mnttabname)340 rewrite_mtab(mntlist *mp, const char *mnttabname)
341 {
342 FILE *mfp;
343 int error = 0;
344 char tmpname[64];
345 int retries;
346 int tmpfd;
347 char *cp;
348 char mcp[128];
349
350 if (!mtab_is_writable()) {
351 return;
352 }
353
354 /*
355 * Concoct a temporary name in the same directory as the target mount
356 * table so that rename() will work.
357 */
358 xstrlcpy(mcp, mnttabname, sizeof(mcp));
359 cp = strrchr(mcp, '/');
360 if (cp) {
361 memmove(tmpname, mcp, cp - mcp);
362 tmpname[cp - mcp] = '\0';
363 } else {
364 plog(XLOG_WARNING, "No '/' in mtab (%s), using \".\" as tmp directory", mnttabname);
365 tmpname[0] = '.';
366 tmpname[1] = '\0';
367 }
368 xstrlcat(tmpname, "/mtabXXXXXX", sizeof(tmpname));
369 retries = 0;
370 enfile1:
371 #ifdef HAVE_MKSTEMP
372 tmpfd = mkstemp(tmpname);
373 fchmod(tmpfd, 0644);
374 #else /* not HAVE_MKSTEMP */
375 mktemp(tmpname);
376 tmpfd = open(tmpname, O_RDWR | O_CREAT | O_TRUNC, 0644);
377 #endif /* not HAVE_MKSTEMP */
378 if (tmpfd < 0) {
379 if (errno == ENFILE && retries++ < NFILE_RETRIES) {
380 sleep(1);
381 goto enfile1;
382 }
383 plog(XLOG_ERROR, "%s: open: %m", tmpname);
384 return;
385 }
386 if (close(tmpfd) < 0)
387 plog(XLOG_ERROR, "%s: Couldn't close tmp file descriptor: %m", __func__);
388
389 retries = 0;
390 enfile2:
391 mfp = setmntent(tmpname, "w");
392 if (!mfp) {
393 if (errno == ENFILE && retries++ < NFILE_RETRIES) {
394 sleep(1);
395 goto enfile2;
396 }
397 plog(XLOG_ERROR, "%s: setmntent(\"%s\", \"w\"): %m", __func__, tmpname);
398 error = 1;
399 goto out;
400 }
401 while (mp) {
402 if (mp->mnt) {
403 if (addmntent(mfp, mp->mnt)) {
404 plog(XLOG_ERROR, "%s: Can't write entry to %s", __func__, tmpname);
405 error = 1;
406 goto out;
407 }
408 }
409 mp = mp->mnext;
410 }
411
412 /*
413 * SunOS 4.1 manuals say that the return code from entmntent()
414 * is always 1 and to treat as a void. That means we need to
415 * call fflush() to make sure the new mtab file got written.
416 */
417 if (fflush(mfp)) {
418 plog(XLOG_ERROR, "flush new mtab file: %m");
419 error = 1;
420 goto out;
421 }
422 (void) endmntent(mfp);
423
424 /*
425 * Rename temporary mtab to real mtab
426 */
427 if (rename(tmpname, mnttabname) < 0) {
428 plog(XLOG_ERROR, "rename %s to %s: %m", tmpname, mnttabname);
429 error = 1;
430 goto out;
431 }
432 out:
433 if (error)
434 (void) unlink(tmpname);
435 }
436
437
438 static void
mtab_stripnl(char * s)439 mtab_stripnl(char *s)
440 {
441 do {
442 s = strchr(s, '\n');
443 if (s)
444 *s++ = ' ';
445 } while (s);
446 }
447
448
449 /*
450 * Append a mntent structure to the
451 * current mount table.
452 */
453 void
write_mntent(mntent_t * mp,const char * mnttabname)454 write_mntent(mntent_t *mp, const char *mnttabname)
455 {
456 int retries = 0;
457 FILE *mfp;
458
459 if (!mtab_is_writable()) {
460 return;
461 }
462
463 enfile:
464 mfp = open_locked_mtab(mnttabname, "a", mp->mnt_dir);
465 if (mfp) {
466 mtab_stripnl(mp->mnt_opts);
467 if (addmntent(mfp, mp))
468 plog(XLOG_ERROR, "%s: Couldn't write %s: %m", __func__, mnttabname);
469 if (fflush(mfp))
470 plog(XLOG_ERROR, "%s: Couldn't flush %s: %m", __func__, mnttabname);
471 (void) endmntent(mfp);
472 } else {
473 if (errno == ENFILE && retries < NFILE_RETRIES) {
474 sleep(1);
475 goto enfile;
476 }
477 plog(XLOG_ERROR, "%s: setmntent(\"%s\", \"a\"): %m", __func__, mnttabname);
478 }
479
480 unlock_mntlist();
481 }
482
483 #endif /* MOUNT_TABLE_ON_FILE */
484
485
486 static mntent_t *
mnt_dup(mntent_t * mp)487 mnt_dup(mntent_t *mp)
488 {
489 mntent_t *new_mp = ALLOC(mntent_t);
490
491 new_mp->mnt_fsname = xstrdup(mp->mnt_fsname);
492 new_mp->mnt_dir = xstrdup(mp->mnt_dir);
493 new_mp->mnt_type = xstrdup(mp->mnt_type);
494 new_mp->mnt_opts = xstrdup(mp->mnt_opts);
495
496 new_mp->mnt_freq = mp->mnt_freq;
497 new_mp->mnt_passno = mp->mnt_passno;
498
499 #ifdef HAVE_MNTENT_T_MNT_TIME
500 # ifdef HAVE_MNTENT_T_MNT_TIME_STRING
501 new_mp->mnt_time = xstrdup(mp->mnt_time);
502 # else /* not HAVE_MNTENT_T_MNT_TIME_STRING */
503 new_mp->mnt_time = mp->mnt_time;
504 # endif /* not HAVE_MNTENT_T_MNT_TIME_STRING */
505 #endif /* HAVE_MNTENT_T_MNT_TIME */
506
507 #ifdef HAVE_MNTENT_T_MNT_CNODE
508 new_mp->mnt_cnode = mp->mnt_cnode;
509 #endif /* HAVE_MNTENT_T_MNT_CNODE */
510
511 return new_mp;
512 }
513
514
515 /*
516 * Read a mount table into memory
517 */
518 mntlist *
read_mtab(char * fs,const char * mnttabname)519 read_mtab(char *fs, const char *mnttabname)
520 {
521 mntlist **mpp, *mhp;
522
523 mntent_t *mep;
524
525 FILE *mfp = open_locked_mtab(mnttabname, "r+", fs);
526
527 if (!mfp)
528 return 0;
529
530 mpp = &mhp;
531
532 /*
533 * XXX - In SunOS 4 there is (yet another) memory leak
534 * which loses 1K the first time getmntent is called.
535 * (jsp)
536 */
537 while ((mep = getmntent(mfp))) {
538 /*
539 * Allocate a new slot
540 */
541 *mpp = ALLOC(struct mntlist);
542
543 /*
544 * Copy the data returned by getmntent
545 */
546 (*mpp)->mnt = mnt_dup(mep);
547
548 /*
549 * Move to next pointer
550 */
551 mpp = &(*mpp)->mnext;
552 }
553 *mpp = NULL;
554
555 #ifdef MOUNT_TABLE_ON_FILE
556 /*
557 * If we are not updating the mount table then we
558 * can free the resources held here, otherwise they
559 * must be held until the mount table update is complete
560 */
561 mnt_file = mfp;
562 #else /* not MOUNT_TABLE_ON_FILE */
563 endmntent(mfp);
564 #endif /* not MOUNT_TABLE_ON_FILE */
565
566 return mhp;
567 }
568