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, &current, 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