1/*
2 * Copyright (c) 2003 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 <stdio.h>
25#include <unistd.h>
26#include <stdlib.h>
27#include <fcntl.h>
28#include <sys/mount.h>
29#include <sys/errno.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/fcntl.h>
33#include <string.h>
34#include <sys/syslimits.h>
35#include <dirent.h>
36
37#include "dynarray.h"
38#include "NetBootServer.h"
39#include "nbsp.h"
40
41static void
42NBSPEntry_print(NBSPEntryRef entry)
43{
44    printf("%s: path %s%s\n", entry->name, entry->path,
45	   entry->is_readonly ? " [read-only]" : "");
46    return;
47}
48
49int
50NBSPList_count(NBSPListRef list)
51{
52    dynarray_t *	dlist = (dynarray_t *)list;
53
54    return (dynarray_count(dlist));
55}
56
57NBSPEntryRef
58NBSPList_element(NBSPListRef list, int i)
59{
60    dynarray_t *	dlist = (dynarray_t *)list;
61
62    return (dynarray_element(dlist, i));
63}
64
65
66void
67NBSPList_print(NBSPListRef list)
68{
69    dynarray_t *	dlist = (dynarray_t *)list;
70    int 		i;
71
72    for (i = 0; i < dynarray_count(dlist); i++) {
73	NBSPEntryRef entry = (NBSPEntryRef)dynarray_element(dlist, i);
74	NBSPEntry_print(entry);
75    }
76    return;
77}
78
79void
80NBSPList_free(NBSPListRef * l)
81{
82    dynarray_t * list;
83    if (l == NULL)
84	return;
85    list = *((dynarray_t * *)l);
86    if (list == NULL)
87	return;
88    dynarray_free(list);
89    free(list);
90    *l = NULL;
91    return;
92}
93
94static struct statfs *
95get_fsstat_list(int * number)
96{
97    int n;
98    struct statfs * stat_p;
99
100    n = getfsstat(NULL, 0, MNT_NOWAIT);
101    if (n <= 0)
102	return (NULL);
103
104    stat_p = (struct statfs *)malloc(n * sizeof(*stat_p));
105    if (stat_p == NULL)
106	return (NULL);
107
108    if (getfsstat(stat_p, n * sizeof(*stat_p), MNT_NOWAIT) <= 0) {
109	free(stat_p);
110	return (NULL);
111    }
112    *number = n;
113    return (stat_p);
114}
115
116static int
117lookup_symlink(const char * symlink_dir,
118	       const char * dir_name,
119	       char * link_name, int link_name_len)
120{
121    DIR *		dir_p;
122    int			len = 0;
123    char		path[PATH_MAX];
124    struct dirent *	scan;
125
126    dir_p = opendir(symlink_dir);
127    if (dir_p == NULL) {
128	goto done;
129    }
130    while ((scan = readdir(dir_p)) != NULL) {
131	char		symlink[MAXNAMLEN];
132	ssize_t		symlink_len;
133
134	if (scan->d_type != DT_LNK) {
135	    continue;
136	}
137	snprintf(path, sizeof(path), "%s/%s",
138		 symlink_dir, scan->d_name);
139	symlink_len = readlink(path, symlink, sizeof(symlink) - 1);
140	if (symlink_len <= 0) {
141	    continue;
142	}
143	symlink[symlink_len] = '\0';
144	if (strcmp(symlink, dir_name) == 0) {
145	    strlcpy(link_name, scan->d_name, link_name_len);
146	    len = strlen(link_name);
147	    break;
148	}
149    }
150 done:
151    if (dir_p != NULL) {
152	closedir(dir_p);
153    }
154    return (len);
155}
156
157NBSPListRef
158NBSPList_init(const char * symlink_name, bool readonly_ok)
159{
160    int				i;
161    dynarray_t *		list = NULL;
162    struct statfs * 		stat_p;
163    int				stat_number;
164
165    stat_p = get_fsstat_list(&stat_number);
166    if (stat_p == NULL || stat_number == 0) {
167	goto done;
168    }
169
170    for (i = 0; i < stat_number; i++) {
171	NBSPEntryRef	entry;
172	struct statfs * p = stat_p + i;
173	char		sharename[MAXNAMLEN];
174	int		sharename_len = 0;
175	char		sharedir[PATH_MAX];
176	int		sharedir_len = 0;
177	char		sharelink[PATH_MAX];
178	char *		root;
179	struct stat	sb;
180
181	if ((p->f_flags & MNT_LOCAL) == 0) {
182	    /* skip non-local filesystems */
183	    continue;
184	}
185	if ((p->f_flags & MNT_RDONLY) != 0 && readonly_ok == FALSE) {
186	    /* skip read-only filesystems if not explicitly allowed */
187	    continue;
188	}
189	if (strcmp(p->f_fstypename, "devfs") == 0
190	    || strcmp(p->f_fstypename, "fdesc") == 0) {
191	    /* don't bother with devfs, fdesc */
192	    continue;
193	}
194	root = p->f_mntonname;
195	if (strcmp(root, "/") == 0)
196	    root = "";
197	snprintf(sharelink, sizeof(sharelink),
198		 "%s" NETBOOT_DIRECTORY "/%s", root, symlink_name);
199	if (lstat(sharelink, &sb) < 0) {
200	    continue; /* doesn't exist */
201	}
202	if ((sb.st_mode & S_IFLNK) == 0) {
203	    continue; /* not a symlink */
204	}
205	if (stat(sharelink, &sb) < 0) {
206	    continue;
207	}
208	sharename_len = readlink(sharelink, sharename, sizeof(sharename) - 1);
209	if (sharename_len <= 0) {
210	    continue;
211	}
212	sharename[sharename_len] = '\0';
213
214	/* remember the actual directory name */
215	snprintf(sharedir, sizeof(sharedir),
216		 "%s" NETBOOT_DIRECTORY "/%s", root, sharename);
217	sharedir_len = strlen(sharedir);
218
219	if (readonly_ok) {
220	    int		tftp_symlink_len;
221
222	    /*
223	     * Lookup the directory in the TFTP directory, assume that
224	     * it is the sharename we want to use for both TFTP and HTTP.
225	     * This isn't a safe assumption, we should independently
226	     * check/remember the TFTP/HTTP symlink names.
227	     */
228	    tftp_symlink_len
229		= lookup_symlink(NETBOOT_TFTP_PATH "/" NETBOOT_TFTP_DIRECTORY,
230				 sharedir, sharename, sizeof(sharename));
231	    if (tftp_symlink_len != 0) {
232		sharename_len = tftp_symlink_len;
233	    }
234	}
235	if (list == NULL) {
236	    list = (dynarray_t *)malloc(sizeof(*list));
237	    if (list == NULL) {
238		goto done;
239	    }
240	    bzero(list, sizeof(*list));
241	    dynarray_init(list, free, NULL);
242	}
243	entry = malloc(sizeof(*entry) + sharename_len + sharedir_len + 2);
244	if (entry == NULL) {
245	    continue;
246	}
247	bzero(entry, sizeof(*entry));
248	if (strcmp(p->f_fstypename, "hfs") == 0) {
249	    entry->is_hfs = TRUE;
250	}
251	if ((p->f_flags & MNT_RDONLY) != 0) {
252	    entry->is_readonly = TRUE;
253	}
254	entry->name = (char *)(entry + 1);
255	strncpy(entry->name, sharename, sharename_len);
256	entry->name[sharename_len] = '\0';
257	entry->path = entry->name + sharename_len + 1;
258	strncpy(entry->path, sharedir, sharedir_len);
259	entry->path[sharedir_len] = '\0';
260	dynarray_add((dynarray_t *)list, entry);
261    }
262 done:
263    if (list) {
264	if (dynarray_count((dynarray_t *)list) == 0) {
265	    free(list);
266	    list = NULL;
267	}
268    }
269    if (stat_p != NULL) {
270	free(stat_p);
271    }
272    return ((NBSPListRef)list);
273}
274
275#ifdef TEST_NBSP
276
277int
278main(int argc, char * argv[])
279{
280    bool		allow_readonly;
281    NBSPListRef 	list;
282    const char *	which;
283
284    if (argc == 1) {
285	which = ".sharepoint";
286	allow_readonly = NBSP_READONLY_OK;
287    }
288    else {
289	which = ".clients";
290	allow_readonly = NBSP_NO_READONLY;
291    }
292    list = NBSPList_init(which, allow_readonly);
293
294    if (list != NULL) {
295	NBSPList_print(list);
296	NBSPList_free(&list);
297    }
298
299    exit(0);
300}
301
302#endif /* TEST_NBSP */
303