1/*
2 * Copyright (c) 2007-2011 Apple Inc.  All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <unistd.h>
25#include <mntopts.h>
26#include <syslog.h>
27
28#include <sys/mount.h>
29
30#include <CoreFoundation/CoreFoundation.h>
31#include <NetFS/NetFS.h>
32#include <NetFS/NetFSPrivate.h>
33/*
34 * XXX - <NetAuth/NetAuth.h> include <NetAuth/NAKeys.h>, which redefines
35 * some of our #defines if _NETFS_H_ isn't defined.
36 *
37 * To prevent that from happening, we include it *after* including NetFS.h,
38 * which defines _NETFS_H_.
39 */
40#include <NetAuth/NetAuth.h>
41
42#define ALT_SOFT	0x00000001
43
44static const struct mntopt mopts_std[] = {
45	MOPT_STDOPTS,
46	MOPT_UPDATE,
47	MOPT_RELOAD,
48	{ "soft",	0, ALT_SOFT, 1 },
49	{ NULL,		0, 0, 0 }
50};
51
52static void usage(void);
53
54static int do_mount_direct(CFURLRef server_URL, CFStringRef mountdir,
55    CFDictionaryRef open_options, CFDictionaryRef mount_options,
56    CFDictionaryRef *mount_infop);
57
58int
59main(int argc, char **argv)
60{
61	int c;
62	int usenetauth = 0;
63	mntoptparse_t mp;
64	int flags, altflags;
65	CFURLRef URL;
66	CFStringRef mountdir_CFString;
67	CFMutableDictionaryRef open_options, mount_options;
68	CFDictionaryRef mount_info;
69	int res;
70
71	flags = altflags = 0;
72	getmnt_silent = 1;
73	while ((c = getopt(argc, argv, "no:rw")) != -1) {
74		switch (c) {
75
76		case 'n':
77			usenetauth = 1;
78			break;
79
80		case 'o':
81			/*
82			 * OK, parse these options, and update the flags.
83			 */
84			mp = getmntopts(optarg, mopts_std, &flags, &altflags);
85			freemntopts(mp);
86			break;
87
88		case 'r':
89			flags |= MNT_RDONLY;
90			break;
91
92		case 'w':
93			flags &= ~MNT_RDONLY;
94			break;
95
96		case '?':
97		default:
98			usage();
99			break;
100		}
101	}
102	argc -= optind;
103	argv += optind;
104
105	if (argc != 2)
106		usage();
107
108	/*
109	 * Nothing can stop the Duke of...
110	 */
111	URL = CFURLCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)argv[0],
112	    strlen(argv[0]), kCFStringEncodingUTF8, NULL);
113	if (URL == NULL)
114		exit(ENOMEM);
115
116	mountdir_CFString = CFStringCreateWithCString(kCFAllocatorDefault,
117	    argv[1], kCFStringEncodingUTF8);
118	if (mountdir_CFString == NULL)
119		exit(ENOMEM);
120
121	open_options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
122	    &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
123	if (open_options == NULL)
124		exit(ENOMEM);
125	/*
126	 * It's OK to use an existing session.
127	 */
128	CFDictionaryAddValue(open_options, kNetFSForceNewSessionKey,
129	    kCFBooleanFalse);
130	/*
131	 * And it's OK to mount something from ourselves.
132	 */
133	CFDictionaryAddValue(open_options, kNetFSAllowLoopbackKey,
134	    kCFBooleanTrue);
135	/*
136	 * This could be mounting a home directory, so we don't want
137	 * the mount to look at user preferences in the home directory.
138	 */
139	CFDictionaryAddValue(open_options, kNetFSNoUserPreferencesKey,
140	    kCFBooleanTrue);
141	/*
142	 * We don't want any UI popped up for the mount.
143	 */
144	CFDictionaryAddValue(open_options, kUIOptionKey, kUIOptionNoUI);
145
146	mount_options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
147	    &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
148	if (mount_options == NULL)
149		exit(ENOMEM);
150	/*
151	 * It's OK to use an existing session.
152	 */
153	CFDictionaryAddValue(mount_options, kNetFSForceNewSessionKey,
154	    kCFBooleanFalse);
155	/*
156	 * We want the URL mounted exactly where we specify.
157	 */
158	CFDictionaryAddValue(mount_options, kNetFSMountAtMountDirKey,
159	    kCFBooleanTrue);
160	/*
161	 * This could be mounting a home directory, so we don't want
162	 * the mount to look at user preferences in the home directory.
163	 */
164	CFDictionaryAddValue(mount_options, kNetFSNoUserPreferencesKey,
165	    kCFBooleanTrue);
166	/*
167	 * We want to allow the URL to specify a directory underneath
168	 * a share point for file systems that support the notion of
169	 * shares.
170	 */
171	CFDictionaryAddValue(mount_options, kNetFSAllowSubMountsKey,
172	    kCFBooleanTrue);
173	/*
174	 * Add the mount flags.
175	 */
176	CFDictionaryAddValue(mount_options, kNetFSMountFlagsKey,
177	    CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type,
178	      &flags));
179	/*
180	 * Add the soft mount flag.
181	 */
182	CFDictionaryAddValue(mount_options, kNetFSSoftMountKey,
183	    (altflags & ALT_SOFT) ? kCFBooleanTrue : kCFBooleanFalse);
184	/*
185	 * We don't want any UI popped up for the mount.
186	 */
187	CFDictionaryAddValue(mount_options, kUIOptionKey, kUIOptionNoUI);
188
189	if (usenetauth)
190		res = NAConnectToServerSync(URL, mountdir_CFString,
191		    open_options, mount_options, &mount_info);
192	else
193		res = do_mount_direct(URL, mountdir_CFString, open_options,
194		    mount_options, &mount_info);
195	/*
196	 * 0 means "no error", EEXIST means "that's already mounted, and
197	 * mountinfo says where it's mounted".  In those cases, a
198	 * directory of mount information was returned; release it.
199	 */
200	if (res == 0 || res == EEXIST)
201		CFRelease(mount_info);
202	CFRelease(mount_options);
203	CFRelease(open_options);
204	CFRelease(mountdir_CFString);
205	CFRelease(URL);
206	if (res != 0) {
207		/*
208		 * Report any failure status that doesn't fit in the
209		 * 8 bits of a UN*X exit status, and map it to EIO
210		 * by default and EAUTH for ENETFS errors.
211		 */
212		if ((res & 0xFFFFFF00) != 0) {
213			syslog(LOG_ERR,
214			    "mount_url: Mount of %s on %s gives status %d",
215			    argv[0], argv[1], res);
216			switch (res) {
217
218			case ENETFSACCOUNTRESTRICTED:
219			case ENETFSPWDNEEDSCHANGE:
220			case ENETFSPWDPOLICY:
221				res = EAUTH;
222				break;
223
224			default:
225				res = EIO;
226				break;
227			}
228		}
229	}
230
231	return res;
232}
233
234static void
235usage(void)
236{
237	fprintf(stderr, "Usage: mount_url [-n] [-rw] [-o options] url node\n");
238	exit(1);
239}
240
241static int
242do_mount_direct(CFURLRef server_URL, CFStringRef mountdir,
243    CFDictionaryRef open_options, CFDictionaryRef mount_options,
244    CFDictionaryRef *mount_infop)
245{
246	int ret;
247	void *session_ref;
248
249	*mount_infop = NULL;
250	ret = netfs_CreateSessionRef(server_URL, &session_ref);
251	if (ret != 0)
252		return ret;
253	ret = netfs_OpenSession(server_URL, session_ref, open_options, NULL);
254	if (ret != 0) {
255		netfs_CloseSession(session_ref);
256		return ret;
257	}
258	ret = netfs_Mount(session_ref, server_URL, mountdir, mount_options,
259	    mount_infop);
260	netfs_CloseSession(session_ref);
261	return ret;
262}
263