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