xref: /illumos-gate/usr/src/cmd/fs.d/nfs/nfsref/nfsref.c (revision 2f172c55)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <strings.h>
30 #include <string.h>
31 #include <limits.h>
32 #include <libnvpair.h>
33 #include <locale.h>
34 #include <sys/stat.h>
35 #include <sys/fs_reparse.h>
36 #include <rp_plugin.h>
37 #include <uuid/uuid.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <priv.h>
42 #include <nfs/nfs4.h>
43 #include <rpcsvc/nfs4_prot.h>
44 #include "ref_subr.h"
45 
46 #ifndef TEXT_DOMAIN
47 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
48 #endif /* TEXT_DOMAIN */
49 
50 extern int errno;
51 
52 void
usage()53 usage()
54 {
55 	fprintf(stderr, gettext("Usage:\n"));
56 	fprintf(stderr,
57 	    gettext("\tnfsref [-t type] add path location [location ...]\n"));
58 	fprintf(stderr, gettext("\tnfsref [-t type] remove path\n"));
59 	fprintf(stderr, gettext("\tnfsref [-t type] lookup path\n"));
60 }
61 
62 /*
63  * Copy a string from source to destination, escaping space
64  * with a backslash and escaping the escape character as well.
65  */
66 int
add_escape(char * src,char * dest,int limit)67 add_escape(char *src, char *dest, int limit)
68 {
69 	char *sp, *dp;
70 	int destlen = 0;
71 
72 	sp = src;
73 	dp = dest;
74 
75 	while (*sp && destlen < limit) {
76 		if (*sp == '\\') {
77 			*dp++ = '\\';
78 			*dp++ = '\\';
79 			destlen++;
80 		} else if (*sp == ' ') {
81 			*dp++ = '\\';
82 			*dp++ = ' ';
83 			destlen++;
84 		} else
85 			*dp++ = *sp;
86 		destlen++;
87 		sp++;
88 	}
89 	if (limit <= 0)
90 		return (-1);
91 	return (destlen);
92 }
93 
94 int
addref(char * sl_path,char * svc_type,int optind,int argc,char * argv[])95 addref(char *sl_path, char *svc_type, int optind, int argc, char *argv[])
96 {
97 	int err, fd, i, len, oldlen, notfound = 0;
98 	char *text, *location;
99 	nvlist_t *nvl = NULL;
100 	char buf[SYMLINK_MAX];
101 	struct stat sbuf;
102 
103 	/* Get an nvlist */
104 	nvl = reparse_init();
105 	if (nvl == NULL)
106 		return (ENOMEM);
107 
108 	/* Get the reparse point data, if the RP exists */
109 	err = readlink(sl_path, buf, SYMLINK_MAX);
110 	if (err == -1) {
111 		if (errno == ENOENT) {
112 			notfound = 1;
113 			err = 0;
114 		} else {
115 			reparse_free(nvl);
116 			return (errno);
117 		}
118 	} else {
119 		buf[err] = '\0';
120 	}
121 
122 	/* Get any data into nvlist */
123 	if (notfound == 0)
124 		err = reparse_parse(buf, nvl);
125 	if (err != 0) {
126 		reparse_free(nvl);
127 		return (err);
128 	}
129 
130 	/*
131 	 * Accumulate multiple locations on the command line into 'buf'
132 	 */
133 	oldlen = len = 0;
134 	location = NULL;
135 	for (i = optind; i < argc; i++) {
136 		bzero(buf, sizeof (buf));
137 		len += add_escape(argv[i], buf, SYMLINK_MAX) + 2;
138 		location = realloc(location, len);
139 		location[oldlen] = '\0';
140 		oldlen = len;
141 		strlcat(location, buf, len);
142 		strlcat(location, " ", len);
143 	}
144 	location[len - 2] = '\0';
145 
146 	/* Add to the list */
147 	err = reparse_add(nvl, svc_type, location);
148 	if (err) {
149 		reparse_free(nvl);
150 		return (err);
151 	}
152 
153 	/* Get the new or modified symlink contents */
154 	err = reparse_unparse(nvl, &text);
155 	reparse_free(nvl);
156 	if (err)
157 		return (err);
158 
159 	/* Delete first if found */
160 	if (notfound == 0) {
161 		err =  reparse_delete(sl_path);
162 		if (err) {
163 			free(text);
164 			return (err);
165 		}
166 	}
167 
168 	/* Finally, write out the reparse point */
169 	err = reparse_create(sl_path, text);
170 	free(text);
171 	if (err)
172 		return (err);
173 
174 	err = lstat(sl_path, &sbuf);
175 	if (err == 0 && strcasecmp(sbuf.st_fstype, "ZFS") != 0)
176 		printf(gettext(
177 		    "Warning: referrals do not work on this filesystem\n"));
178 
179 	if (notfound)
180 		printf(gettext("Created reparse point %s\n"), sl_path);
181 	else
182 		printf(gettext("Added to reparse point %s\n"), sl_path);
183 
184 	return (0);
185 }
186 
187 int
delref(char * sl_path,char * svc_type)188 delref(char *sl_path, char *svc_type)
189 {
190 	char *cp;
191 	char *svc_data;
192 	int err;
193 	nvlist_t *nvl;
194 	nvpair_t *curr;
195 	char buf[SYMLINK_MAX];
196 	int fd, fd2;
197 	FILE *fp, *fp2;
198 	char uuid[UUID_PRINTABLE_STRING_LENGTH], path[256], loc[2048];
199 
200 	/* Get an nvlist */
201 	if (!(nvl = reparse_init()))
202 		return (ENOMEM);
203 
204 	/* Get the symlink data (should be there) */
205 	err = readlink(sl_path, buf, SYMLINK_MAX);
206 	if (err == -1) {
207 		reparse_free(nvl);
208 		return (errno);
209 	}
210 	buf[err] = '\0';
211 
212 	/* Get the records into the nvlist */
213 	err = reparse_parse(buf, nvl);
214 	if (err) {
215 		reparse_free(nvl);
216 		return (err);
217 	}
218 
219 	/* Remove from nvlist */
220 	err = reparse_remove(nvl, svc_type);
221 	if (err) {
222 		reparse_free(nvl);
223 		return (err);
224 	}
225 
226 	/* Any list entries left? If so, turn nvlist back to string. */
227 	curr = nvlist_next_nvpair(nvl, NULL);
228 	if (curr != NULL) {
229 		err = reparse_unparse(nvl, &cp);
230 		reparse_free(nvl);
231 		if (err)
232 			return (err);
233 	} else {
234 		reparse_free(nvl);
235 		cp = NULL;
236 	}
237 
238 	/* Finally, delete and perhaps recreate the reparse point */
239 	err = reparse_delete(sl_path);
240 	if (err) {
241 		free(cp);
242 		return (err);
243 	}
244 
245 	if (cp != NULL) {
246 		err = reparse_create(sl_path, cp);
247 		free(cp);
248 		if (err)
249 			return (err);
250 	}
251 	printf(gettext("Removed svc_type '%s' from %s\n"), svc_type, sl_path);
252 	return (err);
253 }
254 
255 int
lookup(char * sl_path,char * svc_type,int type_set)256 lookup(char *sl_path, char *svc_type, int type_set)
257 {
258 	int err;
259 	size_t bufsize;
260 	char buf[1024];
261 	char *type, *svc_data;
262 	nvlist_t *nvl;
263 	nvpair_t *curr;
264 	fs_locations4 fsl;
265 	XDR xdr;
266 
267 	if (!(nvl = reparse_init()))
268 		return (-1);
269 
270 	/* Get reparse point data */
271 	err = readlink(sl_path, buf, SYMLINK_MAX);
272 	if (err == -1)
273 		return (errno);
274 	buf[err] = '\0';
275 
276 	/* Parse it to an nvlist */
277 	err = reparse_parse(buf, nvl);
278 	if (err) {
279 		reparse_free(nvl);
280 		return (err);
281 	}
282 
283 	/* Look for entries of the requested service type */
284 	curr = NULL;
285 	while ((curr = nvlist_next_nvpair(nvl, curr)) != NULL) {
286 		type = nvpair_name(curr);
287 		if (type_set && strcasecmp(type, svc_type) == 0)
288 			break;
289 		if (!type_set && strncasecmp(type, "nfs", 3) == 0)
290 			break;
291 	}
292 	if (curr == NULL) {
293 		reparse_free(nvl);
294 		return (ENOENT);
295 	}
296 
297 	/* Get the service data and look it up */
298 	nvpair_value_string(curr, &svc_data);
299 
300 	bufsize = sizeof (buf);
301 	err = reparse_deref(type, svc_data, buf, &bufsize);
302 	reparse_free(nvl);
303 	if (err)
304 		return (err);
305 
306 	xdrmem_create(&xdr, buf, bufsize, XDR_DECODE);
307 	err = xdr_fs_locations4(&xdr, &fsl);
308 	XDR_DESTROY(&xdr);
309 	if (err != TRUE)
310 		return (ENOENT);
311 	printf(gettext("%s points to: "), sl_path);
312 	print_referral_summary(&fsl);
313 	return (0);
314 }
315 
316 extern char *optarg;
317 extern int optind, optopt;
318 
319 int
main(int argc,char * argv[])320 main(int argc, char *argv[])
321 {
322 	char c, *command, *sl_path, *svc_type;
323 	int type_set, err;
324 
325 	(void) setlocale(LC_ALL, "");
326 	(void) textdomain(TEXT_DOMAIN);
327 
328 	svc_type = "nfs-basic";		/* Default from SMF some day */
329 	type_set = 0;			/* Lookup any nfs type */
330 
331 	/* Look for options (just the service type now) */
332 	while ((c = getopt(argc, argv, "t:")) != -1) {
333 		switch (c) {
334 		case 't':
335 			svc_type = optarg;
336 			type_set = 1;
337 			break;
338 
339 		default:
340 			usage();
341 			exit(1);
342 		}
343 	}
344 
345 	/* Make sure there's at least a command and one argument */
346 	if (optind + 1 >= argc) {
347 		usage();
348 		exit(1);
349 	}
350 
351 	err = rp_plugin_init();
352 	switch (err) {
353 	case RP_OK:
354 		break;
355 	case RP_NO_PLUGIN_DIR:
356 		fprintf(stderr,
357 		    gettext("Warning: no plugin directory, continuing...\n"));
358 		break;
359 	case RP_NO_PLUGIN:
360 		fprintf(stderr,
361 		    gettext("Warning: no plugin found, continuing...\n"));
362 		break;
363 	case RP_NO_MEMORY:
364 		fprintf(stderr,
365 		    gettext("rp_plugin_init failed, no memory\n"));
366 		exit(0);
367 	default:
368 		fprintf(stderr,
369 		    gettext("rp_plugin_init failed, error %d\n"), err);
370 		exit(0);
371 	}
372 
373 	command = argv[optind++];
374 	sl_path = argv[optind++];
375 
376 	if (strcmp(command, "add") == 0) {
377 
378 		if (optind >= argc) {
379 			usage();
380 			exit(1);
381 		}
382 
383 		err = addref(sl_path, svc_type, optind, argc, argv);
384 
385 	} else if (strcmp(command, "remove") == 0) {
386 
387 		err = delref(sl_path, svc_type);
388 
389 	} else if (strcmp(command, "lookup") == 0) {
390 
391 		err = lookup(sl_path, svc_type, type_set);
392 
393 	} else {
394 		usage();
395 		exit(1);
396 	}
397 	if (err != 0)
398 		fprintf(stderr, gettext("Command %s failed: %s\n"), command,
399 		    strerror(err));
400 	return (err);
401 }
402