]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/mdmfs/mdmfs.c
This commit was generated by cvs2svn to compensate for changes in r165670,
[FreeBSD/FreeBSD.git] / sbin / mdmfs / mdmfs.c
1 /*
2  * Copyright (c) 2001 Dima Dorfman.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 /*
28  * mdmfs (md/MFS) is a wrapper around mdconfig(8),
29  * newfs(8), and mount(8) that mimics the command line option set of
30  * the deprecated mount_mfs(8).
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/mdioctl.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40
41 #include <assert.h>
42 #include <err.h>
43 #include <fcntl.h>
44 #include <grp.h>
45 #include <paths.h>
46 #include <pwd.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52
53 typedef enum { false, true } bool;
54
55 struct mtpt_info {
56         uid_t            mi_uid;
57         bool             mi_have_uid;
58         gid_t            mi_gid;
59         bool             mi_have_gid;
60         mode_t           mi_mode;
61         bool             mi_have_mode;
62 };
63
64 static  bool debug;             /* Emit debugging information? */
65 static  bool loudsubs;          /* Suppress output from helper programs? */
66 static  bool norun;             /* Actually run the helper programs? */
67 static  int unit;               /* The unit we're working with. */
68 static  const char *mdname;     /* Name of memory disk device (e.g., "md"). */
69 static  size_t mdnamelen;       /* Length of mdname. */
70 static  const char *path_mdconfig =_PATH_MDCONFIG;
71
72 static void      argappend(char **, const char *, ...) __printflike(2, 3);
73 static void      debugprintf(const char *, ...) __printflike(1, 2);
74 static void      do_mdconfig_attach(const char *, const enum md_types);
75 static void      do_mdconfig_attach_au(const char *, const enum md_types);
76 static void      do_mdconfig_detach(void);
77 static void      do_mount(const char *, const char *);
78 static void      do_mtptsetup(const char *, struct mtpt_info *);
79 static void      do_newfs(const char *);
80 static void      extract_ugid(const char *, struct mtpt_info *);
81 static int       run(int *, const char *, ...) __printflike(2, 3);
82 static void      usage(void);
83
84 int
85 main(int argc, char **argv)
86 {
87         struct mtpt_info mi;            /* Mountpoint info. */
88         char *mdconfig_arg, *newfs_arg, /* Args to helper programs. */
89             *mount_arg;
90         enum md_types mdtype;           /* The type of our memory disk. */
91         bool have_mdtype;
92         bool detach, softdep, autounit, newfs;
93         char *mtpoint, *unitstr;
94         char *p;
95         int ch;
96         void *set;
97         unsigned long ul;
98
99         /* Misc. initialization. */
100         (void)memset(&mi, '\0', sizeof(mi));
101         detach = true;
102         softdep = true;
103         autounit = false;
104         newfs = true;
105         have_mdtype = false;
106         mdtype = MD_SWAP;
107         mdname = MD_NAME;
108         mdnamelen = strlen(mdname);
109         /*
110          * Can't set these to NULL.  They may be passed to the
111          * respective programs without modification.  I.e., we may not
112          * receive any command-line options which will caused them to
113          * be modified.
114          */
115         mdconfig_arg = strdup("");
116         newfs_arg = strdup("");
117         mount_arg = strdup("");
118
119         /* If we were started as mount_mfs or mfs, imply -C. */
120         if (strcmp(getprogname(), "mount_mfs") == 0 ||
121             strcmp(getprogname(), "mfs") == 0) {
122                 /* Make compatibility assumptions. */
123                 mi.mi_mode = 01777;
124                 mi.mi_have_mode = true;
125         }
126
127         while ((ch = getopt(argc, argv,
128             "a:b:Cc:Dd:E:e:F:f:hi:LlMm:Nn:O:o:Pp:Ss:t:Uv:w:X")) != -1)
129                 switch (ch) {
130                 case 'a':
131                         argappend(&newfs_arg, "-a %s", optarg);
132                         break;
133                 case 'b':
134                         argappend(&newfs_arg, "-b %s", optarg);
135                         break;
136                 case 'C':
137                         /* Ignored for compatibility. */
138                         break;
139                 case 'c':
140                         argappend(&newfs_arg, "-c %s", optarg);
141                         break;
142                 case 'D':
143                         detach = false;
144                         break;
145                 case 'd':
146                         argappend(&newfs_arg, "-d %s", optarg);
147                         break;
148                 case 'E':
149                         path_mdconfig = optarg;
150                         break;
151                 case 'e':
152                         argappend(&newfs_arg, "-e %s", optarg);
153                         break;
154                 case 'F':
155                         if (have_mdtype)
156                                 usage();
157                         mdtype = MD_VNODE;
158                         have_mdtype = true;
159                         argappend(&mdconfig_arg, "-f %s", optarg);
160                         break;
161                 case 'f':
162                         argappend(&newfs_arg, "-f %s", optarg);
163                         break;
164                 case 'h':
165                         usage();
166                         break;
167                 case 'i':
168                         argappend(&newfs_arg, "-i %s", optarg);
169                         break;
170                 case 'L':
171                         loudsubs = true;
172                         break;
173                 case 'l':
174                         argappend(&newfs_arg, "-l");
175                         break;
176                 case 'M':
177                         if (have_mdtype)
178                                 usage();
179                         mdtype = MD_MALLOC;
180                         have_mdtype = true;
181                         break;
182                 case 'm':
183                         argappend(&newfs_arg, "-m %s", optarg);
184                         break;
185                 case 'N':
186                         norun = true;
187                         break;
188                 case 'n':
189                         argappend(&newfs_arg, "-n %s", optarg);
190                         break;
191                 case 'O':
192                         argappend(&newfs_arg, "-o %s", optarg);
193                         break;
194                 case 'o':
195                         argappend(&mount_arg, "-o %s", optarg);
196                         break;
197                 case 'P':
198                         newfs = false;
199                         break;
200                 case 'p':
201                         if ((set = setmode(optarg)) == NULL)
202                                 usage();
203                         mi.mi_mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
204                         mi.mi_have_mode = true;
205                         free(set);
206                         break;
207                 case 'S':
208                         softdep = false;
209                         break;
210                 case 's':
211                         argappend(&mdconfig_arg, "-s %s", optarg);
212                         break;
213                 case 'U':
214                         softdep = true;
215                         break;
216                 case 'v':
217                         argappend(&newfs_arg, "-O %s", optarg);
218                         break;
219                 case 'w':
220                         extract_ugid(optarg, &mi);
221                         break;
222                 case 'X':
223                         debug = true;
224                         break;
225                 default:
226                         usage();
227                 }
228         argc -= optind;
229         argv += optind;
230         if (argc < 2)
231                 usage();
232
233         /* Derive 'unit' (global). */
234         unitstr = argv[0];
235         if (strncmp(unitstr, "/dev/", 5) == 0)
236                 unitstr += 5;
237         if (strncmp(unitstr, mdname, mdnamelen) == 0)
238                 unitstr += mdnamelen;
239         if (*unitstr == '\0') {
240                 autounit = true;
241                 unit = -1;
242         } else {
243                 ul = strtoul(unitstr, &p, 10);
244                 if (ul == ULONG_MAX || *p != '\0')
245                         errx(1, "bad device unit: %s", unitstr);
246                 unit = ul;
247         }
248
249         mtpoint = argv[1];
250         if (!have_mdtype)
251                 mdtype = MD_SWAP;
252         if (softdep)
253                 argappend(&newfs_arg, "-U");
254         if (mdtype != MD_VNODE && !newfs)
255                 errx(1, "-P requires a vnode-backed disk");
256
257         /* Do the work. */
258         if (detach && !autounit)
259                 do_mdconfig_detach();
260         if (autounit)
261                 do_mdconfig_attach_au(mdconfig_arg, mdtype);
262         else
263                 do_mdconfig_attach(mdconfig_arg, mdtype);
264         if (newfs)
265                 do_newfs(newfs_arg);
266         do_mount(mount_arg, mtpoint);
267         do_mtptsetup(mtpoint, &mi);
268
269         return (0);
270 }
271
272 /*
273  * Append the expansion of 'fmt' to the buffer pointed to by '*dstp';
274  * reallocate as required.
275  */
276 static void
277 argappend(char **dstp, const char *fmt, ...)
278 {
279         char *old, *new;
280         va_list ap;
281         
282         old = *dstp;
283         assert(old != NULL);
284
285         va_start(ap, fmt);
286         if (vasprintf(&new, fmt,ap) == -1)
287                 errx(1, "vasprintf");
288         va_end(ap);
289
290         *dstp = new;
291         if (asprintf(&new, "%s %s", old, new) == -1)
292                 errx(1, "asprintf");
293         free(*dstp);
294         free(old);
295
296         *dstp = new;
297 }
298
299 /*
300  * If run-time debugging is enabled, print the expansion of 'fmt'.
301  * Otherwise, do nothing.
302  */
303 static void
304 debugprintf(const char *fmt, ...)
305 {
306         va_list ap;
307
308         if (!debug)
309                 return;
310         fprintf(stderr, "DEBUG: ");
311         va_start(ap, fmt);
312         vfprintf(stderr, fmt, ap);
313         va_end(ap);
314         fprintf(stderr, "\n");
315         fflush(stderr);
316 }
317
318 /*
319  * Attach a memory disk with a known unit.
320  */
321 static void
322 do_mdconfig_attach(const char *args, const enum md_types mdtype)
323 {
324         int rv;
325         const char *ta;         /* Type arg. */
326
327         switch (mdtype) {
328         case MD_SWAP:
329                 ta = "-t swap";
330                 break;
331         case MD_VNODE:
332                 ta = "-t vnode";
333                 break;
334         case MD_MALLOC:
335                 ta = "-t malloc";
336                 break;
337         default:
338                 abort();
339         }
340         rv = run(NULL, "%s -a %s%s -u %s%d", path_mdconfig, ta, args,
341             mdname, unit);
342         if (rv)
343                 errx(1, "mdconfig (attach) exited with error code %d", rv);
344 }
345
346 /*
347  * Attach a memory disk with an unknown unit; use autounit.
348  */
349 static void
350 do_mdconfig_attach_au(const char *args, const enum md_types mdtype)
351 {
352         const char *ta;         /* Type arg. */
353         char *linep, *linebuf;  /* Line pointer, line buffer. */
354         int fd;                 /* Standard output of mdconfig invocation. */
355         FILE *sfd;
356         int rv;
357         char *p;
358         size_t linelen;
359         unsigned long ul;
360
361         switch (mdtype) {
362         case MD_SWAP:
363                 ta = "-t swap";
364                 break;
365         case MD_VNODE:
366                 ta = "-t vnode";
367                 break;
368         case MD_MALLOC:
369                 ta = "-t malloc";
370                 break;
371         default:
372                 abort();
373         }
374         rv = run(&fd, "%s -a %s%s", path_mdconfig, ta, args);
375         if (rv)
376                 errx(1, "mdconfig (attach) exited with error code %d", rv);
377
378         /* Receive the unit number. */
379         if (norun) {    /* Since we didn't run, we can't read.  Fake it. */
380                 unit = 0;
381                 return;
382         }
383         sfd = fdopen(fd, "r");
384         if (sfd == NULL)
385                 err(1, "fdopen");
386         linep = fgetln(sfd, &linelen);
387         if (linep == NULL && linelen < mdnamelen + 1)
388                 errx(1, "unexpected output from mdconfig (attach)");
389         /* If the output format changes, we want to know about it. */
390         assert(strncmp(linep, mdname, mdnamelen) == 0);
391         linebuf = malloc(linelen - mdnamelen + 1);
392         assert(linebuf != NULL);
393         /* Can't use strlcpy because linep is not NULL-terminated. */
394         strncpy(linebuf, linep + mdnamelen, linelen);
395         linebuf[linelen] = '\0';
396         ul = strtoul(linebuf, &p, 10);
397         if (ul == ULONG_MAX || *p != '\n')
398                 errx(1, "unexpected output from mdconfig (attach)");
399         unit = ul;
400
401         fclose(sfd);
402         close(fd);
403 }
404
405 /*
406  * Detach a memory disk.
407  */
408 static void
409 do_mdconfig_detach(void)
410 {
411         int rv;
412
413         rv = run(NULL, "%s -d -u %s%d", path_mdconfig, mdname, unit);
414         if (rv && debug)        /* This is allowed to fail. */
415                 warnx("mdconfig (detach) exited with error code %d (ignored)",
416                       rv);
417 }
418
419 /*
420  * Mount the configured memory disk.
421  */
422 static void
423 do_mount(const char *args, const char *mtpoint)
424 {
425         int rv;
426
427         rv = run(NULL, "%s%s /dev/%s%d %s", _PATH_MOUNT, args,
428             mdname, unit, mtpoint);
429         if (rv)
430                 errx(1, "mount exited with error code %d", rv);
431 }
432
433 /*
434  * Various configuration of the mountpoint.  Mostly, enact 'mip'.
435  */
436 static void
437 do_mtptsetup(const char *mtpoint, struct mtpt_info *mip)
438 {
439
440         if (mip->mi_have_mode) {
441                 debugprintf("changing mode of %s to %o.", mtpoint,
442                     mip->mi_mode);
443                 if (!norun)
444                         if (chmod(mtpoint, mip->mi_mode) == -1)
445                                 err(1, "chmod: %s", mtpoint);
446         }
447         /*
448          * We have to do these separately because the user may have
449          * only specified one of them.
450          */
451         if (mip->mi_have_uid) {
452                 debugprintf("changing owner (user) or %s to %u.", mtpoint,
453                     mip->mi_uid);
454                 if (!norun)
455                         if (chown(mtpoint, mip->mi_uid, -1) == -1)
456                                 err(1, "chown %s to %u (user)", mtpoint,
457                                     mip->mi_uid);
458         }
459         if (mip->mi_have_gid) {
460                 debugprintf("changing owner (group) or %s to %u.", mtpoint,
461                     mip->mi_gid);
462                 if (!norun)
463                         if (chown(mtpoint, -1, mip->mi_gid) == -1)
464                                 err(1, "chown %s to %u (group)", mtpoint,
465                                     mip->mi_gid);
466         }
467 }
468
469 /*
470  * Put a file system on the memory disk.
471  */
472 static void
473 do_newfs(const char *args)
474 {
475         int rv;
476
477         rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit);
478         if (rv)
479                 errx(1, "newfs exited with error code %d", rv);
480 }
481
482 /*
483  * 'str' should be a user and group name similar to the last argument
484  * to chown(1); i.e., a user, followed by a colon, followed by a
485  * group.  The user and group in 'str' may be either a [ug]id or a
486  * name.  Upon return, the uid and gid fields in 'mip' will contain
487  * the uid and gid of the user and group name in 'str', respectively.
488  *
489  * In other words, this derives a user and group id from a string
490  * formatted like the last argument to chown(1).
491  *
492  * Notice: At this point we don't support only a username or only a
493  * group name. do_mtptsetup already does, so when this feature is
494  * desired, this is the only routine that needs to be changed.
495  */
496 static void
497 extract_ugid(const char *str, struct mtpt_info *mip)
498 {
499         char *ug;                       /* Writable 'str'. */
500         char *user, *group;             /* Result of extracton. */
501         struct passwd *pw;
502         struct group *gr;
503         char *p;
504         uid_t *uid;
505         gid_t *gid;
506
507         uid = &mip->mi_uid;
508         gid = &mip->mi_gid;
509         mip->mi_have_uid = mip->mi_have_gid = false;
510
511         /* Extract the user and group from 'str'.  Format above. */
512         ug = strdup(str);
513         assert(ug != NULL);
514         group = ug;
515         user = strsep(&group, ":");
516         if (user == NULL || group == NULL || *user == '\0' || *group == '\0')
517                 usage();
518
519         /* Derive uid. */
520         *uid = strtoul(user, &p, 10);
521         if (*uid == (uid_t)ULONG_MAX)
522                 usage();
523         if (*p != '\0') {
524                 pw = getpwnam(user);
525                 if (pw == NULL)
526                         errx(1, "invalid user: %s", user);
527                 *uid = pw->pw_uid;
528         }
529         mip->mi_have_uid = true;
530
531         /* Derive gid. */
532         *gid = strtoul(group, &p, 10);
533         if (*gid == (gid_t)ULONG_MAX)
534                 usage();
535         if (*p != '\0') {
536                 gr = getgrnam(group);
537                 if (gr == NULL)
538                         errx(1, "invalid group: %s", group);
539                 *gid = gr->gr_gid;
540         }
541         mip->mi_have_gid = true;
542
543         free(ug);
544 }
545
546 /*
547  * Run a process with command name and arguments pointed to by the
548  * formatted string 'cmdline'.  Since system(3) is not used, the first
549  * space-delimited token of 'cmdline' must be the full pathname of the
550  * program to run.  The return value is the return code of the process
551  * spawned.  If 'ofd' is non-NULL, it is set to the standard output of
552  * the program spawned (i.e., you can read from ofd and get the output
553  * of the program).
554  */
555 static int
556 run(int *ofd, const char *cmdline, ...)
557 {
558         char **argv, **argvp;           /* Result of splitting 'cmd'. */
559         int argc;
560         char *cmd;                      /* Expansion of 'cmdline'. */
561         int pid, status;                /* Child info. */
562         int pfd[2];                     /* Pipe to the child. */
563         int nfd;                        /* Null (/dev/null) file descriptor. */
564         bool dup2dn;                    /* Dup /dev/null to stdout? */
565         va_list ap;
566         char *p;
567         int rv, i;
568
569         dup2dn = true;
570         va_start(ap, cmdline);
571         rv = vasprintf(&cmd, cmdline, ap);
572         if (rv == -1)
573                 err(1, "vasprintf");
574         va_end(ap);
575
576         /* Split up 'cmd' into 'argv' for use with execve. */
577         for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++)
578                 argc++;         /* 'argc' generation loop. */
579         argv = (char **)malloc(sizeof(*argv) * (argc + 1));
580         assert(argv != NULL);
581         for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;)
582                 if (**argv != '\0')
583                         if (++argvp >= &argv[argc]) {
584                                 *argvp = NULL;
585                                 break;
586                         }
587         assert(*argv);
588
589         /* Make sure the above loop works as expected. */
590         if (debug) {
591                 /*
592                  * We can't, but should, use debugprintf here.  First,
593                  * it appends a trailing newline to the output, and
594                  * second it prepends "DEBUG: " to the output.  The
595                  * former is a problem for this would-be first call,
596                  * and the latter for the would-be call inside the
597                  * loop.
598                  */
599                 (void)fprintf(stderr, "DEBUG: running:");
600                 /* Should be equivilent to 'cmd' (before strsep, of course). */
601                 for (i = 0; argv[i] != NULL; i++)
602                         (void)fprintf(stderr, " %s", argv[i]);
603                 (void)fprintf(stderr, "\n");
604         }
605
606         /* Create a pipe if necessary and fork the helper program. */
607         if (ofd != NULL) {
608                 if (pipe(&pfd[0]) == -1)
609                         err(1, "pipe");
610                 *ofd = pfd[0];
611                 dup2dn = false;
612         }
613         pid = fork();
614         switch (pid) {
615         case 0:
616                 /* XXX can we call err() in here? */
617                 if (norun)
618                         _exit(0);
619                 if (ofd != NULL)
620                         if (dup2(pfd[1], STDOUT_FILENO) < 0)
621                                 err(1, "dup2");
622                 if (!loudsubs) {
623                         nfd = open(_PATH_DEVNULL, O_RDWR);
624                         if (nfd == -1)
625                                 err(1, "open: %s", _PATH_DEVNULL);
626                         if (dup2(nfd, STDIN_FILENO) < 0)
627                                 err(1, "dup2");
628                         if (dup2dn)
629                                 if (dup2(nfd, STDOUT_FILENO) < 0)
630                                    err(1, "dup2");
631                         if (dup2(nfd, STDERR_FILENO) < 0)
632                                 err(1, "dup2");
633                 }
634
635                 (void)execv(argv[0], argv);
636                 warn("exec: %s", argv[0]);
637                 _exit(-1);
638         case -1:
639                 err(1, "fork");
640         }
641
642         free(cmd);
643         free(argv);
644         while (waitpid(pid, &status, 0) != pid)
645                 ;
646         return (WEXITSTATUS(status));
647 }
648
649 static void
650 usage(void)
651 {
652
653         fprintf(stderr,
654 "usage: %s [-DLlMNPSUX] [-a maxcontig] [-b block-size] [-c cylinders]\n"
655 "\t[-d rotdelay] [-E path-mdconfig] [-e maxbpg] [-F file] [-f frag-size]\n"
656 "\t[-i bytes] [-m percent-free] [-n rotational-positions] [-O optimization]\n"
657 "\t[-o mount-options] [-p permissions] [-s size] [-v version]\n"
658 "\t[-w user:group] md-device mount-point\n", getprogname());
659         exit(1);
660 }