]> git.ipfire.org Git - thirdparty/util-linux.git/blob - mount/fstab.c
Imported from util-linux-2.9v 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->mnt_fsname = mnt->mnt_fsname;
107 mc->mnt_dir = mnt->mnt_dir;
108 mc->mnt_type = mnt->mnt_type;
109 mc->mnt_opts = mnt->mnt_opts;
110 mc->nxt = NULL;
111 }
112 }
113 mc0->prev = mc;
114 if (ferror (mfp->mntent_fp)) {
115 int errsv = errno;
116 error(_("warning: error reading %s: %s"), fnam, strerror (errsv));
117 mc0->nxt = mc0->prev = NULL;
118 }
119 my_endmntent(mfp);
120 }
121
122 /*
123 * Read /etc/mtab. If that fails, try /proc/mounts.
124 * This produces a linked list. The list head mounttable is a dummy.
125 * Return 0 on success.
126 */
127 static void
128 read_mounttable() {
129 mntFILE *mfp;
130 const char *fnam;
131 struct mntentchn *mc = &mounttable;
132
133 got_mtab = 1;
134 mc->nxt = mc->prev = NULL;
135
136 fnam = MOUNTED;
137 mfp = my_setmntent (fnam, "r");
138 if (mfp == NULL || mfp->mntent_fp == NULL) {
139 int errsv = errno;
140 fnam = PROC_MOUNTS;
141 mfp = my_setmntent (fnam, "r");
142 if (mfp == NULL || mfp->mntent_fp == NULL) {
143 error(_("warning: can't open %s: %s"), MOUNTED, strerror (errsv));
144 return;
145 }
146 if (verbose)
147 printf (_("mount: could not open %s - using %s instead\n"),
148 MOUNTED, PROC_MOUNTS);
149 }
150 read_mntentchn(mfp, fnam, mc);
151 }
152
153 static void
154 read_fstab() {
155 mntFILE *mfp = NULL;
156 const char *fnam;
157 struct mntentchn *mc = &fstab;
158
159 got_fstab = 1;
160 mc->nxt = mc->prev = NULL;
161
162 fnam = _PATH_FSTAB;
163 mfp = my_setmntent (fnam, "r");
164 if (mfp == NULL || mfp->mntent_fp == NULL) {
165 int errsv = errno;
166 error(_("warning: can't open %s: %s"), _PATH_FSTAB, strerror (errsv));
167 return;
168 }
169 read_mntentchn(mfp, fnam, mc);
170 }
171
172
173 /* Given the name NAME, try to find it in mtab. */
174 struct mntentchn *
175 getmntfile (const char *name) {
176 struct mntentchn *mc;
177
178 for (mc = mtab_head()->nxt; mc; mc = mc->nxt)
179 if (streq (mc->mnt_dir, name) || (streq (mc->mnt_fsname, name)))
180 break;
181
182 return mc;
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, *mh;
192
193 mh = mtab_head();
194 if (!mcprev)
195 mcprev = mh;
196 for (mc = mcprev->prev; mc && mc != mh; mc = mc->prev)
197 if (streq (mc->mnt_dir, name) || (streq (mc->mnt_fsname, name)))
198 return mc;
199
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 {
207 struct mntentchn *mc;
208 char *opts, *s;
209 int l;
210
211 if (!file)
212 return NULL;
213
214 l = strlen(file);
215
216 for (mc = mtab_head()->nxt; mc; mc = mc->nxt)
217 if ((opts = mc->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
224 return NULL;
225 }
226
227 /* Find the dir FILE in fstab. */
228 struct mntentchn *
229 getfsfile (const char *file) {
230 struct mntentchn *mc;
231
232 for (mc = fstab_head()->nxt; mc; mc = mc->nxt)
233 if (streq (mc->mnt_dir, file))
234 break;
235
236 return mc;
237 }
238
239 /* Find the device SPEC in fstab. */
240 struct mntentchn *
241 getfsspec (const char *spec)
242 {
243 struct mntentchn *mc;
244
245 for (mc = fstab_head()->nxt; mc; mc = mc->nxt)
246 if (streq (mc->mnt_fsname, spec))
247 break;
248
249 return mc;
250 }
251
252 /* Find the uuid UUID in fstab. */
253 struct mntentchn *
254 getfsuuidspec (const char *uuid)
255 {
256 struct mntentchn *mc;
257
258 for (mc = fstab_head()->nxt; mc; mc = mc->nxt)
259 if (strncmp (mc->mnt_fsname, "UUID=", 5) == 0
260 && streq(mc->mnt_fsname + 5, uuid))
261 break;
262
263 return mc;
264 }
265
266 /* Find the label LABEL in fstab. */
267 struct mntentchn *
268 getfsvolspec (const char *label)
269 {
270 struct mntentchn *mc;
271
272 for (mc = fstab_head()->nxt; mc; mc = mc->nxt)
273 if (strncmp (mc->mnt_fsname, "LABEL=", 6) == 0
274 && streq(mc->mnt_fsname + 6, label))
275 break;
276
277 return mc;
278 }
279
280 /* Updating mtab ----------------------------------------------*/
281
282 /* Flag for already existing lock file. */
283 static int we_created_lockfile = 0;
284
285 /* Flag to indicate that signals have been set up. */
286 static int signals_have_been_setup = 0;
287
288 /* Ensure that the lock is released if we are interrupted. */
289 static void
290 handler (int sig) {
291 die (EX_USER, "%s", sys_siglist[sig]);
292 }
293
294 static void
295 setlkw_timeout (int sig) {
296 /* nothing, fcntl will fail anyway */
297 }
298
299 /* Create the lock file.
300 The lock file will be removed if we catch a signal or when we exit. */
301 /* The old code here used flock on a lock file /etc/mtab~ and deleted
302 this lock file afterwards. However, as rgooch remarks, that has a
303 race: a second mount may be waiting on the lock and proceed as
304 soon as the lock file is deleted by the first mount, and immediately
305 afterwards a third mount comes, creates a new /etc/mtab~, applies
306 flock to that, and also proceeds, so that the second and third mount
307 now both are scribbling in /etc/mtab.
308 The new code uses a link() instead of a creat(), where we proceed
309 only if it was us that created the lock, and hence we always have
310 to delete the lock afterwards. Now the use of flock() is in principle
311 superfluous, but avoids an arbitrary sleep(). */
312
313 /* Where does the link point to? Obvious choices are mtab and mtab~~.
314 HJLu points out that the latter leads to races. Right now we use
315 mtab~.<pid> instead. */
316 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
317
318 void
319 lock_mtab (void) {
320 int tries = 3;
321 char *linktargetfile;
322
323 if (!signals_have_been_setup) {
324 int sig = 0;
325 struct sigaction sa;
326
327 sa.sa_handler = handler;
328 sa.sa_flags = 0;
329 sigfillset (&sa.sa_mask);
330
331 while (sigismember (&sa.sa_mask, ++sig) != -1
332 && sig != SIGCHLD) {
333 if (sig == SIGALRM)
334 sa.sa_handler = setlkw_timeout;
335 else
336 sa.sa_handler = handler;
337 sigaction (sig, &sa, (struct sigaction *) 0);
338 }
339 signals_have_been_setup = 1;
340 }
341
342 /* somewhat clumsy, but some ancient systems do not have snprintf() */
343 /* use 20 as upper bound for the length of %d output */
344 linktargetfile = xmalloc(strlen(MOUNTLOCK_LINKTARGET) + 20);
345 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
346
347 /* Repeat until it was us who made the link */
348 while (!we_created_lockfile) {
349 struct flock flock;
350 int errsv, fd, i, j;
351
352 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
353 if (i < 0) {
354 int errsv = errno;
355 /* linktargetfile does not exist (as a file)
356 and we cannot create it. Read-only filesystem?
357 Too many files open in the system? Filesystem full? */
358 die (EX_FILEIO, _("can't create lock file %s: %s "
359 "(use -n flag to override)"),
360 linktargetfile, strerror (errsv));
361 }
362 close(i);
363
364 j = link(linktargetfile, MOUNTED_LOCK);
365 errsv = errno;
366
367 (void) unlink(linktargetfile);
368
369 if (j < 0 && errsv != EEXIST) {
370 die (EX_FILEIO, _("can't link lock file %s: %s "
371 "(use -n flag to override)"),
372 MOUNTED_LOCK, strerror (errsv));
373 }
374
375 fd = open (MOUNTED_LOCK, O_WRONLY);
376
377 if (fd < 0) {
378 int errsv = errno;
379 /* Strange... Maybe the file was just deleted? */
380 if (errno == ENOENT && tries-- > 0)
381 continue;
382 die (EX_FILEIO, _("can't open lock file %s: %s "
383 "(use -n flag to override)"),
384 MOUNTED_LOCK, strerror (errsv));
385 }
386
387 flock.l_type = F_WRLCK;
388 flock.l_whence = SEEK_SET;
389 flock.l_start = 0;
390 flock.l_len = 0;
391
392 if (j == 0) {
393 /* We made the link. Now claim the lock. */
394 if (fcntl (fd, F_SETLK, &flock) == -1) {
395 if (verbose) {
396 int errsv = errno;
397 printf(_("Can't lock lock file %s: %s\n"),
398 MOUNTED_LOCK, strerror (errsv));
399 }
400 /* proceed anyway */
401 }
402 we_created_lockfile = 1;
403 } else {
404 /* Someone else made the link. Wait. */
405 alarm(LOCK_TIMEOUT);
406 if (fcntl (fd, F_SETLKW, &flock) == -1) {
407 int errsv = errno;
408 die (EX_FILEIO, _("can't lock lock file %s: %s"),
409 MOUNTED_LOCK, (errno == EINTR) ?
410 _("timed out") : strerror (errsv));
411 }
412 alarm(0);
413 /* Maybe limit the number of iterations? */
414 }
415
416 close(fd);
417 }
418 }
419
420 /* Remove lock file. */
421 void
422 unlock_mtab (void) {
423 if (we_created_lockfile) {
424 unlink (MOUNTED_LOCK);
425 we_created_lockfile = 0;
426 }
427 }
428
429 /*
430 * Update the mtab.
431 * Used by umount with null INSTEAD: remove any DIR entries.
432 * Used by mount upon a remount: update option part,
433 * and complain if a wrong device or type was given.
434 * [Note that often a remount will be a rw remount of /
435 * where there was no entry before, and we'll have to believe
436 * the values given in INSTEAD.]
437 */
438
439 void
440 update_mtab (const char *dir, struct mntent *instead) {
441 struct mntent *mnt;
442 struct mntent *next;
443 struct mntent remnt;
444 int added = 0;
445 mntFILE *mfp, *mftmp;
446
447 if (mtab_does_not_exist() || mtab_is_a_symlink())
448 return;
449
450 lock_mtab();
451
452 mfp = my_setmntent(MOUNTED, "r");
453 if (mfp == NULL || mfp->mntent_fp == NULL) {
454 int errsv = errno;
455 error (_("cannot open %s (%s) - mtab not updated"),
456 MOUNTED, strerror (errsv));
457 goto leave;
458 }
459
460 mftmp = my_setmntent (MOUNTED_TEMP, "w");
461 if (mftmp == NULL || mfp->mntent_fp == NULL) {
462 int errsv = errno;
463 error (_("cannot open %s (%s) - mtab not updated"),
464 MOUNTED_TEMP, strerror (errsv));
465 goto leave;
466 }
467
468 while ((mnt = my_getmntent (mfp))) {
469 if (streq (mnt->mnt_dir, dir)
470 #if 0
471 /* Matthew Wilcox <willy@odie.barnet.ac.uk> */
472 /* This is meant for Patch 212 on Jitterbug,
473 still in incoming, to allow remounting
474 on a different directory. */
475 || (instead && instead->mnt_fsname &&
476 (!streq (instead->mnt_fsname, "none")) &&
477 (streq (mnt->mnt_fsname, instead->mnt_fsname)))
478 #endif
479 ) {
480 added++;
481 if (instead) { /* a remount */
482 remnt = *instead;
483 next = &remnt;
484 remnt.mnt_fsname = mnt->mnt_fsname;
485 remnt.mnt_type = mnt->mnt_type;
486 if (instead->mnt_fsname
487 && !streq(mnt->mnt_fsname, instead->mnt_fsname))
488 printf(_("mount: warning: cannot change "
489 "mounted device with a remount\n"));
490 else if (instead->mnt_type
491 && !streq(instead->mnt_type, "unknown")
492 && !streq(mnt->mnt_type, instead->mnt_type))
493 printf(_("mount: warning: cannot change "
494 "filesystem type with a remount\n"));
495 } else
496 next = NULL;
497 } else
498 next = mnt;
499 if (next && my_addmntent(mftmp, next) == 1) {
500 int errsv = errno;
501 die (EX_FILEIO, _("error writing %s: %s"),
502 MOUNTED_TEMP, strerror (errsv));
503 }
504 }
505 if (instead && !added && my_addmntent(mftmp, instead) == 1) {
506 int errsv = errno;
507 die (EX_FILEIO, _("error writing %s: %s"),
508 MOUNTED_TEMP, strerror (errsv));
509 }
510
511 my_endmntent (mfp);
512 if (fchmod (fileno (mftmp->mntent_fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
513 int errsv = errno;
514 fprintf(stderr, _("error changing mode of %s: %s\n"), MOUNTED_TEMP,
515 strerror (errsv));
516 }
517 my_endmntent (mftmp);
518
519 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
520 int errsv = errno;
521 fprintf(stderr, _("can't rename %s to %s: %s\n"), MOUNTED_TEMP, MOUNTED,
522 strerror(errsv));
523 }
524
525 leave:
526 unlock_mtab();
527 }