]> git.ipfire.org Git - thirdparty/util-linux.git/blob - mount/fstab.c
Imported from util-linux-2.10s tarball.
[thirdparty/util-linux.git] / mount / fstab.c
1 /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
2 * - added Native Language Support
3 * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
4 * - fixed strerr(errno) in gettext calls
5 */
6
7 #include <unistd.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/stat.h>
12 #include "mntent.h"
13 #include "fstab.h"
14 #include "sundries.h" /* for xmalloc() etc */
15 #include "nls.h"
16
17 #define streq(s, t) (strcmp ((s), (t)) == 0)
18
19 #define PROC_MOUNTS "/proc/mounts"
20
21
22 /* Information about mtab. ------------------------------------*/
23 static int have_mtab_info = 0;
24 static int var_mtab_does_not_exist = 0;
25 static int var_mtab_is_a_symlink = 0;
26
27 static void
28 get_mtab_info(void) {
29 struct stat mtab_stat;
30
31 if (!have_mtab_info) {
32 if (lstat(MOUNTED, &mtab_stat))
33 var_mtab_does_not_exist = 1;
34 else if (S_ISLNK(mtab_stat.st_mode))
35 var_mtab_is_a_symlink = 1;
36 have_mtab_info = 1;
37 }
38 }
39
40 int
41 mtab_does_not_exist(void) {
42 get_mtab_info();
43 return var_mtab_does_not_exist;
44 }
45
46 int
47 mtab_is_a_symlink(void) {
48 get_mtab_info();
49 return var_mtab_is_a_symlink;
50 }
51
52 int
53 mtab_is_writable() {
54 static int ret = -1;
55
56 /* Should we write to /etc/mtab upon an update?
57 Probably not if it is a symlink to /proc/mounts, since that
58 would create a file /proc/mounts in case the proc filesystem
59 is not mounted. */
60 if (mtab_is_a_symlink())
61 return 0;
62
63 if (ret == -1) {
64 int fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
65 if (fd >= 0) {
66 close(fd);
67 ret = 1;
68 } else
69 ret = 0;
70 }
71 return ret;
72 }
73
74 /* Contents of mtab and fstab ---------------------------------*/
75
76 struct mntentchn mounttable, fstab;
77 static int got_mtab = 0;
78 static int got_fstab = 0;
79
80 static void read_mounttable(void), read_fstab(void);
81
82 struct mntentchn *
83 mtab_head() {
84 if (!got_mtab)
85 read_mounttable();
86 return &mounttable;
87 }
88
89 struct mntentchn *
90 fstab_head() {
91 if (!got_fstab)
92 read_fstab();
93 return &fstab;
94 }
95
96 static void
97 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
98 struct mntentchn *mc = mc0;
99 struct mntent *mnt;
100
101 while ((mnt = my_getmntent (mfp)) != NULL) {
102 if (!streq (mnt->mnt_type, MNTTYPE_IGNORE)) {
103 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
104 mc->nxt->prev = mc;
105 mc = mc->nxt;
106 mc->m = *mnt;
107 mc->nxt = mc0;
108 }
109 }
110 mc0->prev = mc;
111 if (ferror (mfp->mntent_fp)) {
112 int errsv = errno;
113 error(_("warning: error reading %s: %s"),
114 fnam, strerror (errsv));
115 mc0->nxt = mc0->prev = NULL;
116 }
117 my_endmntent(mfp);
118 }
119
120 /*
121 * Read /etc/mtab. If that fails, try /proc/mounts.
122 * This produces a linked list. The list head mounttable is a dummy.
123 * Return 0 on success.
124 */
125 static void
126 read_mounttable() {
127 mntFILE *mfp;
128 const char *fnam;
129 struct mntentchn *mc = &mounttable;
130
131 got_mtab = 1;
132 mc->nxt = mc->prev = NULL;
133
134 fnam = MOUNTED;
135 mfp = my_setmntent (fnam, "r");
136 if (mfp == NULL || mfp->mntent_fp == NULL) {
137 int errsv = errno;
138 fnam = PROC_MOUNTS;
139 mfp = my_setmntent (fnam, "r");
140 if (mfp == NULL || mfp->mntent_fp == NULL) {
141 error(_("warning: can't open %s: %s"), MOUNTED, strerror (errsv));
142 return;
143 }
144 if (verbose)
145 printf (_("mount: could not open %s - using %s instead\n"),
146 MOUNTED, PROC_MOUNTS);
147 }
148 read_mntentchn(mfp, fnam, mc);
149 }
150
151 static void
152 read_fstab() {
153 mntFILE *mfp = NULL;
154 const char *fnam;
155 struct mntentchn *mc = &fstab;
156
157 got_fstab = 1;
158 mc->nxt = mc->prev = NULL;
159
160 fnam = _PATH_FSTAB;
161 mfp = my_setmntent (fnam, "r");
162 if (mfp == NULL || mfp->mntent_fp == NULL) {
163 int errsv = errno;
164 error(_("warning: can't open %s: %s"),
165 _PATH_FSTAB, strerror (errsv));
166 return;
167 }
168 read_mntentchn(mfp, fnam, mc);
169 }
170
171
172 /* Given the name NAME, try to find it in mtab. */
173 struct mntentchn *
174 getmntfile (const char *name) {
175 struct mntentchn *mc, *mc0;
176
177 mc0 = mtab_head();
178 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
179 if (streq (mc->m.mnt_dir, name) ||
180 streq (mc->m.mnt_fsname, name))
181 return mc;
182 return NULL;
183 }
184
185 /*
186 * Given the name NAME, and the place MCPREV we found it last time,
187 * try to find more occurrences.
188 */
189 struct mntentchn *
190 getmntfilesbackward (const char *name, struct mntentchn *mcprev) {
191 struct mntentchn *mc, *mc0;
192
193 mc0 = mtab_head();
194 if (!mcprev)
195 mcprev = mc0;
196 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
197 if (streq (mc->m.mnt_dir, name) ||
198 streq (mc->m.mnt_fsname, name))
199 return mc;
200 return NULL;
201 }
202
203 /* Given the name FILE, try to find the option "loop=FILE" in mtab. */
204 struct mntentchn *
205 getmntoptfile (const char *file) {
206 struct mntentchn *mc, *mc0;
207 char *opts, *s;
208 int l;
209
210 if (!file)
211 return NULL;
212
213 l = strlen(file);
214
215 mc0 = mtab_head();
216 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
217 if ((opts = mc->m.mnt_opts) != NULL
218 && (s = strstr(opts, "loop="))
219 && !strncmp(s+5, file, l)
220 && (s == opts || s[-1] == ',')
221 && (s[l+5] == 0 || s[l+5] == ','))
222 return mc;
223 return NULL;
224 }
225
226 /* Find the entry (SPEC,FILE) in fstab */
227 struct mntentchn *
228 getfsspecfile (const char *spec, const char *file) {
229 struct mntentchn *mc, *mc0;
230
231 mc0 = fstab_head();
232 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
233 if (streq (mc->m.mnt_dir, file) &&
234 streq (mc->m.mnt_fsname, spec))
235 return mc;
236 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
237 if ((streq (mc->m.mnt_dir, file) ||
238 streq (canonicalize(mc->m.mnt_dir), file))
239 && (streq (mc->m.mnt_fsname, spec) ||
240 streq (canonicalize(mc->m.mnt_fsname), spec)))
241 return mc;
242 return NULL;
243 }
244
245 /* Find the dir FILE in fstab. */
246 struct mntentchn *
247 getfsfile (const char *file) {
248 struct mntentchn *mc, *mc0;
249
250 mc0 = fstab_head();
251 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
252 if (streq (mc->m.mnt_dir, file))
253 return mc;
254 return NULL;
255 }
256
257 /* Find the device SPEC in fstab. */
258 struct mntentchn *
259 getfsspec (const char *spec) {
260 struct mntentchn *mc, *mc0;
261
262 mc0 = fstab_head();
263 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
264 if (streq (mc->m.mnt_fsname, spec))
265 return mc;
266 return NULL;
267 }
268
269 /* Find the uuid UUID in fstab. */
270 struct mntentchn *
271 getfsuuidspec (const char *uuid) {
272 struct mntentchn *mc, *mc0;
273
274 mc0 = fstab_head();
275 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
276 if (strncmp (mc->m.mnt_fsname, "UUID=", 5) == 0
277 && streq(mc->m.mnt_fsname + 5, uuid))
278 return mc;
279 return NULL;
280 }
281
282 /* Find the label LABEL in fstab. */
283 struct mntentchn *
284 getfsvolspec (const char *label) {
285 struct mntentchn *mc, *mc0;
286
287 mc0 = fstab_head();
288 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
289 if (strncmp (mc->m.mnt_fsname, "LABEL=", 6) == 0
290 && streq(mc->m.mnt_fsname + 6, label))
291 return mc;
292 return NULL;
293 }
294
295 /* Updating mtab ----------------------------------------------*/
296
297 /* Flag for already existing lock file. */
298 static int we_created_lockfile = 0;
299
300 /* Flag to indicate that signals have been set up. */
301 static int signals_have_been_setup = 0;
302
303 /* Ensure that the lock is released if we are interrupted. */
304 static void
305 handler (int sig) {
306 die (EX_USER, "%s", sys_siglist[sig]);
307 }
308
309 static void
310 setlkw_timeout (int sig) {
311 /* nothing, fcntl will fail anyway */
312 }
313
314 /* Create the lock file.
315 The lock file will be removed if we catch a signal or when we exit. */
316 /* The old code here used flock on a lock file /etc/mtab~ and deleted
317 this lock file afterwards. However, as rgooch remarks, that has a
318 race: a second mount may be waiting on the lock and proceed as
319 soon as the lock file is deleted by the first mount, and immediately
320 afterwards a third mount comes, creates a new /etc/mtab~, applies
321 flock to that, and also proceeds, so that the second and third mount
322 now both are scribbling in /etc/mtab.
323 The new code uses a link() instead of a creat(), where we proceed
324 only if it was us that created the lock, and hence we always have
325 to delete the lock afterwards. Now the use of flock() is in principle
326 superfluous, but avoids an arbitrary sleep(). */
327
328 /* Where does the link point to? Obvious choices are mtab and mtab~~.
329 HJLu points out that the latter leads to races. Right now we use
330 mtab~.<pid> instead. */
331 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
332
333 void
334 lock_mtab (void) {
335 int tries = 3;
336 char *linktargetfile;
337
338 if (!signals_have_been_setup) {
339 int sig = 0;
340 struct sigaction sa;
341
342 sa.sa_handler = handler;
343 sa.sa_flags = 0;
344 sigfillset (&sa.sa_mask);
345
346 while (sigismember (&sa.sa_mask, ++sig) != -1
347 && sig != SIGCHLD) {
348 if (sig == SIGALRM)
349 sa.sa_handler = setlkw_timeout;
350 else
351 sa.sa_handler = handler;
352 sigaction (sig, &sa, (struct sigaction *) 0);
353 }
354 signals_have_been_setup = 1;
355 }
356
357 /* somewhat clumsy, but some ancient systems do not have snprintf() */
358 /* use 20 as upper bound for the length of %d output */
359 linktargetfile = xmalloc(strlen(MOUNTLOCK_LINKTARGET) + 20);
360 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
361
362 /* Repeat until it was us who made the link */
363 while (!we_created_lockfile) {
364 struct flock flock;
365 int errsv, fd, i, j;
366
367 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
368 if (i < 0) {
369 int errsv = errno;
370 /* linktargetfile does not exist (as a file)
371 and we cannot create it. Read-only filesystem?
372 Too many files open in the system?
373 Filesystem full? */
374 die (EX_FILEIO, _("can't create lock file %s: %s "
375 "(use -n flag to override)"),
376 linktargetfile, strerror (errsv));
377 }
378 close(i);
379
380 j = link(linktargetfile, MOUNTED_LOCK);
381 errsv = errno;
382
383 (void) unlink(linktargetfile);
384
385 if (j < 0 && errsv != EEXIST) {
386 die (EX_FILEIO, _("can't link lock file %s: %s "
387 "(use -n flag to override)"),
388 MOUNTED_LOCK, strerror (errsv));
389 }
390
391 fd = open (MOUNTED_LOCK, O_WRONLY);
392
393 if (fd < 0) {
394 int errsv = errno;
395 /* Strange... Maybe the file was just deleted? */
396 if (errno == ENOENT && tries-- > 0)
397 continue;
398 die (EX_FILEIO, _("can't open lock file %s: %s "
399 "(use -n flag to override)"),
400 MOUNTED_LOCK, strerror (errsv));
401 }
402
403 flock.l_type = F_WRLCK;
404 flock.l_whence = SEEK_SET;
405 flock.l_start = 0;
406 flock.l_len = 0;
407
408 if (j == 0) {
409 /* We made the link. Now claim the lock. */
410 if (fcntl (fd, F_SETLK, &flock) == -1) {
411 if (verbose) {
412 int errsv = errno;
413 printf(_("Can't lock lock file %s: %s\n"),
414 MOUNTED_LOCK, strerror (errsv));
415 }
416 /* proceed anyway */
417 }
418 we_created_lockfile = 1;
419 } else {
420 static int tries = 0;
421
422 /* Someone else made the link. Wait. */
423 alarm(LOCK_TIMEOUT);
424 if (fcntl (fd, F_SETLKW, &flock) == -1) {
425 int errsv = errno;
426 die (EX_FILEIO, _("can't lock lock file %s: %s"),
427 MOUNTED_LOCK, (errno == EINTR) ?
428 _("timed out") : strerror (errsv));
429 }
430 alarm(0);
431 /* Limit the number of iterations - maybe there
432 still is some old /etc/mtab~ */
433 if (tries++ > 3) {
434 if (tries > 5)
435 die (EX_FILEIO, _("Cannot create link %s\n"
436 "Perhaps there is a stale lock file?\n"),
437 MOUNTED_LOCK);
438 sleep(1);
439 }
440 }
441
442 close(fd);
443 }
444 }
445
446 /* Remove lock file. */
447 void
448 unlock_mtab (void) {
449 if (we_created_lockfile) {
450 unlink (MOUNTED_LOCK);
451 we_created_lockfile = 0;
452 }
453 }
454
455 /*
456 * Update the mtab.
457 * Used by umount with null INSTEAD: remove the last DIR entry.
458 * Used by mount upon a remount: update option part,
459 * and complain if a wrong device or type was given.
460 * [Note that often a remount will be a rw remount of /
461 * where there was no entry before, and we'll have to believe
462 * the values given in INSTEAD.]
463 */
464
465 void
466 update_mtab (const char *dir, struct mntent *instead) {
467 mntFILE *mfp, *mftmp;
468 const char *fnam = MOUNTED;
469 struct mntentchn mtabhead; /* dummy */
470 struct mntentchn *mc, *mc0, absent;
471
472 if (mtab_does_not_exist() || mtab_is_a_symlink())
473 return;
474
475 lock_mtab();
476
477 /* having locked mtab, read it again */
478 mc0 = mc = &mtabhead;
479 mc->nxt = mc->prev = NULL;
480
481 mfp = my_setmntent(fnam, "r");
482 if (mfp == NULL || mfp->mntent_fp == NULL) {
483 int errsv = errno;
484 error (_("cannot open %s (%s) - mtab not updated"),
485 fnam, strerror (errsv));
486 goto leave;
487 }
488
489 read_mntentchn(mfp, fnam, mc);
490
491 /* find last occurrence of dir */
492 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
493 if (streq (mc->m.mnt_dir, dir))
494 break;
495 if (mc && mc != mc0) {
496 if (instead == NULL) {
497 /* An umount - remove entry */
498 if (mc && mc != mc0) {
499 mc->prev->nxt = mc->nxt;
500 mc->nxt->prev = mc->prev;
501 }
502 } else {
503 /* A remount */
504 mc->m.mnt_opts = instead->mnt_opts;
505 }
506 } else if (instead) {
507 /* not found, add a new entry */
508 absent.m = *instead;
509 absent.nxt = mc0;
510 absent.prev = mc0->prev;
511 mc0->prev = &absent;
512 if (mc0->nxt == NULL)
513 mc0->nxt = &absent;
514 }
515
516 /* write chain to mtemp */
517 mftmp = my_setmntent (MOUNTED_TEMP, "w");
518 if (mftmp == NULL || mfp->mntent_fp == NULL) {
519 int errsv = errno;
520 error (_("cannot open %s (%s) - mtab not updated"),
521 MOUNTED_TEMP, strerror (errsv));
522 goto leave;
523 }
524
525 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
526 if (my_addmntent(mftmp, &(mc->m)) == 1) {
527 int errsv = errno;
528 die (EX_FILEIO, _("error writing %s: %s"),
529 MOUNTED_TEMP, strerror (errsv));
530 }
531 }
532
533 if (fchmod (fileno (mftmp->mntent_fp),
534 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
535 int errsv = errno;
536 fprintf(stderr, _("error changing mode of %s: %s\n"),
537 MOUNTED_TEMP, strerror (errsv));
538 }
539 my_endmntent (mftmp);
540
541 /* rename mtemp to mtab */
542 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
543 int errsv = errno;
544 fprintf(stderr, _("can't rename %s to %s: %s\n"),
545 MOUNTED_TEMP, MOUNTED, strerror(errsv));
546 }
547
548 leave:
549 unlock_mtab();
550 }