1 /*
2 * Copyright © Elvis Pfützenreuter, 2000
3 * Copyright © Jan Engelhardt, 2006 - 2009
4 * Copyright © Bastian Kleineidam, 2005
5 *
6 * This file is part of pam_mount; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1
9 * of the License, or (at your option) any later version.
10 */
11 #include <config.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <assert.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <limits.h>
19 #include <signal.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <libHX/ctype_helper.h>
26 #include <libHX/defs.h>
27 #include <libHX/deque.h>
28 #include <libHX/proc.h>
29 #include <grp.h>
30 #include <pwd.h>
31 #include "pam_mount.h"
32
33 /* Functions */
34 static inline bool mkmountpoint(struct vol *, const char *);
35
36 //-----------------------------------------------------------------------------
37 /**
38 * log_output
39 * @fd: file descriptor to read from
40 * @cmsg: conditional message
41 *
42 * Reads all data from @fd and logs it using w4rn(). @fd is usually connected
43 * to a pipe to another process's stdout or stderr. Only if @fd actually has
44 * output for us, @cmsg will be printed.
45 *
46 * @fd will be closed.
47 */
log_output(int fd,const char * cmsg)48 static void log_output(int fd, const char *cmsg)
49 {
50 hxmc_t *buf = NULL;
51 FILE *fp;
52
53 if ((fp = fdopen(fd, "r")) == NULL) {
54 w4rn("error opening file: %s\n", strerror(errno));
55 close(fd);
56 return;
57 }
58
59 setvbuf(fp, NULL, _IOLBF, 0);
60 do {
61 if (HX_getl(&buf, fp) == NULL)
62 break;
63 HX_chomp(buf);
64 if (*buf != '\0' && cmsg != NULL) {
65 l0g("%s", cmsg);
66 cmsg = NULL;
67 }
68
69 l0g("%s\n", buf);
70 } while (true);
71 fclose(fp);
72 HXmc_free(buf);
73 }
74
75 /**
76 * run_ofl -
77 * @config: current configuration
78 * @vinfo:
79 *
80 * Runs `ofl` on a directory/mountpoint and logs its output, for debugging
81 * purposes. (ofl is a better-suited lsof/fuser.)
82 */
run_ofl(const struct config * const config,const char * mntpt,unsigned int signum)83 static void run_ofl(const struct config *const config, const char *mntpt,
84 unsigned int signum)
85 {
86 struct HXformat_map *vinfo;
87 struct HXproc proc;
88 struct HXdeque *argv;
89 struct stat sb;
90 int ret;
91
92 if (stat(mntpt, &sb) < 0 && errno == ENOENT)
93 return;
94
95 vinfo = HXformat_init();
96 if (vinfo == NULL)
97 return;
98 format_add(vinfo, "MNTPT", mntpt);
99 HXformat_add(vinfo, "SIGNAL", reinterpret_cast(void *,
100 static_cast(long, signum)), HXFORMAT_IMMED | HXTYPE_UINT);
101 argv = arglist_build(config->command[CMD_OFL], vinfo);
102 HXformat_free(vinfo);
103 if (argv == NULL)
104 return;
105 memset(&proc, 0, sizeof(proc));
106 proc.p_flags = HXPROC_VERBOSE;
107 ret = pmt_spawn_dq(argv, &proc);
108 if (ret <= 0)
109 l0g("error executing ofl: %s\n", strerror(-ret));
110 else
111 HXproc_wait(&proc);
112 }
113
114 /**
115 * already_mounted -
116 * @config: current config
117 * @vpt: volume descriptor
118 * @vinfo:
119 *
120 * Checks if @config->volume[@vol] is already mounted, and returns 1 if this
121 * the case, 0 if not and -1 on error.
122 */
123 #if defined(HAVE_GETMNTENT)
124 /* elsewhere */
125 #elif defined(HAVE_GETMNTINFO)
126 /* elsewhere */
127 #else
pmt_already_mounted(const struct config * const config,const struct vol * vpt,struct HXformat_map * vinfo)128 int pmt_already_mounted(const struct config *const config,
129 const struct vol *vpt, struct HXformat_map *vinfo)
130 {
131 l0g("check for previous mount not implemented on arch.\n");
132 return -1;
133 }
134 #endif
135
fstype_networked(enum command_type fstype)136 static bool fstype_networked(enum command_type fstype)
137 {
138 switch (fstype) {
139 case CMD_NFSMOUNT:
140 case CMD_CIFSMOUNT:
141 case CMD_SMBMOUNT:
142 case CMD_NCPMOUNT:
143 return true;
144 default:
145 return false;
146 }
147 }
148
149 /**
150 * vol_to_dev -
151 * @vol: volume to analyze
152 *
153 * Turn a volume into the mountspec as accepted by the specific mount program.
154 */
pmt_vol_to_dev(const struct vol * vol)155 hxmc_t *pmt_vol_to_dev(const struct vol *vol)
156 {
157 hxmc_t *ret;
158
159 switch (vol->type) {
160 case CMD_SMBMOUNT:
161 case CMD_CIFSMOUNT:
162 if (vol->server != NULL) {
163 ret = HXmc_strinit("//");
164 HXmc_strcat(&ret, vol->server);
165 HXmc_strcat(&ret, "/");
166 HXmc_strcat(&ret, vol->volume);
167 } else {
168 ret = HXmc_strinit(vol->volume);
169 }
170 break;
171
172 case CMD_NCPMOUNT:
173 if (vol->server != NULL) {
174 ret = HXmc_strinit(vol->server);
175 HXmc_strcat(&ret, "/");
176 HXmc_strcat(&ret, kvplist_get(&vol->options, "user"));
177 } else {
178 ret = HXmc_strinit(vol->volume);
179 }
180 break;
181
182 case CMD_NFSMOUNT:
183 if (vol->server != NULL) {
184 ret = HXmc_strinit(vol->server);
185 HXmc_strcat(&ret, ":");
186 HXmc_strcat(&ret, vol->volume);
187 } else {
188 ret = HXmc_strinit(vol->volume);
189 }
190 break;
191
192 default:
193 if (!fstype_networked(vol->type) && vol->server != NULL)
194 /*
195 * Possible causes: we do not know about the fs yet.
196 * (Was once the case with NFS4, for example.)
197 */
198 l0g("The \"server\" attribute is ignored for this "
199 "filesystem (%s).\n", vol->fstype);
200
201 ret = HXmc_strinit(vol->volume);
202 break;
203 }
204
205 return ret;
206 }
207
log_pm_input(const struct config * const config,const struct vol * vpt)208 static void log_pm_input(const struct config *const config,
209 const struct vol *vpt)
210 {
211 hxmc_t *options;
212
213 options = kvplist_to_str(&vpt->options);
214 w4rn(
215 "Mount info: %s, user=%s <volume fstype=\"%s\" "
216 "server=\"%s\" path=\"%s\" "
217 "mountpoint=\"%s\" cipher=\"%s\" fskeypath=\"%s\" "
218 "fskeycipher=\"%s\" fskeyhash=\"%s\" options=\"%s\" /> "
219 "fstab=%u ssh=%u\n",
220 vpt->globalconf ? "globalconf" : "luserconf",
221 znul(vpt->user), znul(vpt->fstype),
222 znul(vpt->server), znul(vpt->volume),
223 vpt->mountpoint, znul(vpt->cipher), znul(vpt->fs_key_path),
224 znul(vpt->fs_key_cipher), znul(vpt->fs_key_hash), options,
225 vpt->use_fstab, vpt->uses_ssh
226 );
227 HXmc_free(options);
228 }
229
230 /**
231 * mkmountpoint - create mountpoint for volume
232 * @volume: volume structure
233 * @d: directory to create
234 *
235 * Switches to the volume user's identity and see if we can create the
236 * mountpoint. This is required for NFS mounts with root_squash enabled
237 * (assuming the mountpoint's parent is writable by the user, e.g. if it is
238 * inside the user's home directory).
239 *
240 * If that fails, do as usual (create as root, chown to user).
241 */
mkmountpoint(struct vol * volume,const char * d)242 static bool mkmountpoint(struct vol *volume, const char *d)
243 {
244 const struct passwd *pe;
245 hxmc_t *dtmp;
246 char *last;
247 bool ret = true;
248 bool is_file;
249
250 is_file = (kvplist_contains(&volume->options, "bind") ||
251 kvplist_contains(&volume->options, "move")) &&
252 pmt_fileop_isreg(volume->volume);
253
254 if ((pe = getpwnam(volume->user)) == NULL) {
255 l0g("getpwuid: %s\n", strerror(errno));
256 return false;
257 }
258 dtmp = HXmc_strinit(d);
259 if (dtmp == NULL || (!is_file && HXmc_strcat(&dtmp, "/") == NULL)) {
260 l0g("HXmc_strinit: %s\n", strerror(errno));
261 return false;
262 }
263
264 last = dtmp;
265 while ((last = strchr(last + 1, '/')) != NULL) {
266 *last = '\0';
267 w4rn("%s: checking %s\n", __func__, dtmp);
268 if (pmt_fileop_exists(dtmp)) {
269 /* If it exists, all is good... */
270 *last = '/';
271 continue;
272 }
273 /*
274 * Prefer creation using user id, due to NFS possibly using
275 * root_squashed NFS.
276 *
277 * The directory will be created in a restricted mode S_IRWXU
278 * here. When mounted, the root directory of the new vfsmount
279 * will override it, so there is no need to use S_IRWXUGO or
280 * S_IRWXU | S_IXUGO here.
281 *
282 * Workaround for CIFS on root_squashed NFS: +S_IXUGO
283 */
284 if (setegid(pe->pw_gid) < 0 ||
285 seteuid(pe->pw_uid) < 0) {
286 l0g("seteuid/setegid %ld:%ld failed: %s\n",
287 static_cast(long, pe->pw_uid),
288 static_cast(long, pe->pw_gid), strerror(errno));
289 } else if (mkdir(dtmp, S_IRWXU | S_IXUGO) == 0) {
290 /* Was createable as user - ok */
291 w4rn("mkdir[%ld] %s\n",
292 static_cast(long, pe->pw_uid), dtmp);
293 *last = '/';
294 continue;
295 }
296 /* Try with root again. */
297 if (seteuid(0) < 0) {
298 l0g("seteuid 0 failed\n");
299 ret = false;
300 break;
301 }
302 if (mkdir(dtmp, S_IRWXU | S_IXUGO) < 0) {
303 l0g("mkdir %s failed: %s\n", dtmp, strerror(errno));
304 ret = false;
305 break;
306 }
307 w4rn("mkdir[0] %s\n", dtmp);
308 if (chown(dtmp, pe->pw_uid, pe->pw_gid) < 0) {
309 l0g("chown %s failed: %s\n", dtmp, strerror(errno));
310 ret = false;
311 break;
312 }
313 w4rn("chown %s -> %ld:%ld\n", dtmp,
314 static_cast(long, pe->pw_uid),
315 static_cast(long, pe->pw_gid));
316 }
317 HXmc_free(dtmp);
318
319 /* Touch the file for bind mount */
320 if (is_file && !pmt_fileop_exists(d)) {
321 int fd = open(d, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
322 if (fd < 0) {
323 l0g("creat %s failed: %s\n", d, strerror(errno));
324 ret = false;
325 goto out;
326 }
327 close(fd);
328 if (chown(d, pe->pw_uid, pe->pw_gid) < 0) {
329 l0g("chown %s failed: %s\n", d, strerror(errno));
330 ret = false;
331 }
332 }
333 /* restore state: */
334 out:
335 seteuid(0);
336 return volume->created_mntpt = ret;
337 }
338
339 /**
340 * do_unmount -
341 * @config: current config
342 * @vpt: volume descriptor
343 * @vinfo:
344 * @password: always %NULL
345 *
346 * Returns zero on error, positive non-zero for success.
347 */
do_unmount(const struct config * config,struct vol * vpt,struct HXformat_map * vinfo,const char * const password)348 int do_unmount(const struct config *config, struct vol *vpt,
349 struct HXformat_map *vinfo, const char *const password)
350 {
351 struct HXdeque *argv;
352 struct HXproc proc;
353 int ret, type;
354
355 assert(vinfo != NULL);
356 assert(password == NULL); /* password should point to NULL for unmounting */
357
358 if (Debug)
359 /*
360 * Often, a process still exists with ~ as its pwd after
361 * logging out. Running ofl helps debug this.
362 */
363 run_ofl(config, vpt->mountpoint, 0);
364
365 switch (vpt->type) {
366 case CMD_CRYPTMOUNT:
367 type = CMD_CRYPTUMOUNT;
368 break;
369 case CMD_SMBMOUNT:
370 type = CMD_SMBUMOUNT;
371 break;
372 case CMD_NCPMOUNT:
373 type = CMD_NCPUMOUNT;
374 break;
375 case CMD_FUSEMOUNT:
376 type = CMD_FUSEUMOUNT;
377 break;
378 default:
379 type = CMD_UMOUNT;
380 break;
381 }
382
383 if (config->command[type] == NULL || config->command[type]->first == 0)
384 l0g("{smb,ncp}umount not defined in pam_count.conf.xml\n");
385
386 argv = arglist_build(config->command[type], vinfo);
387 memset(&proc, 0, sizeof(proc));
388 proc.p_flags = HXPROC_VERBOSE | HXPROC_NULL_STDOUT | HXPROC_STDERR;
389 proc.p_ops = &pmt_dropprivs_ops;
390 if ((ret = pmt_spawn_dq(argv, &proc)) <= 0) {
391 ret = 0;
392 goto out;
393 }
394
395 log_output(proc.p_stderr, "umount messages:\n");
396 if ((ret = HXproc_wait(&proc)) >= 0)
397 /* pass on through the result from the umount process */
398 ret = proc.p_exited && proc.p_status == 0;
399
400 out:
401 if (config->mkmntpoint && config->rmdir_mntpt && vpt->created_mntpt)
402 if (rmdir(vpt->mountpoint) < 0)
403 /* non-fatal, but warn */
404 w4rn("could not remove %s\n", vpt->mountpoint);
405 return ret;
406 }
407
check_filesystem(const struct config * config,const struct vol * vpt,struct HXformat_map * vinfo)408 static int check_filesystem(const struct config *config, const struct vol *vpt,
409 struct HXformat_map *vinfo)
410 {
411 /* PRE: config points to a valid struct config
412 * config->volume[vol] is a valid struct vol
413 * POST: integrity of volume has been checked
414 * FN VAL: if error 0 else 1, errors are logged
415 */
416 #if defined (__linux__)
417 const char *fsck_target;
418 struct HXdeque *argv;
419 struct HXproc proc;
420 int ret;
421
422 assert(vinfo != NULL);
423
424 if (vpt->type == CMD_CRYPTMOUNT)
425 /*
426 * Cryptmount involves dm-crypt or LUKS, so using the raw
427 * device as fsck target is meaningless.
428 * So we do _not_ set FSCKTARGET in vinfo at all, and
429 * mount_set_fsck() depends on this behavior.
430 */
431 return 0;
432
433 fsck_target = vpt->volume;
434
435 if (config->command[CMD_FSCK]->items == 0) {
436 l0g("fsck not defined in pam_mount.conf.xml\n");
437 return 0;
438 }
439
440 if (kvplist_contains(&vpt->options, "bind") ||
441 kvplist_contains(&vpt->options, "move") ||
442 fstype_nodev(vpt->fstype) != 0)
443 return 1;
444
445 format_add(vinfo, "FSCKTARGET", fsck_target);
446
447 argv = arglist_build(config->command[CMD_FSCK], vinfo);
448 memset(&proc, 0, sizeof(proc));
449 proc.p_flags = HXPROC_VERBOSE | HXPROC_STDOUT | HXPROC_STDERR;
450 if ((ret = pmt_spawn_dq(argv, &proc)) <= 0)
451 return 0;
452
453 /* stdout and stderr must be logged for fsck */
454 log_output(proc.p_stdout, NULL);
455 log_output(proc.p_stderr, NULL);
456 w4rn("waiting for filesystem check\n");
457 if ((ret = HXproc_wait(&proc)) < 0)
458 l0g("error waiting for child: %s\n", strerror(-ret));
459
460 /*
461 * pass on through the result -- okay if 0 (no errors) or 1 (errors
462 * corrected)
463 */
464 return proc.p_exited && (proc.p_status == 0 || proc.p_status == 1);
465 #else
466 l0g("checking filesystem not implemented on arch.\n");
467 return 1;
468 #endif
469 }
470
471 /**
472 * mount_set_fsck - set the FSCK environment variable for mount.crypt
473 * @config: configuration
474 * @vol: current volume
475 * @vinfo: variable substituions
476 */
mount_set_fsck(const struct config * config,const struct vol * vol,struct HXformat_map * vinfo)477 static void mount_set_fsck(const struct config *config,
478 const struct vol *vol, struct HXformat_map *vinfo)
479 {
480 const struct HXdeque_node *i;
481 hxmc_t *string, *current;
482
483 if (vol->type != CMD_CRYPTMOUNT)
484 return;
485
486 format_add(vinfo, "FSCKTARGET", "");
487 string = HXmc_meminit(NULL, 0);
488
489 for (i = config->command[CMD_FSCK]->first; i != NULL; i = i->next) {
490 if (HXformat_aprintf(vinfo, ¤t, i->ptr) > 0) {
491 HXmc_strcat(&string, current);
492 HXmc_strcat(&string, " ");
493 }
494 HXmc_free(current);
495 }
496
497 setenv("FSCK", string, true);
498 HXmc_free(string);
499 }
500
501 /**
502 * do_mount -
503 * @config: current config
504 * @vpt: volume descriptor
505 * @vinfo:
506 * @password: login password (may be %NULL)
507 *
508 * Returns zero on error, positive non-zero for success.
509 */
do_mount(const struct config * config,struct vol * vpt,struct HXformat_map * vinfo,const char * password)510 int do_mount(const struct config *config, struct vol *vpt,
511 struct HXformat_map *vinfo, const char *password)
512 {
513 const struct HXdeque_node *n;
514 struct HXdeque *argv;
515 struct HXproc proc;
516 const char *mount_user;
517 int ret;
518
519 assert(vinfo != NULL);
520
521 ret = pmt_already_mounted(config, vpt, vinfo);
522 if (ret < 0) {
523 l0g("could not determine if %s is already mounted, "
524 "failing\n", vpt->volume);
525 return 0;
526 } else if (ret > 0) {
527 w4rn("%s already seems to be mounted at %s, "
528 "skipping\n", vpt->volume, vpt->mountpoint);
529 return 1;
530 }
531 if (!pmt_fileop_exists(vpt->mountpoint)) {
532 if (config->mkmntpoint) {
533 if (!mkmountpoint(vpt, vpt->mountpoint))
534 return 0;
535 } else {
536 l0g("mount point %s does not exist (pam_mount not "
537 "configured to make it)\n",
538 vpt->mountpoint);
539 return 0;
540 }
541 }
542
543 if (config->command[vpt->type]->items == 0) {
544 l0g("proper mount command not defined in "
545 "pam_mount.conf.xml\n");
546 return 0;
547 }
548
549 password = (password != NULL) ? password : "";
550 if ((argv = HXdeque_init()) == NULL)
551 misc_log("malloc: %s\n", strerror(errno));
552 if (vpt->uses_ssh)
553 for (n = config->command[CMD_FD0SSH]->first;
554 n != NULL; n = n->next)
555 arglist_add(argv, n->ptr, vinfo);
556
557 for (n = config->command[vpt->type]->first; n != NULL; n = n->next)
558 arglist_add(argv, n->ptr, vinfo);
559
560 /*
561 * Note to future editors: Do not do a second-time substitution of the
562 * arguments. Variables specified within <volume>s are already expanded
563 * in expandconfig(), but see the comment in mount_op().
564 */
565
566 if (vpt->type == CMD_LCLMOUNT &&
567 !check_filesystem(config, vpt, vinfo))
568 l0g("error checking filesystem but will continue\n");
569 /* send password down pipe to mount process */
570 if (vpt->type == CMD_SMBMOUNT || vpt->type == CMD_CIFSMOUNT)
571 setenv("PASSWD_FD", "0", 1);
572
573 mount_set_fsck(config, vpt, vinfo);
574 arglist_log(argv);
575 mount_user = vpt->noroot ? vpt->user : NULL;
576 memset(&proc, 0, sizeof(proc));
577 proc.p_flags = HXPROC_VERBOSE | HXPROC_STDIN |
578 HXPROC_NULL_STDOUT | HXPROC_STDERR;
579 proc.p_ops = &pmt_dropprivs_ops;
580 proc.p_data = const_cast1(char *, mount_user);
581 if ((ret = pmt_spawn_dq(argv, &proc)) <= 0)
582 return 0;
583
584 if (write(proc.p_stdin, password, strlen(password)) !=
585 strlen(password))
586 /* FIXME: clean: returns value of exit below */
587 l0g("error sending password to mount\n");
588 close(proc.p_stdin);
589
590 log_output(proc.p_stderr, "Messages from underlying mount program:\n");
591 if ((ret = HXproc_wait(&proc)) < 0) {
592 l0g("error waiting for child: %s\n", strerror(-ret));
593 return 0;
594 }
595
596 if (!proc.p_exited || proc.p_status != 0) {
597 /*
598 * Remove mountpoint if mount failed, to flag unavailability
599 * of service (e.g. when mntpt is the user's home directory).
600 */
601 if (rmdir(vpt->mountpoint) < 0)
602 /* non-fatal, but warn */
603 w4rn("could not remove %s again: %s\n",
604 vpt->mountpoint, strerror(errno));
605 vpt->created_mntpt = false;
606 }
607
608 if (Debug) {
609 if (pmt_fileop_exists("/proc/self/mountinfo"))
610 pmt_readfile("/proc/self/mountinfo");
611 else if (pmt_fileop_exists("/proc/self/mounts"))
612 pmt_readfile("/proc/self/mounts");
613 else if (pmt_fileop_exists("/proc/mounts"))
614 pmt_readfile("/proc/mounts");
615 }
616
617 /* pass on through the result from the umount process */
618 return proc.p_exited && proc.p_status == 0;
619 }
620
621 /**
622 * mount_op -
623 * @mnt: function to execute mount operations (do_mount or do_unmount)
624 * @config: current configuration
625 * @vpt: volume descriptor
626 * @password: password string (may be %NULL on unmount)
627 *
628 * Returns zero on error, positive non-zero for success.
629 * Note: Checked by volume_record_sane() and read_volume()
630 */
mount_op(mount_op_fn_t * mnt,const struct config * config,struct vol * vpt,const char * password)631 int mount_op(mount_op_fn_t *mnt, const struct config *config,
632 struct vol *vpt, const char *password)
633 {
634 int fnval;
635 struct HXformat_map *vinfo;
636 struct passwd *pe;
637 hxmc_t *options;
638 char real_mpt[PATH_MAX+1];
639
640 /*
641 * This expansion (the other is in expandconfig()!) expands the mount
642 * command arguments (as defined in rdconf1.c) and should not be used
643 * to expand the <volume> attributes themselves.
644 *
645 * If you added an attribute, edit expandconfig instead.
646 * If you added a variable to the mount arg table in rdconf1.c,
647 * edit here. In fact, the @vinfo that is created below contains only
648 * arguments from the mount argument table.
649 */
650 if ((vinfo = HXformat_init()) == NULL)
651 return 0;
652
653 HXmc_free(vpt->combopath);
654 vpt->combopath = pmt_vol_to_dev(vpt);
655 if (vpt->combopath == NULL) {
656 l0g("vol_to_dev: %s\n", strerror(errno));
657 return 0;
658 }
659
660 if (realpath(vpt->mountpoint, real_mpt) == NULL) {
661 w4rn("Could not get realpath of %s: %s\n",
662 vpt->mountpoint, strerror(errno));
663 } else {
664 real_mpt[sizeof(real_mpt)-1] = '\0';
665 free(vpt->mountpoint);
666 vpt->mountpoint = xstrdup(real_mpt);
667 }
668
669 format_add(vinfo, "MNTPT", vpt->mountpoint);
670 format_add(vinfo, "FSTYPE", vpt->fstype);
671 format_add(vinfo, "VOLUME", vpt->volume);
672 format_add(vinfo, "COMBOPATH", vpt->combopath);
673 format_add(vinfo, "SERVER", vpt->server);
674 format_add(vinfo, "USER", vpt->user);
675 format_add(vinfo, "CIPHER", vpt->cipher);
676 format_add(vinfo, "FSKEYCIPHER", vpt->fs_key_cipher);
677 format_add(vinfo, "FSKEYHASH", vpt->fs_key_hash);
678 format_add(vinfo, "FSKEYPATH", vpt->fs_key_path);
679
680 if ((pe = getpwnam(vpt->user)) == NULL) {
681 w4rn("getpwnam(\"%s\") failed: %s\n",
682 Config.user, strerror(errno));
683 } else {
684 HXformat_add(vinfo, "USERUID", reinterpret_cast(void *,
685 static_cast(long, pe->pw_uid)),
686 HXTYPE_UINT | HXFORMAT_IMMED);
687 HXformat_add(vinfo, "USERGID", reinterpret_cast(void *,
688 static_cast(long, pe->pw_gid)),
689 HXTYPE_UINT | HXFORMAT_IMMED);
690 }
691
692 options = kvplist_to_str(&vpt->options);
693 HXformat_add(vinfo, "OPTIONS", options, HXTYPE_STRING | HXFORMAT_IMMED);
694
695 if (Debug)
696 log_pm_input(config, vpt);
697
698 fnval = (*mnt)(config, vpt, vinfo, password);
699 HXmc_free(options);
700 HXformat_free(vinfo);
701 return fnval;
702 }
703
704 /**
705 * fstype_nodev -
706 * @name: fstype to check
707 *
708 * Returns 1 if the filesystem does not require a block device, 0 if it does
709 * require a block device, -1 if we could not find out.
710 */
fstype_nodev(const char * name)711 int fstype_nodev(const char *name)
712 {
713 char buf[80];
714 FILE *fp;
715
716 if (name == NULL)
717 return 0;
718 if ((fp = fopen("/proc/filesystems", "r")) == NULL)
719 return -1;
720
721 while (fgets(buf, sizeof(buf), fp) != NULL) {
722 char *bp = buf;
723 HX_chomp(buf);
724 while (!HX_isspace(*bp) && *bp != '\0')
725 ++bp;
726 while (HX_isspace(*bp))
727 ++bp;
728 if (strcasecmp(bp, name) == 0) {
729 fclose(fp);
730 return strncasecmp(buf, "nodev", 5) == 0;
731 }
732 }
733
734 fclose(fp);
735 return -1;
736 }
737
738 /**
739 * umount_final - called when the last session has exited
740 *
741 * Send signals to processes and then unmount.
742 */
umount_final(struct config * config)743 void umount_final(struct config *config)
744 {
745 struct vol *vol;
746
747 if (HXlist_empty(&config->volume_list.list))
748 /* Avoid needlessy waiting on usleep */
749 return;
750
751 if (config->sig_hup)
752 HXlist_for_each_entry_rev(vol, &config->volume_list, list)
753 run_ofl(config, vol->mountpoint, SIGHUP);
754 if (config->sig_term) {
755 usleep(config->sig_wait);
756 HXlist_for_each_entry_rev(vol, &config->volume_list, list)
757 run_ofl(config, vol->mountpoint, SIGTERM);
758 }
759 if (config->sig_kill) {
760 usleep(config->sig_wait);
761 HXlist_for_each_entry_rev(vol, &config->volume_list, list)
762 run_ofl(config, vol->mountpoint, SIGKILL);
763 }
764 HXlist_for_each_entry_rev(vol, &config->volume_list, list) {
765 w4rn("going to unmount\n");
766 if (!mount_op(do_unmount, config, vol, NULL))
767 l0g("unmount of %s failed\n",
768 vol->volume);
769 }
770 }
771
772 /**
773 * fstype_icase - return whether the volume name is case-insensitive
774 * @fstype: filesystem type (cifs, etc.)
775 *
776 * For some filesystems, notably those which do not distinguish between case
777 * sensitivity, the volume ("share") name is usually also case-insensitive.
778 */
fstype_icase(const char * fstype)779 bool fstype_icase(const char *fstype)
780 {
781 if (fstype == NULL)
782 return false;
783 return strcasecmp(fstype, "cifs") == 0 ||
784 strcasecmp(fstype, "smbfs") == 0 ||
785 strcasecmp(fstype, "ncpfs") == 0;
786 }
787
fstype2_icase(enum command_type fstype)788 bool fstype2_icase(enum command_type fstype)
789 {
790 switch (fstype) {
791 case CMD_CIFSMOUNT:
792 case CMD_SMBMOUNT:
793 case CMD_NCPMOUNT:
794 return true;
795 default:
796 return false;
797 }
798 }
799