4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 #pragma ident "%Z%%M% %I% %E% SMI"
34 #include <sys/types.h>
36 #include <sys/param.h>
39 #include <netinet/in.h>
41 #include <sys/tiuser.h>
45 #include <sys/mntent.h>
46 #include <sys/mnttab.h>
48 #include <sys/mount.h>
49 #include <sys/fs/autofs.h>
57 #include "automount.h"
60 static int unmount_mntpnt(struct mnttab
*);
61 static int call_fork_exec(char *, char *, char **, int);
62 static void remove_browse_options(char *);
63 static int inherit_options(char *, char **);
78 struct mapent
*me
, *mapents
= NULL
;
79 char mntpnt
[MAXPATHLEN
];
80 char spec_mntpnt
[MAXPATHLEN
];
82 char *private; /* fs specific data. eg prevhost in case of nfs */
85 action_list
*alp
, *prev
, *tmp
;
86 char root
[MAXPATHLEN
];
88 char next_subdir
[MAXPATHLEN
];
89 bool_t mount_access
= TRUE
;
91 bool_t isrestricted
= hasrestrictopt(mapopts
);
92 char *stack
[STACKSIZ
];
93 char **stkptr
= stack
;
98 /* initialize the stack of open files for this thread */
99 stack_op(INIT
, NULL
, stack
, &stkptr
);
101 err
= getmapent(key
, mapname
, &ml
, stack
, &stkptr
, &iswildcard
,
104 mapents
= parse_entry(key
, mapname
, mapopts
, &ml
,
105 subdir
, isdirect
, mount_access
);
110 trace_prt(1, " do_mount1:\n");
111 for (me
= mapents
; me
; me
= me
->map_next
) {
112 trace_prt(1, " (%s,%s)\t%s%s%s\n",
113 me
->map_fstype
? me
->map_fstype
: "",
114 me
->map_mounter
? me
->map_mounter
: "",
116 me
->map_root
? me
->map_root
: "",
117 me
->map_mntpnt
? me
->map_mntpnt
: "");
118 trace_prt(0, "\t\t-%s\n",
119 me
->map_mntopts
? me
->map_mntopts
: "");
121 for (mfs
= me
->map_fs
; mfs
; mfs
= mfs
->mfs_next
)
122 trace_prt(0, "\t\t%s:%s\tpenalty=%d\n",
123 mfs
->mfs_host
? mfs
->mfs_host
: "",
124 mfs
->mfs_dir
? mfs
->mfs_dir
: "",
132 * Each mapent in the list describes a mount to be done.
133 * Normally there's just a single entry, though in the
134 * case of /net mounts there may be many entries, that
135 * must be mounted as a hierarchy. For each mount the
136 * automountd must make sure the required mountpoint
137 * exists and invoke the appropriate mount command for
141 for (me
= mapents
; me
&& !err
; me
= me
->map_next
) {
142 len
= snprintf(mntpnt
, sizeof (mntpnt
), "%s%s%s", path
,
143 mapents
->map_root
, me
->map_mntpnt
);
145 if (len
>= sizeof (mntpnt
)) {
146 free_mapent(mapents
);
147 return (ENAMETOOLONG
);
150 * remove trailing /'s from mountpoint to avoid problems
151 * stating a directory with two or more trailing slashes.
152 * This will let us mount directories from machines
153 * which export with two or more slashes (apollo for instance).
156 while (mntpnt
[len
] == '/')
157 mntpnt
[len
--] = '\0';
159 (void) strcpy(spec_mntpnt
, mntpnt
);
162 inherit_options(mapopts
, &me
->map_mntopts
) != 0) {
163 syslog(LOG_ERR
, "malloc of options failed");
164 free_mapent(mapents
);
168 if (strcmp(me
->map_fstype
, MNTTYPE_NFS
) == 0) {
169 remove_browse_options(me
->map_mntopts
);
170 if (flags
== DOMOUNT_KERNEL
) {
171 alp
= (action_list
*)malloc(
172 sizeof (action_list
));
175 "malloc of alp failed");
178 memset(alp
, 0, sizeof (action_list
));
182 mount_nfs(me
, spec_mntpnt
, private, overlay
, uid
,
185 * We must retry if we don't have access to the
186 * root file system and there are other
187 * following mapents. The reason we can't
188 * continue because the rest of the mapent list
189 * depends on whether mount_access is TRUE or FALSE.
191 if (err
== NFSERR_ACCES
&& me
->map_next
!= NULL
) {
193 * don't expect mount_access to be
194 * FALSE here, but we do a check
197 if (mount_access
== TRUE
) {
198 mount_access
= FALSE
;
200 free_mapent(mapents
);
212 for (tmp
= *alpp
; tmp
!= NULL
;
219 } else if (strcmp(me
->map_fstype
, MNTTYPE_AUTOFS
) == 0) {
221 len
= strlcpy(root
, path
, sizeof (root
));
223 len
= snprintf(root
, sizeof (root
), "%s/%s",
226 if (len
>= sizeof (root
)) {
227 free_mapent(mapents
);
228 return (ENAMETOOLONG
);
231 alp
= (action_list
*)malloc(sizeof (action_list
));
233 syslog(LOG_ERR
, "malloc of alp failed");
236 memset(alp
, 0, sizeof (action_list
));
239 * get the next subidr, but only if its a modified
240 * or faked autofs mount
242 if (me
->map_modified
|| me
->map_faked
) {
243 len
= snprintf(next_subdir
,
244 sizeof (next_subdir
), "%s%s", subdir
,
247 next_subdir
[0] = '\0';
252 trace_prt(1, " root=%s\t next_subdir=%s\n",
254 if (len
< sizeof (next_subdir
)) {
255 err
= mount_autofs(me
, spec_mntpnt
, alp
,
256 root
, next_subdir
, key
);
262 * append to action list
268 for (tmp
= *alpp
; tmp
!= NULL
;
277 } else if (strcmp(me
->map_fstype
, MNTTYPE_LOFS
) == 0) {
278 remove_browse_options(me
->map_mntopts
);
279 err
= loopbackmount(me
->map_fs
->mfs_dir
, spec_mntpnt
,
280 me
->map_mntopts
, overlay
);
283 remove_browse_options(me
->map_mntopts
);
284 err
= mount_generic(me
->map_fs
->mfs_dir
,
285 me
->map_fstype
, me
->map_mntopts
,
286 spec_mntpnt
, overlay
);
291 free_mapent(mapents
);
294 * If an error occurred,
295 * the filesystem doesn't exist, or could not be
296 * mounted. Return EACCES to autofs indicating that
297 * the mountpoint can not be accessed if this is not
298 * a wildcard access. If it is a wildcard access we
299 * return ENOENT since the lookup that triggered
300 * this mount request will fail and the entry will not
305 * No error occurred, return 0 to indicate success.
310 * The filesystem does not exist or could not be mounted.
311 * Return ENOENT if the lookup was triggered by a wildcard
312 * access. Wildcard entries only exist if they can be
313 * mounted. They can not be listed otherwise (through
315 * Return EACCES if the lookup was not triggered by a
316 * wildcard access. Map entries that are explicitly defined
317 * in maps are visible via readdir(2), therefore we return
318 * EACCES to indicate that the entry exists, but the directory
319 * can not be opened. This is the same behavior of a Unix
320 * directory that exists, but has its execute bit turned off.
321 * The directory is there, but the user does not have access
333 #define VFS_PATH "/usr/lib/fs"
336 mount_generic(special
, fstype
, opts
, mntpnt
, overlay
)
337 char *special
, *fstype
, *opts
, *mntpnt
;
343 char *newargv
[ARGV_MAX
];
346 trace_prt(1, " mount: %s %s %s %s\n",
347 special
, mntpnt
, fstype
, opts
);
350 if (stat(mntpnt
, &stbuf
) < 0) {
351 syslog(LOG_ERR
, "Couldn't stat %s: %m", mntpnt
);
361 * Use "quiet" option to suppress warnings about unsupported
367 m
.mnt_mntopts
= opts
;
368 if (hasmntopt(&m
, MNTOPT_RO
) != NULL
)
374 newargv
[i
++] = special
;
375 newargv
[i
++] = mntpnt
;
377 res
= call_fork_exec(fstype
, "mount", newargv
, verbose
);
378 if (res
== 0 && trace
> 1) {
379 if (stat(mntpnt
, &stbuf
) == 0) {
380 trace_prt(1, " mount of %s dev=%x rdev=%x OK\n",
381 mntpnt
, stbuf
.st_dev
, stbuf
.st_rdev
);
383 trace_prt(1, " failed to stat %s\n", mntpnt
);
390 automountd_do_fork_exec(void *cookie
, char *argp
, size_t arg_size
,
391 door_desc_t
*dfd
, uint_t n_desc
)
399 char *newargv
[ARGV_MAX
];
403 command
= (command_t
*)argp
;
404 if (sizeof (*command
) != arg_size
) {
406 door_return((char *)&res
, sizeof (res
), NULL
, 0);
409 switch ((child_pid
= fork1())) {
411 syslog(LOG_ERR
, "Cannot fork: %m");
419 fd
= open(command
->console
? "/dev/console" : "/dev/null",
427 for (i
= 0; *command
->argv
[i
]; i
++) {
428 newargv
[i
] = strdup(command
->argv
[i
]);
429 if (newargv
[i
] == (char *)NULL
) {
430 syslog(LOG_ERR
, "failed to copy argument '%s'"
431 " of %s: %m", command
->argv
[i
],
438 (void) execv(command
->file
, newargv
);
440 syslog(LOG_ERR
, "exec %s: %m", command
->file
);
447 (void) waitpid(child_pid
, &stat_loc
, WUNTRACED
);
449 if (WIFEXITED(stat_loc
)) {
452 " fork_exec: returns exit status %d\n",
453 WEXITSTATUS(stat_loc
));
456 res
= WEXITSTATUS(stat_loc
);
457 } else if (WIFSIGNALED(stat_loc
)) {
460 " fork_exec: returns signal status %d\n",
466 " fork_exec: returns unknown status\n");
471 door_return((char *)&res
, sizeof (res
), NULL
, 0);
472 trace_prt(1, "automountd_do_fork_exec, door return failed %s, %s\n",
473 command
->file
, strerror(errno
));
474 door_return(NULL
, 0, NULL
, 0);
485 m
.mnt_special
= ur
->mntresource
;
486 m
.mnt_mountp
= ur
->mntpnt
;
487 m
.mnt_fstype
= ur
->fstype
;
488 m
.mnt_mntopts
= ur
->mntopts
;
490 * Special case for NFS mounts.
491 * Don't want to attempt unmounts from
492 * a dead server. If any member of a
493 * hierarchy belongs to a dead server
494 * give up (try later).
496 if (strcmp(ur
->fstype
, MNTTYPE_NFS
) == 0) {
497 struct replica
*list
;
499 bool_t pubopt
= FALSE
;
504 * See if a port number was specified. If one was
505 * specified that is too large to fit in 16 bits, truncate
506 * the high-order bits (for historical compatibility). Use
507 * zero to indicate "no port specified".
509 got_port
= nopt(&m
, MNTOPT_PORT
, &nfs_port
);
512 nfs_port
&= USHRT_MAX
;
514 if (hasmntopt(&m
, MNTOPT_PUBLIC
))
517 list
= parse_replica(ur
->mntresource
, &n
);
520 syslog(LOG_ERR
, "Memory allocation failed: %m");
525 for (i
= 0; i
< n
; i
++) {
526 if (pingnfs(list
[i
].host
, 1, NULL
, 0, nfs_port
,
527 pubopt
, list
[i
].path
, NULL
) != RPC_SUCCESS
) {
529 free_replica(list
, n
);
533 free_replica(list
, n
);
536 res
= unmount_mntpnt(&m
);
545 char *fstype
= mnt
->mnt_fstype
;
546 char *mountp
= mnt
->mnt_mountp
;
547 char *newargv
[ARGV_MAX
];
550 if (strcmp(fstype
, MNTTYPE_NFS
) == 0) {
551 res
= nfsunmount(mnt
);
552 } else if (strcmp(fstype
, MNTTYPE_LOFS
) == 0) {
553 if ((res
= umount(mountp
)) < 0)
559 res
= call_fork_exec(fstype
, "umount", newargv
, verbose
);
562 * filesystem specific unmount command not found
564 if ((res
= umount(mountp
)) < 0)
570 trace_prt(1, " unmount %s %s\n",
571 mountp
, res
? "failed" : "OK");
576 * Remove the autofs specific options 'browse', 'nobrowse' and
577 * 'restrict' from 'opts'.
580 remove_browse_options(char *opts
)
583 char buf
[MAXOPTSLEN
], new[MAXOPTSLEN
];
587 (void) strcpy(buf
, opts
);
589 while (p
= (char *)strtok_r(pb
, ",", &placeholder
)) {
591 if (strcmp(p
, MNTOPT_NOBROWSE
) != 0 &&
592 strcmp(p
, MNTOPT_BROWSE
) != 0 &&
593 strcmp(p
, MNTOPT_RESTRICT
) != 0) {
595 (void) strcat(new, ",");
596 (void) strcat(new, p
);
599 (void) strcpy(opts
, new);
602 static const char *restropts
[] = {
605 #define NROPTS (sizeof (restropts)/sizeof (restropts[0]))
608 inherit_options(char *opts
, char **mapentopts
)
615 size_t len
= strlen(*mapentopts
);
617 for (i
= 0; i
< NROPTS
; i
++)
618 len
+= strlen(restropts
[i
]);
620 /* "," for each new option plus the trailing NUL */
627 (void) strcpy(new, *mapentopts
);
629 mtmap
.mnt_mntopts
= *mapentopts
;
630 mtopt
.mnt_mntopts
= opts
;
632 for (i
= 0; i
< NROPTS
; i
++) {
633 if (hasmntopt(&mtopt
, (char *)restropts
[i
]) != NULL
&&
634 hasmntopt(&mtmap
, (char *)restropts
[i
]) == NULL
) {
636 (void) strcat(new, ",");
637 (void) strcat(new, restropts
[i
]);
646 hasrestrictopt(char *opts
)
650 mt
.mnt_mntopts
= opts
;
652 return (hasmntopt(&mt
, MNTOPT_RESTRICT
) != NULL
);
656 call_fork_exec(fstype
, cmd
, newargv
, console
)
664 char path
[MAXPATHLEN
];
671 bzero(&command
, sizeof (command
));
672 /* build the full path name of the fstype dependent command */
673 (void) snprintf(path
, MAXPATHLEN
, "%s/%s/%s", VFS_PATH
, fstype
, cmd
);
675 if (stat(path
, &stbuf
) != 0) {
680 strlcpy(command
.file
, path
, MAXPATHLEN
);
681 strlcpy(command
.argv
[0], path
, MAXOPTSLEN
);
682 for (i
= 2; newargv
[i
]; i
++) {
683 strlcpy(command
.argv
[i
-1], newargv
[i
], MAXOPTSLEN
);
686 trace_prt(1, " call_fork_exec: %s ", command
.file
);
687 for (i
= 0; *command
.argv
[i
]; i
++)
688 trace_prt(0, "%s ", command
.argv
[i
]);
692 command
.console
= console
;
694 darg
.data_ptr
= (char *)&command
;
695 darg
.data_size
= sizeof (command
);
696 darg
.desc_ptr
= NULL
;
698 darg
.rbuf
= (char *)&status
;
699 darg
.rsize
= sizeof (status
);
701 ret
= door_call(did_fork_exec
, &darg
);
703 trace_prt(1, " call_fork_exec: door_call failed %d\n", ret
);