1/*
2 * Copyright (c) 1999, Boris Popov
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 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *    This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD$
33 */
34#include <sys/param.h>
35#include <sys/stat.h>
36#include <sys/errno.h>
37#include <sys/mount.h>
38#include <sys/sysctl.h>
39#include <machine/cpu.h>
40
41#include <stdio.h>
42#include <string.h>
43#include <pwd.h>
44#include <grp.h>
45#include <unistd.h>
46#include <ctype.h>
47#include <stdlib.h>
48#include <err.h>
49#include <sysexits.h>
50#include <time.h>
51
52#include <netncp/ncp_lib.h>
53#include <netncp/ncp_rcfile.h>
54#include <fs/nwfs/nwfs_mount.h>
55#include "mntopts.h"
56
57#define	NWFS_VFSNAME	"nwfs"
58
59static char mount_point[MAXPATHLEN + 1];
60static void usage(void);
61static int parsercfile(struct ncp_conn_loginfo *li, struct nwfs_args *mdata);
62
63static struct mntopt mopts[] = {
64	MOPT_STDOPTS,
65	MOPT_END
66};
67
68static int
69parsercfile(struct ncp_conn_loginfo *li __unused,
70    struct nwfs_args *mdata __unused)
71{
72	return 0;
73}
74
75int
76main(int argc, char *argv[])
77{
78	NWCONN_HANDLE connHandle;
79	struct nwfs_args mdata;
80	struct ncp_conn_loginfo li;
81	struct stat st;
82	struct nw_entry_info einfo;
83	struct tm *tm;
84	time_t ltime;
85	int opt, error, mntflags, nlsopt, wall_clock;
86	int uid_set, gid_set;
87	size_t len;
88	char *p, *p1, tmp[1024];
89	u_char *pv;
90
91	if (argc < 2)
92		usage();
93	if (argc == 2) {
94		if (strcmp(argv[1], "-h") == 0) {
95			usage();
96		} else if (strcmp(argv[1], "-v") == 0) {
97			errx(EX_OK, "version %d.%d.%d", NWFS_VERSION / 100000,
98			    (NWFS_VERSION % 10000) / 1000,
99			    (NWFS_VERSION % 1000) / 100);
100		}
101	}
102
103	if(ncp_initlib()) exit(1);
104
105	mntflags = error = 0;
106	bzero(&mdata,sizeof(mdata));
107	gid_set = uid_set = 0;
108	nlsopt = 0;
109
110	if (ncp_li_init(&li, argc, argv)) return 1;
111	/*
112	 * A little bit weird, but I should figure out which server/user to use
113	 * _before_ reading .rc file
114	 */
115	if (argc >= 3 && argv[argc-1][0] != '-' && argv[argc-2][0] != '-' &&
116	    argv[argc-2][0] == '/') {
117		p = argv[argc-2];
118		error = 1;
119		do {
120			if (*p++ != '/') break;
121			p1 = tmp;
122			while (*p != ':' && *p != 0) *p1++ = *p++;
123			if (*p++ == 0) break;
124			*p1 = 0;
125			if (ncp_li_setserver(&li, tmp)) break;
126			p1 = tmp;
127			while (*p != '/' && *p != 0) *p1++ = *p++;
128			if (*p++ == 0) break;
129			*p1 = 0;
130			if (ncp_li_setuser(&li, tmp)) break;
131			p1 = tmp;
132			while (*p != '/' && *p != 0) *p1++ = *p++;
133			*p1 = 0;
134			if (strlen(tmp) > NCP_VOLNAME_LEN) {
135				warnx("volume name too long: %s", tmp);
136				break;
137			}
138			ncp_str_upper(strcpy(mdata.mounted_vol,tmp));
139			if (*p == '/')
140				p++;
141			p1 = mdata.root_path + 2;
142			pv = mdata.root_path + 1;
143			for(;*p;) {
144				*pv = 0;
145				while (*p != '/' && *p) {
146					*p1++ = *p++;
147					(*pv)++;
148				}
149				if (*pv) {
150					ncp_nls_mem_u2n(pv + 1, pv + 1, *pv);
151					pv += (*pv) + 1;
152					mdata.root_path[0]++;
153				}
154				if (*p++ == 0) break;
155				p1++;
156			}
157			error = 0;
158		} while(0);
159		if (error)
160			errx(EX_DATAERR,
161			    "an error occurred while parsing '%s'",
162			    argv[argc - 2]);
163	}
164	if (ncp_li_readrc(&li)) return 1;
165	if (ncp_rc) {
166		parsercfile(&li,&mdata);
167		rc_close(ncp_rc);
168	}
169	while ((opt = getopt(argc, argv, STDPARAM_OPT"V:c:d:f:g:l:n:o:u:w:")) != -1) {
170		switch (opt) {
171		    case STDPARAM_ARGS:
172			if (ncp_li_arg(&li, opt, optarg)) {
173				return 1;
174			}
175			break;
176		    case 'V':
177			if (strlen(optarg) > NCP_VOLNAME_LEN)
178				errx(EX_DATAERR, "volume too long: %s", optarg);
179			ncp_str_upper(strcpy(mdata.mounted_vol,optarg));
180			break;
181		    case 'u': {
182			struct passwd *pwd;
183
184			pwd = isdigit(optarg[0]) ?
185			    getpwuid(atoi(optarg)) : getpwnam(optarg);
186			if (pwd == NULL)
187				errx(EX_NOUSER, "unknown user '%s'", optarg);
188			mdata.uid = pwd->pw_uid;
189			uid_set = 1;
190			break;
191		    }
192		    case 'g': {
193			struct group *grp;
194
195			grp = isdigit(optarg[0]) ?
196			    getgrgid(atoi(optarg)) : getgrnam(optarg);
197			if (grp == NULL)
198				errx(EX_NOUSER, "unknown group '%s'", optarg);
199			mdata.gid = grp->gr_gid;
200			gid_set = 1;
201			break;
202		    }
203		    case 'd':
204			errno = 0;
205			mdata.dir_mode = strtol(optarg, &p, 8);
206			if (errno || *p != 0)
207				errx(EX_DATAERR, "invalid value for directory mode");
208			break;
209		    case 'f':
210			errno = 0;
211			mdata.file_mode = strtol(optarg, &p, 8);
212			if (errno || *p != 0)
213				errx(EX_DATAERR, "invalid value for file mode");
214			break;
215		    case '?':
216			usage();
217			/*NOTREACHED*/
218		    case 'n': {
219			char *inp, *nsp;
220
221			nsp = inp = optarg;
222			while ((nsp = strsep(&inp, ",;:")) != NULL) {
223				if (strcasecmp(nsp, "OS2") == 0)
224					mdata.flags |= NWFS_MOUNT_NO_OS2;
225				else if (strcasecmp(nsp, "LONG") == 0)
226					mdata.flags |= NWFS_MOUNT_NO_LONG;
227				else if (strcasecmp(nsp, "NFS") == 0)
228					mdata.flags |= NWFS_MOUNT_NO_NFS;
229				else
230					errx(EX_DATAERR, "unknown namespace '%s'", nsp);
231			}
232			break;
233		    };
234		    case 'l':
235			if (ncp_nls_setlocale(optarg) != 0) return 1;
236			mdata.flags |= NWFS_MOUNT_HAVE_NLS;
237			break;
238		    case 'o':
239			getmntopts(optarg, mopts, &mntflags, 0);
240			break;
241		    case 'c':
242			switch (optarg[0]) {
243			    case 'l':
244				nlsopt |= NWHP_LOWER;
245				break;
246			    case 'u':
247				nlsopt |= NWHP_UPPER;
248				break;
249			    case 'n':
250				nlsopt |= NWHP_LOWER | NWHP_UPPER;
251				break;
252			    case 'L':
253				nlsopt |= NWHP_LOWER | NWHP_NOSTRICT;
254				break;
255			    case 'U':
256				nlsopt |= NWHP_UPPER | NWHP_NOSTRICT;
257				break;
258			    default:
259		    		errx(EX_DATAERR, "invalid suboption '%c' for -c",
260				    optarg[0]);
261			}
262			break;
263		    case 'w':
264			if (ncp_nls_setrecodebyname(optarg) != 0)
265				return 1;
266			mdata.flags |= NWFS_MOUNT_HAVE_NLS;
267			break;
268		    default:
269			usage();
270		}
271	}
272
273	if (optind == argc - 2) {
274		optind++;
275	} else if (mdata.mounted_vol[0] == 0)
276		errx(EX_USAGE, "volume name should be specified");
277
278	if (optind != argc - 1)
279		usage();
280	realpath(argv[optind], mount_point);
281
282	if (stat(mount_point, &st) == -1)
283		err(EX_OSERR, "could not find mount point %s", mount_point);
284	if (!S_ISDIR(st.st_mode)) {
285		errno = ENOTDIR;
286		err(EX_OSERR, "can't mount on %s", mount_point);
287	}
288	if (ncp_geteinfo(mount_point, &einfo) == 0)
289		errx(EX_OSERR, "can't mount on %s twice", mount_point);
290
291	if (uid_set == 0) {
292		mdata.uid = st.st_uid;
293	}
294	if (gid_set == 0) {
295		mdata.gid = st.st_gid;
296	}
297	if (mdata.file_mode == 0 ) {
298		mdata.file_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
299	}
300	if (mdata.dir_mode == 0) {
301		mdata.dir_mode = mdata.file_mode;
302		if ((mdata.dir_mode & S_IRUSR) != 0)
303			mdata.dir_mode |= S_IXUSR;
304		if ((mdata.dir_mode & S_IRGRP) != 0)
305			mdata.dir_mode |= S_IXGRP;
306		if ((mdata.dir_mode & S_IROTH) != 0)
307			mdata.dir_mode |= S_IXOTH;
308	}
309	if (li.access_mode == 0) {
310		li.access_mode = mdata.dir_mode;
311	}
312/*	if (mdata.flags & NWFS_MOUNT_HAVE_NLS) {*/
313		mdata.nls = ncp_nls;
314/*	}*/
315	mdata.nls.opt = nlsopt;
316
317	len = sizeof(wall_clock);
318	if (sysctlbyname("machdep.wall_cmos_clock", &wall_clock, &len, NULL, 0) == -1)
319		err(EX_OSERR, "get wall_clock");
320	if (wall_clock == 0) {
321		time(&ltime);
322		tm = localtime(&ltime);
323		mdata.tz = -(tm->tm_gmtoff / 60);
324	}
325
326	error = ncp_li_check(&li);
327	if (error)
328		return 1;
329	li.opt |= NCP_OPT_WDOG;
330	/* well, now we can try to login, or use already established connection */
331	error = ncp_li_login(&li, &connHandle);
332	if (error) {
333		ncp_error("cannot login to server %s", error, li.server);
334		exit(1);
335	}
336	error = ncp_conn2ref(connHandle, &mdata.connRef);
337	if (error) {
338		ncp_error("could not convert handle to reference", error);
339		ncp_disconnect(connHandle);
340		exit(1);
341	}
342	strcpy(mdata.mount_point,mount_point);
343	mdata.version = NWFS_VERSION;
344	error = mount(NWFS_VFSNAME, mdata.mount_point, mntflags, (void*)&mdata);
345	if (error) {
346		ncp_error("mount error: %s", error, mdata.mount_point);
347		ncp_disconnect(connHandle);
348		exit(1);
349	}
350	/*
351	 * I'm leave along my handle, but kernel should keep own ...
352	 */
353	ncp_disconnect(connHandle);
354	/* we are done ?, impossible ... */
355	return 0;
356}
357
358static void
359usage(void)
360{
361	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
362	"usage: mount_nwfs [-Chv] -S server -U user [-connection options]",
363	"                  -V volume [-M mode] [-c case] [-d mode] [-f mode]",
364	"                  [-g gid] [-l locale] [-n os2] [-u uid] [-w scheme]",
365	"                  node",
366	"       mount_nwfs [-options] /server:user/volume[/path] node");
367
368	exit (1);
369}
370