mount.c (7313B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <sys/mount.h> 3 #include <sys/stat.h> 4 #include <sys/types.h> 5 #include <sys/wait.h> 6 7 #include <errno.h> 8 #include <limits.h> 9 #include <mntent.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 #include "text.h" 16 #include "util.h" 17 18 #define FSOPTS_MAXLEN 512 19 20 struct { 21 const char *opt; 22 const char *notopt; 23 unsigned long v; 24 } optnames[] = { 25 { "defaults", NULL, 0 }, 26 { "remount", NULL, MS_REMOUNT }, 27 { "ro", "rw", MS_RDONLY }, 28 { "sync", "async", MS_SYNCHRONOUS }, 29 { "dirsync", NULL, MS_DIRSYNC }, 30 { "nodev", "dev", MS_NODEV }, 31 { "noatime", "atime", MS_NOATIME }, 32 { "noauto", "auto", 0 }, 33 { "nodiratime", "diratime", MS_NODIRATIME }, 34 { "noexec", "exec", MS_NOEXEC }, 35 { "nosuid", "suid", MS_NOSUID }, 36 { "mand", "nomand", MS_MANDLOCK }, 37 { "relatime", "norelatime", MS_RELATIME }, 38 { "bind", NULL, MS_BIND }, 39 { NULL, NULL, 0 } 40 }; 41 42 static unsigned long argflags = 0; 43 static char fsopts[FSOPTS_MAXLEN] = ""; 44 45 static char * 46 findtype(const char *types, const char *t) 47 { 48 const char *p; 49 size_t len; 50 51 for (len = strlen(t); (p = strstr(types, t)); types = p + len) { 52 if (!strncmp(p, t, len) && (p[len] == '\0' || p[len] == ',')) 53 return (char *)p; 54 } 55 return NULL; 56 } 57 58 static void 59 parseopts(const char *popts, unsigned long *flags, char *data, size_t datasiz) 60 { 61 unsigned int i, validopt; 62 size_t optlen, dlen = 0; 63 const char *name, *e; 64 65 name = popts; 66 data[0] = '\0'; 67 do { 68 if ((e = strstr(name, ","))) 69 optlen = e - name; 70 else 71 optlen = strlen(name); 72 73 validopt = 0; 74 for (i = 0; optnames[i].opt; i++) { 75 if (optnames[i].opt && 76 !strncmp(name, optnames[i].opt, optlen)) { 77 *flags |= optnames[i].v; 78 validopt = 1; 79 break; 80 } 81 if (optnames[i].notopt && 82 !strncmp(name, optnames[i].notopt, optlen)) { 83 *flags &= ~optnames[i].v; 84 validopt = 1; 85 break; 86 } 87 } 88 89 if (!validopt && optlen > 0) { 90 /* unknown option, pass as data option to mount() */ 91 if (dlen + optlen + 2 >= datasiz) 92 return; /* prevent overflow */ 93 if (dlen) 94 data[dlen++] = ','; 95 memcpy(&data[dlen], name, optlen); 96 dlen += optlen; 97 data[dlen] = '\0'; 98 } 99 name = e + 1; 100 } while (e); 101 } 102 103 static int 104 mounthelper(const char *fsname, const char *dir, const char *fstype) 105 { 106 pid_t pid; 107 char eprog[PATH_MAX]; 108 char const *eargv[10]; 109 int status, i; 110 111 pid = fork(); 112 switch(pid) { 113 case -1: 114 break; 115 case 0: 116 snprintf(eprog, sizeof(eprog), "mount.%s", fstype); 117 118 i = 0; 119 eargv[i++] = eprog; 120 if (argflags & MS_BIND) 121 eargv[i++] = "-B"; 122 if (argflags & MS_MOVE) 123 eargv[i++] = "-M"; 124 if (argflags & MS_REC) 125 eargv[i++] = "-R"; 126 127 if (fsopts[0]) { 128 eargv[i++] = "-o"; 129 eargv[i++] = fsopts; 130 } 131 eargv[i++] = fsname; 132 eargv[i++] = dir; 133 eargv[i] = NULL; 134 135 execvp(eprog, (char * const *)eargv); 136 if (errno == ENOENT) 137 _exit(1); 138 weprintf("execvp:"); 139 _exit(1); 140 break; 141 default: 142 if (waitpid(pid, &status, 0) < 0) { 143 weprintf("waitpid:"); 144 return -1; 145 } 146 if (WIFEXITED(status)) 147 return WEXITSTATUS(status); 148 else if (WIFSIGNALED(status)) 149 return 1; 150 break; 151 } 152 return 0; 153 } 154 155 static int 156 mounted(const char *dir) 157 { 158 FILE *fp; 159 struct mntent *me, mebuf; 160 struct stat st1, st2; 161 char linebuf[256]; 162 163 if (stat(dir, &st1) < 0) { 164 weprintf("stat %s:", dir); 165 return 0; 166 } 167 if (!(fp = setmntent("/proc/mounts", "r"))) 168 eprintf("setmntent %s:", "/proc/mounts"); 169 170 while ((me = getmntent_r(fp, &mebuf, linebuf, sizeof(linebuf)))) { 171 if (stat(me->mnt_dir, &st2) < 0) { 172 weprintf("stat %s:", me->mnt_dir); 173 continue; 174 } 175 if (st1.st_dev == st2.st_dev && 176 st1.st_ino == st2.st_ino) 177 return 1; 178 } 179 endmntent(fp); 180 181 return 0; 182 } 183 184 static void 185 usage(void) 186 { 187 eprintf("usage: %s [-BMRan] [-t fstype] [-o options] [source] [target]\n", 188 argv0); 189 } 190 191 int 192 main(int argc, char *argv[]) 193 { 194 char *types = NULL, data[FSOPTS_MAXLEN] = "", *resolvpath = NULL; 195 char *files[] = { "/proc/mounts", "/etc/fstab", NULL }; 196 const char *source, *target; 197 struct mntent *me = NULL; 198 int aflag = 0, status = 0, i, r; 199 unsigned long flags = 0; 200 FILE *fp; 201 202 ARGBEGIN { 203 case 'B': 204 argflags |= MS_BIND; 205 break; 206 case 'M': 207 argflags |= MS_MOVE; 208 break; 209 case 'R': 210 argflags |= MS_REC; 211 break; 212 case 'a': 213 aflag = 1; 214 break; 215 case 'o': 216 estrlcat(fsopts, EARGF(usage()), sizeof(fsopts)); 217 parseopts(fsopts, &flags, data, sizeof(data)); 218 break; 219 case 't': 220 types = EARGF(usage()); 221 break; 222 case 'n': 223 break; 224 default: 225 usage(); 226 } ARGEND; 227 228 if (argc < 1 && aflag == 0) { 229 if (!(fp = fopen(files[0], "r"))) 230 eprintf("fopen %s:", files[0]); 231 concat(fp, files[0], stdout, "<stdout>"); 232 fclose(fp); 233 return 0; 234 } 235 236 if (aflag == 1) 237 goto mountall; 238 239 source = argv[0]; 240 target = argv[1]; 241 242 if (!target) { 243 target = argv[0]; 244 source = NULL; 245 if (strcmp(target, "/") != 0) { 246 if (!(resolvpath = realpath(target, NULL))) 247 eprintf("realpath %s:", target); 248 target = resolvpath; 249 } 250 } 251 252 for (i = 0; files[i]; i++) { 253 if (!(fp = setmntent(files[i], "r"))) { 254 if (strcmp(files[i], "/proc/mounts") != 0) 255 weprintf("setmntent %s:", files[i]); 256 continue; 257 } 258 while ((me = getmntent(fp))) { 259 if (strcmp(me->mnt_dir, target) == 0 || 260 strcmp(me->mnt_fsname, target) == 0 || 261 (source && strcmp(me->mnt_dir, source) == 0) || 262 (source && strcmp(me->mnt_fsname, source) == 0)) { 263 if (!source) { 264 target = me->mnt_dir; 265 source = me->mnt_fsname; 266 } 267 if (!fsopts[0]) 268 estrlcat(fsopts, me->mnt_opts, sizeof(fsopts)); 269 parseopts(fsopts, &flags, data, sizeof(data)); 270 if (!types) 271 types = me->mnt_type; 272 goto mountsingle; 273 } 274 } 275 endmntent(fp); 276 fp = NULL; 277 } 278 if (!source) 279 eprintf("can't find %s in /etc/fstab\n", target); 280 281 mountsingle: 282 r = mounthelper(source, target, types); 283 if (r == -1) 284 status = 1; 285 if (r > 0 && mount(source, target, types, argflags | flags, data) < 0) { 286 weprintf("mount: %s:", source); 287 status = 1; 288 } 289 if (fp) 290 endmntent(fp); 291 free(resolvpath); 292 return status; 293 294 mountall: 295 if (!(fp = setmntent("/etc/fstab", "r"))) 296 eprintf("setmntent %s:", "/etc/fstab"); 297 while ((me = getmntent(fp))) { 298 /* has "noauto" option or already mounted: skip */ 299 if (hasmntopt(me, MNTOPT_NOAUTO) || mounted(me->mnt_dir)) 300 continue; 301 flags = 0; 302 fsopts[0] = '\0'; 303 if (strlcat(fsopts, me->mnt_opts, sizeof(fsopts)) >= sizeof(fsopts)) { 304 weprintf("%s: option string too long\n", me->mnt_dir); 305 status = 1; 306 continue; 307 } 308 parseopts(fsopts, &flags, data, sizeof(data)); 309 /* if -t types specified: 310 * if non-match, skip 311 * if match and prefixed with "no", skip */ 312 if (types && 313 ((types[0] == 'n' && types[1] == 'o' && 314 findtype(types + 2, me->mnt_type)) || 315 (!findtype(types, me->mnt_type)))) 316 continue; 317 318 r = mounthelper(me->mnt_fsname, me->mnt_dir, me->mnt_type); 319 if (r > 0 && mount(me->mnt_fsname, me->mnt_dir, me->mnt_type, 320 argflags | flags, data) < 0) { 321 weprintf("mount: %s:", me->mnt_fsname); 322 status = 1; 323 } 324 } 325 endmntent(fp); 326 327 return status; 328 }