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 <sys/types.h> 32 #include <sys/stat.h> 33 #include <sys/errno.h> 34 #include <limits.h> 35 #include <libnvpair.h> 36 #include <dlfcn.h> 37 #include <link.h> 38 #include <rp_plugin.h> 39 #include <fcntl.h> 40 #include <uuid/uuid.h> 41 #include <rpc/types.h> 42 #include <rpc/xdr.h> 43 #include <rpc/auth.h> 44 #include <rpc/clnt.h> 45 #include <rpc/rpc_msg.h> 46 #include <sys/param.h> 47 #include <nfs/nfs4.h> 48 #include <rpcsvc/nfs4_prot.h> 49 #include "ref_subr.h" 50 51 extern int errno; 52 53 #define SERVICE_TYPE "nfs-basic" 54 55 char *nfs_basic_service_type(void); 56 boolean_t nfs_basic_supports_svc(const char *); 57 int nfs_basic_deref(const char *, const char *, char *, size_t *); 58 int nfs_basic_form(const char *, const char *, char *, size_t *); 59 60 struct rp_plugin_ops rp_plugin_ops = { 61 RP_PLUGIN_V1, 62 NULL, /* rpo_init */ 63 NULL, /* rpo_fini */ 64 nfs_basic_service_type, 65 nfs_basic_supports_svc, 66 nfs_basic_form, 67 nfs_basic_deref 68 }; 69 70 /* 71 * What service type does this module support? 72 */ 73 char * 74 nfs_basic_service_type() 75 { 76 return (SERVICE_TYPE); 77 } 78 79 /* 80 * Does this module support a particular service type? 81 */ 82 boolean_t 83 nfs_basic_supports_svc(const char *svc_type) 84 { 85 if (!svc_type) 86 return (0); 87 return (!strncasecmp(svc_type, SERVICE_TYPE, strlen(SERVICE_TYPE))); 88 } 89 90 /* 91 * Take a string with a set of locations like this: 92 * host1:/path1 host2:/path2 host3:/path3 93 * and convert it to an fs_locations4 for the deref routine. 94 */ 95 static fs_locations4 * 96 get_fs_locations(char *buf) 97 { 98 fs_locations4 *result = NULL; 99 fs_location4 *fsl_array; 100 int i, gothost; 101 int fsl_count = 0, escape = 0, delimiter = 0; 102 int len; 103 char *p, *sp, *dp, buf2[SYMLINK_MAX]; 104 105 if (buf == NULL) 106 return (NULL); 107 #ifdef DEBUG 108 printf("get_fs_locations: input %s\n", buf); 109 #endif 110 /* 111 * Count fs_location entries by counting spaces. 112 * Remember that escaped spaces ("\ ") may exist. 113 * We mark the location boundaries with null bytes. 114 * Variable use: 115 * escape - set if we have found a backspace, 116 * part of either "\ " or "\\" 117 * delimiter - set if we have found a space and 118 * used to skip multiple spaces 119 */ 120 for (sp = buf; sp && *sp; sp++) { 121 if (*sp == '\\') { 122 escape = 1; 123 delimiter = 0; 124 continue; 125 } 126 if (*sp == ' ') { 127 if (delimiter == 1) 128 continue; 129 if (escape == 0) { 130 delimiter = 1; 131 fsl_count++; 132 *sp = '\0'; 133 } else 134 escape = 0; 135 } else 136 delimiter = 0; 137 } 138 len = sp - buf; 139 sp--; 140 if (escape == 0 && *sp != '\0') 141 fsl_count++; 142 #ifdef DEBUG 143 printf("get_fs_locations: fsl_count %d\n", fsl_count); 144 #endif 145 if (fsl_count == 0) 146 goto out; 147 148 /* Alloc space for everything */ 149 result = calloc(1, sizeof (fs_locations4)); 150 if (result == NULL) 151 goto out; 152 fsl_array = calloc(fsl_count, sizeof (fs_location4)); 153 if (fsl_array == NULL) { 154 free(result); 155 result = NULL; 156 goto out; 157 } 158 result->locations.locations_len = fsl_count; 159 result->locations.locations_val = fsl_array; 160 result->fs_root.pathname4_len = 0; 161 result->fs_root.pathname4_val = NULL; 162 163 /* 164 * Copy input, removing escapes from host:/path/to/my\ files 165 */ 166 sp = buf; 167 dp = buf2; 168 bzero(buf2, sizeof (buf2)); 169 170 i = gothost = 0; 171 while ((sp && *sp && (sp - buf < len)) || gothost) { 172 173 if (!gothost) { 174 /* Drop leading spaces */ 175 if (*sp == ' ') { 176 sp++; 177 continue; 178 } 179 180 /* Look for the rightmost colon for host */ 181 p = strrchr(sp, ':'); 182 if (!p) { 183 #ifdef DEBUG 184 printf("get_fs_locations: skipping %s\n", sp); 185 #endif 186 fsl_count--; 187 sp += strlen(sp) + 1; 188 } else { 189 bcopy(sp, dp, p - sp); 190 sp = p + 1; 191 #ifdef DEBUG 192 printf("get_fs_locations: host %s\n", buf2); 193 #endif 194 fsl_array[i].server.server_len = 1; 195 fsl_array[i].server.server_val = 196 malloc(sizeof (utf8string)); 197 if (fsl_array[i].server.server_val == NULL) { 198 int j; 199 200 free(result); 201 result = NULL; 202 for (j = 0; j < i; j++) 203 free(fsl_array[j]. 204 server.server_val); 205 free(fsl_array); 206 goto out; 207 } 208 str_to_utf8(buf2, 209 fsl_array[i].server.server_val); 210 gothost = 1; 211 dp = buf2; 212 bzero(buf2, sizeof (buf2)); 213 } 214 continue; 215 } 216 217 /* End of string should mean a pathname */ 218 if (*sp == '\0' && gothost) { 219 #ifdef DEBUG 220 printf("get_fs_locations: path %s\n", buf2); 221 #endif 222 (void) make_pathname4(buf2, &fsl_array[i].rootpath); 223 i++; 224 gothost = 0; 225 dp = buf2; 226 bzero(buf2, sizeof (buf2)); 227 if (sp - buf < len) 228 sp++; 229 continue; 230 } 231 232 /* Skip a single escape character */ 233 if (*sp == '\\') 234 sp++; 235 236 /* Plain char, just copy it */ 237 *dp++ = *sp++; 238 } 239 240 /* 241 * If we're still expecting a path name, we don't have a 242 * server:/path pair and should discard the server and 243 * note that we got fewer locations than expected. 244 */ 245 if (gothost) { 246 fsl_count--; 247 free(fsl_array[i].server.server_val); 248 fsl_array[i].server.server_val = NULL; 249 fsl_array[i].server.server_len = 0; 250 } 251 252 /* 253 * If we have zero entries, we never got a whole server:/path 254 * pair, and so cannot have anything else allocated. 255 */ 256 if (fsl_count <= 0) { 257 free(result); 258 free(fsl_array); 259 return (NULL); 260 } 261 262 /* 263 * Make sure we reflect the right number of locations. 264 */ 265 if (fsl_count < result->locations.locations_len) 266 result->locations.locations_len = fsl_count; 267 268 out: 269 return (result); 270 } 271 272 /* 273 * Deref function for nfs-basic service type returns an fs_locations4. 274 */ 275 int 276 nfs_basic_deref(const char *svc_type, const char *svc_data, char *buf, 277 size_t *bufsz) 278 { 279 int slen, err; 280 fs_locations4 *fsl; 281 XDR xdr; 282 283 if ((!svc_type) || (!svc_data) || (!buf) || (!bufsz) || (*bufsz == 0)) 284 return (EINVAL); 285 286 if (strcasecmp(svc_type, SERVICE_TYPE)) 287 return (ENOTSUP); 288 289 fsl = get_fs_locations((char *)svc_data); 290 if (fsl == NULL) 291 return (ENOENT); 292 #ifdef DEBUG 293 printf("nfs_basic_deref: past get_fs_locations()\n"); 294 #endif 295 slen = xdr_sizeof(xdr_fs_locations4, (void *)fsl); 296 if (slen > *bufsz) { 297 *bufsz = slen; 298 xdr_free(xdr_fs_locations4, (char *)fsl); 299 return (EOVERFLOW); 300 } 301 #ifdef DEBUG 302 printf("nfs_basic_deref: past buffer check\n"); 303 print_referral_summary(fsl); 304 #endif 305 xdrmem_create(&xdr, buf, *bufsz, XDR_ENCODE); 306 err = xdr_fs_locations4(&xdr, fsl); 307 XDR_DESTROY(&xdr); 308 xdr_free(xdr_fs_locations4, (char *)fsl); 309 if (err != TRUE) 310 return (EINVAL); 311 *bufsz = slen; 312 #ifdef DEBUG 313 printf("nfs_basic_deref: past xdr_fs_locations4() and done\n"); 314 #endif 315 return (0); 316 } 317 318 /* 319 * Form function for nfs-basic service type. 320 */ 321 int 322 nfs_basic_form(const char *svc_type, const char *svc_data, char *buf, 323 size_t *bufsz) 324 { 325 int slen; 326 327 if ((!svc_type) || (!svc_data) || (!buf) || (*bufsz == 0)) 328 return (EINVAL); 329 330 if (strcmp(svc_type, SERVICE_TYPE)) 331 return (ENOTSUP); 332 333 slen = strlen(svc_data) + 1; 334 if (slen > *bufsz) { 335 *bufsz = slen; 336 return (EOVERFLOW); 337 } 338 *bufsz = slen; 339 strncpy(buf, svc_data, slen); 340 return (0); 341 } 342