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