/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <limits.h>
#include <libnvpair.h>
#include <locale.h>
#include <sys/stat.h>
#include <sys/fs_reparse.h>
#include <rp_plugin.h>
#include <uuid/uuid.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <priv.h>
#include <nfs/nfs4.h>
#include <rpcsvc/nfs4_prot.h>
#include "ref_subr.h"
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN "SUNW_OST_OSCMD"
#endif /* TEXT_DOMAIN */
extern int errno;
void
usage()
{
fprintf(stderr, gettext("Usage:\n"));
fprintf(stderr,
gettext("\tnfsref [-t type] add path location [location ...]\n"));
fprintf(stderr, gettext("\tnfsref [-t type] remove path\n"));
fprintf(stderr, gettext("\tnfsref [-t type] lookup path\n"));
}
/*
* Copy a string from source to destination, escaping space
* with a backslash and escaping the escape character as well.
*/
int
add_escape(char *src, char *dest, int limit)
{
char *sp, *dp;
int destlen = 0;
sp = src;
dp = dest;
while (*sp && destlen < limit) {
if (*sp == '\\') {
*dp++ = '\\';
*dp++ = '\\';
destlen++;
} else if (*sp == ' ') {
*dp++ = '\\';
*dp++ = ' ';
destlen++;
} else
*dp++ = *sp;
destlen++;
sp++;
}
if (limit <= 0)
return (-1);
return (destlen);
}
int
addref(char *sl_path, char *svc_type, int optind, int argc, char *argv[])
{
int err, fd, i, len, oldlen, notfound = 0;
char *text, *location;
nvlist_t *nvl = NULL;
char buf[SYMLINK_MAX];
struct stat sbuf;
/* Get an nvlist */
nvl = reparse_init();
if (nvl == NULL)
return (ENOMEM);
/* Get the reparse point data, if the RP exists */
err = readlink(sl_path, buf, SYMLINK_MAX);
if (err == -1) {
if (errno == ENOENT) {
notfound = 1;
err = 0;
} else {
reparse_free(nvl);
return (errno);
}
} else {
buf[err] = '\0';
}
/* Get any data into nvlist */
if (notfound == 0)
err = reparse_parse(buf, nvl);
if (err != 0) {
reparse_free(nvl);
return (err);
}
/*
* Accumulate multiple locations on the command line into 'buf'
*/
oldlen = len = 0;
location = NULL;
for (i = optind; i < argc; i++) {
bzero(buf, sizeof (buf));
len += add_escape(argv[i], buf, SYMLINK_MAX) + 2;
location = realloc(location, len);
location[oldlen] = '\0';
oldlen = len;
strlcat(location, buf, len);
strlcat(location, " ", len);
}
location[len - 2] = '\0';
/* Add to the list */
err = reparse_add(nvl, svc_type, location);
if (err) {
reparse_free(nvl);
return (err);
}
/* Get the new or modified symlink contents */
err = reparse_unparse(nvl, &text);
reparse_free(nvl);
if (err)
return (err);
/* Delete first if found */
if (notfound == 0) {
err = reparse_delete(sl_path);
if (err) {
free(text);
return (err);
}
}
/* Finally, write out the reparse point */
err = reparse_create(sl_path, text);
free(text);
if (err)
return (err);
err = lstat(sl_path, &sbuf);
if (err == 0 && strcasecmp(sbuf.st_fstype, "ZFS") != 0)
printf(gettext(
"Warning: referrals do not work on this filesystem\n"));
if (notfound)
printf(gettext("Created reparse point %s\n"), sl_path);
else
printf(gettext("Added to reparse point %s\n"), sl_path);
return (0);
}
int
delref(char *sl_path, char *svc_type)
{
char *cp;
char *svc_data;
int err;
nvlist_t *nvl;
nvpair_t *curr;
char buf[SYMLINK_MAX];
int fd, fd2;
FILE *fp, *fp2;
char uuid[UUID_PRINTABLE_STRING_LENGTH], path[256], loc[2048];
/* Get an nvlist */
if (!(nvl = reparse_init()))
return (ENOMEM);
/* Get the symlink data (should be there) */
err = readlink(sl_path, buf, SYMLINK_MAX);
if (err == -1) {
reparse_free(nvl);
return (errno);
}
buf[err] = '\0';
/* Get the records into the nvlist */
err = reparse_parse(buf, nvl);
if (err) {
reparse_free(nvl);
return (err);
}
/* Remove from nvlist */
err = reparse_remove(nvl, svc_type);
if (err) {
reparse_free(nvl);
return (err);
}
/* Any list entries left? If so, turn nvlist back to string. */
curr = nvlist_next_nvpair(nvl, NULL);
if (curr != NULL) {
err = reparse_unparse(nvl, &cp);
reparse_free(nvl);
if (err)
return (err);
} else {
reparse_free(nvl);
cp = NULL;
}
/* Finally, delete and perhaps recreate the reparse point */
err = reparse_delete(sl_path);
if (err) {
free(cp);
return (err);
}
if (cp != NULL) {
err = reparse_create(sl_path, cp);
free(cp);
if (err)
return (err);
}
printf(gettext("Removed svc_type '%s' from %s\n"), svc_type, sl_path);
return (err);
}
int
lookup(char *sl_path, char *svc_type, int type_set)
{
int err;
size_t bufsize;
char buf[1024];
char *type, *svc_data;
nvlist_t *nvl;
nvpair_t *curr;
fs_locations4 fsl;
XDR xdr;
if (!(nvl = reparse_init()))
return (-1);
/* Get reparse point data */
err = readlink(sl_path, buf, SYMLINK_MAX);
if (err == -1)
return (errno);
buf[err] = '\0';
/* Parse it to an nvlist */
err = reparse_parse(buf, nvl);
if (err) {
reparse_free(nvl);
return (err);
}
/* Look for entries of the requested service type */
curr = NULL;
while ((curr = nvlist_next_nvpair(nvl, curr)) != NULL) {
type = nvpair_name(curr);
if (type_set && strcasecmp(type, svc_type) == 0)
break;
if (!type_set && strncasecmp(type, "nfs", 3) == 0)
break;
}
if (curr == NULL) {
reparse_free(nvl);
return (ENOENT);
}
/* Get the service data and look it up */
nvpair_value_string(curr, &svc_data);
bufsize = sizeof (buf);
err = reparse_deref(type, svc_data, buf, &bufsize);
reparse_free(nvl);
if (err)
return (err);
xdrmem_create(&xdr, buf, bufsize, XDR_DECODE);
err = xdr_fs_locations4(&xdr, &fsl);
XDR_DESTROY(&xdr);
if (err != TRUE)
return (ENOENT);
printf(gettext("%s points to: "), sl_path);
print_referral_summary(&fsl);
return (0);
}
extern char *optarg;
extern int optind, optopt;
int
main(int argc, char *argv[])
{
char c, *command, *sl_path, *svc_type;
int type_set, err;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
svc_type = "nfs-basic"; /* Default from SMF some day */
type_set = 0; /* Lookup any nfs type */
/* Look for options (just the service type now) */
while ((c = getopt(argc, argv, "t:")) != -1) {
switch (c) {
case 't':
svc_type = optarg;
type_set = 1;
break;
default:
usage();
exit(1);
}
}
/* Make sure there's at least a command and one argument */
if (optind + 1 >= argc) {
usage();
exit(1);
}
err = rp_plugin_init();
switch (err) {
case RP_OK:
break;
case RP_NO_PLUGIN_DIR:
fprintf(stderr,
gettext("Warning: no plugin directory, continuing...\n"));
break;
case RP_NO_PLUGIN:
fprintf(stderr,
gettext("Warning: no plugin found, continuing...\n"));
break;
case RP_NO_MEMORY:
fprintf(stderr,
gettext("rp_plugin_init failed, no memory\n"));
exit(0);
default:
fprintf(stderr,
gettext("rp_plugin_init failed, error %d\n"), err);
exit(0);
}
command = argv[optind++];
sl_path = argv[optind++];
if (strcmp(command, "add") == 0) {
if (optind >= argc) {
usage();
exit(1);
}
err = addref(sl_path, svc_type, optind, argc, argv);
} else if (strcmp(command, "remove") == 0) {
err = delref(sl_path, svc_type);
} else if (strcmp(command, "lookup") == 0) {
err = lookup(sl_path, svc_type, type_set);
} else {
usage();
exit(1);
}
if (err != 0)
fprintf(stderr, gettext("Command %s failed: %s\n"), command,
strerror(err));
return (err);
}