nfs_subr.c revision 82794
1/* 2 * Copyright (c) 1997-2001 Erez Zadok 3 * Copyright (c) 1990 Jan-Simon Pendry 4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5 * Copyright (c) 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry at Imperial College, London. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgment: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * %W% (Berkeley) %G% 40 * 41 * $Id: nfs_subr.c,v 1.6.2.3 2001/04/14 21:08:22 ezk Exp $ 42 * 43 */ 44 45#ifdef HAVE_CONFIG_H 46# include <config.h> 47#endif /* HAVE_CONFIG_H */ 48#include <am_defs.h> 49#include <amd.h> 50 51/* 52 * Convert from UN*X to NFS error code. 53 * Some systems like linux define their own (see 54 * conf/mount/mount_linux.h). 55 */ 56#ifndef nfs_error 57# define nfs_error(e) ((nfsstat)(e)) 58#endif /* nfs_error */ 59 60/* forward declarations */ 61static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail); 62 63 64static char * 65do_readlink(am_node *mp, int *error_return, nfsattrstat **attrpp) 66{ 67 char *ln; 68 69 /* 70 * If there is a readlink method, then use 71 * that, otherwise if a link exists use 72 * that, otherwise use the mount point. 73 */ 74 if (mp->am_mnt->mf_ops->readlink) { 75 int retry = 0; 76 mp = (*mp->am_mnt->mf_ops->readlink) (mp, &retry); 77 if (mp == 0) { 78 *error_return = retry; 79 return 0; 80 } 81 /* reschedule_timeout_mp(); */ 82 } 83 84 if (mp->am_link) { 85 ln = mp->am_link; 86 } else { 87 ln = mp->am_mnt->mf_mount; 88 } 89 if (attrpp) 90 *attrpp = &mp->am_attr; 91 92 return ln; 93} 94 95 96voidp 97nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp) 98{ 99 static char res; 100 101 return (voidp) &res; 102} 103 104 105nfsattrstat * 106nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) 107{ 108 static nfsattrstat res; 109 am_node *mp; 110 int retry; 111 time_t now = clocktime(); 112 113#ifdef DEBUG 114 amuDebug(D_TRACE) 115 plog(XLOG_DEBUG, "getattr:"); 116#endif /* DEBUG */ 117 118 mp = fh_to_mp2(argp, &retry); 119 if (mp == 0) { 120 121#ifdef DEBUG 122 amuDebug(D_TRACE) 123 plog(XLOG_DEBUG, "\tretry=%d", retry); 124#endif /* DEBUG */ 125 126 if (retry < 0) { 127 amd_stats.d_drops++; 128 return 0; 129 } 130 res.ns_status = nfs_error(retry); 131 } else { 132 nfsattrstat *attrp = &mp->am_attr; 133 134#ifdef DEBUG 135 amuDebug(D_TRACE) 136 plog(XLOG_DEBUG, "\tstat(%s), size = %d, mtime=%ld", 137 mp->am_path, 138 (int) attrp->ns_u.ns_attr_u.na_size, 139 (long) attrp->ns_u.ns_attr_u.na_mtime.nt_seconds); 140#endif /* DEBUG */ 141 142 /* Delay unmount of what was looked up */ 143 if (mp->am_timeo_w < 4 * gopt.am_timeo_w) 144 mp->am_timeo_w += gopt.am_timeo_w; 145 mp->am_ttl = now + mp->am_timeo_w; 146 147 mp->am_stats.s_getattr++; 148 return attrp; 149 } 150 151 return &res; 152} 153 154 155nfsattrstat * 156nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp) 157{ 158 static nfsattrstat res; 159 160 if (!fh_to_mp(&argp->sag_fhandle)) 161 res.ns_status = nfs_error(ESTALE); 162 else 163 res.ns_status = nfs_error(EROFS); 164 165 return &res; 166} 167 168 169voidp 170nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp) 171{ 172 static char res; 173 174 return (voidp) &res; 175} 176 177 178nfsdiropres * 179nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) 180{ 181 static nfsdiropres res; 182 am_node *mp; 183 int retry; 184 uid_t uid; 185 gid_t gid; 186 187#ifdef DEBUG 188 amuDebug(D_TRACE) 189 plog(XLOG_DEBUG, "lookup:"); 190#endif /* DEBUG */ 191 192 /* finally, find the effective uid/gid from RPC request */ 193 if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0) 194 plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials"); 195 sprintf(opt_uid, "%d", (int) uid); 196 sprintf(opt_gid, "%d", (int) gid); 197 198 mp = fh_to_mp2(&argp->da_fhandle, &retry); 199 if (mp == 0) { 200 if (retry < 0) { 201 amd_stats.d_drops++; 202 return 0; 203 } 204 res.dr_status = nfs_error(retry); 205 } else { 206 int error; 207 am_node *ap; 208#ifdef DEBUG 209 amuDebug(D_TRACE) 210 plog(XLOG_DEBUG, "\tlookuppn(%s, %s)", mp->am_path, argp->da_name); 211#endif /* DEBUG */ 212 ap = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &error, VLOOK_CREATE); 213 if (ap == 0) { 214 if (error < 0) { 215 amd_stats.d_drops++; 216 return 0; 217 } 218 res.dr_status = nfs_error(error); 219 } else { 220 /* 221 * XXX: EXPERIMENTAL! Delay unmount of what was looked up. This 222 * should reduce the chance for race condition between unmounting an 223 * entry synchronously, and re-mounting it asynchronously. 224 */ 225 if (ap->am_ttl < mp->am_ttl) 226 ap->am_ttl = mp->am_ttl; 227 mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle); 228 res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr; 229 res.dr_status = NFS_OK; 230 } 231 mp->am_stats.s_lookup++; 232 /* reschedule_timeout_mp(); */ 233 } 234 235 return &res; 236} 237 238 239void 240quick_reply(am_node *mp, int error) 241{ 242 SVCXPRT *transp = mp->am_transp; 243 nfsdiropres res; 244 xdrproc_t xdr_result = (xdrproc_t) xdr_diropres; 245 246 /* 247 * If there's a transp structure then we can reply to the client's 248 * nfs lookup request. 249 */ 250 if (transp) { 251 if (error == 0) { 252 /* 253 * Construct a valid reply to a lookup request. Same 254 * code as in nfsproc_lookup_2_svc() above. 255 */ 256 mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle); 257 res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr; 258 res.dr_status = NFS_OK; 259 } else 260 /* 261 * Return the error that was passed to us. 262 */ 263 res.dr_status = nfs_error(error); 264 265 /* 266 * Send off our reply 267 */ 268 if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res)) 269 svcerr_systemerr(transp); 270 271 /* 272 * Free up transp. It's only used for one reply. 273 */ 274 XFREE(transp); 275 mp->am_transp = NULL; 276#ifdef DEBUG 277 dlog("Quick reply sent for %s", mp->am_mnt->mf_mount); 278#endif /* DEBUG */ 279 } 280} 281 282 283nfsreadlinkres * 284nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) 285{ 286 static nfsreadlinkres res; 287 am_node *mp; 288 int retry; 289 290#ifdef DEBUG 291 amuDebug(D_TRACE) 292 plog(XLOG_DEBUG, "readlink:"); 293#endif /* DEBUG */ 294 295 mp = fh_to_mp2(argp, &retry); 296 if (mp == 0) { 297 readlink_retry: 298 if (retry < 0) { 299 amd_stats.d_drops++; 300 return 0; 301 } 302 res.rlr_status = nfs_error(retry); 303 } else { 304 char *ln = do_readlink(mp, &retry, (nfsattrstat **) 0); 305 if (ln == 0) 306 goto readlink_retry; 307 res.rlr_status = NFS_OK; 308#ifdef DEBUG 309 amuDebug(D_TRACE) 310 if (ln) 311 plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln); 312#endif /* DEBUG */ 313 res.rlr_u.rlr_data_u = ln; 314 mp->am_stats.s_readlink++; 315 } 316 317 return &res; 318} 319 320 321nfsreadres * 322nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp) 323{ 324 static nfsreadres res; 325 326 memset((char *) &res, 0, sizeof(res)); 327 res.rr_status = nfs_error(EACCES); 328 329 return &res; 330} 331 332 333voidp 334nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp) 335{ 336 static char res; 337 338 return (voidp) &res; 339} 340 341 342nfsattrstat * 343nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp) 344{ 345 static nfsattrstat res; 346 347 if (!fh_to_mp(&argp->wra_fhandle)) 348 res.ns_status = nfs_error(ESTALE); 349 else 350 res.ns_status = nfs_error(EROFS); 351 352 return &res; 353} 354 355 356nfsdiropres * 357nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp) 358{ 359 static nfsdiropres res; 360 361 if (!fh_to_mp(&argp->ca_where.da_fhandle)) 362 res.dr_status = nfs_error(ESTALE); 363 else 364 res.dr_status = nfs_error(EROFS); 365 366 return &res; 367} 368 369 370static nfsstat * 371unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp) 372{ 373 static nfsstat res; 374 int retry; 375 376 am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE); 377 if (mp == 0) { 378 if (retry < 0) { 379 amd_stats.d_drops++; 380 return 0; 381 } 382 res = nfs_error(retry); 383 goto out; 384 } 385 386 if (mp->am_fattr.na_type != NFDIR) { 387 res = nfs_error(ENOTDIR); 388 goto out; 389 } 390 391#ifdef DEBUG 392 amuDebug(D_TRACE) 393 plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name); 394#endif /* DEBUG */ 395 396 mp = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &retry, VLOOK_DELETE); 397 if (mp == 0) { 398 /* 399 * Ignore retries... 400 */ 401 if (retry < 0) 402 retry = 0; 403 /* 404 * Usual NFS workaround... 405 */ 406 else if (retry == ENOENT) 407 retry = 0; 408 res = nfs_error(retry); 409 } else { 410 forcibly_timeout_mp(mp); 411 res = NFS_OK; 412 } 413 414out: 415 return &res; 416} 417 418 419nfsstat * 420nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) 421{ 422 return unlink_or_rmdir(argp, rqstp, TRUE); 423} 424 425 426nfsstat * 427nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp) 428{ 429 static nfsstat res; 430 431 if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle)) 432 res = nfs_error(ESTALE); 433 /* 434 * If the kernel is doing clever things with referenced files 435 * then let it pretend... 436 */ 437 else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4)) 438 res = NFS_OK; 439 /* 440 * otherwise a failure 441 */ 442 else 443 res = nfs_error(EROFS); 444 445 return &res; 446} 447 448 449nfsstat * 450nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp) 451{ 452 static nfsstat res; 453 454 if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle)) 455 res = nfs_error(ESTALE); 456 else 457 res = nfs_error(EROFS); 458 459 return &res; 460} 461 462 463nfsstat * 464nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp) 465{ 466 static nfsstat res; 467 468 if (!fh_to_mp(&argp->sla_from.da_fhandle)) 469 res = nfs_error(ESTALE); 470 else 471 res = nfs_error(EROFS); 472 473 return &res; 474} 475 476 477nfsdiropres * 478nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp) 479{ 480 static nfsdiropres res; 481 482 if (!fh_to_mp(&argp->ca_where.da_fhandle)) 483 res.dr_status = nfs_error(ESTALE); 484 else 485 res.dr_status = nfs_error(EROFS); 486 487 return &res; 488} 489 490 491nfsstat * 492nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) 493{ 494 return unlink_or_rmdir(argp, rqstp, FALSE); 495} 496 497 498nfsreaddirres * 499nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp) 500{ 501 static nfsreaddirres res; 502 static nfsentry e_res[MAX_READDIR_ENTRIES]; 503 am_node *mp; 504 int retry; 505 506#ifdef DEBUG 507 amuDebug(D_TRACE) 508 plog(XLOG_DEBUG, "readdir:"); 509#endif /* DEBUG */ 510 511 mp = fh_to_mp2(&argp->rda_fhandle, &retry); 512 if (mp == 0) { 513 if (retry < 0) { 514 amd_stats.d_drops++; 515 return 0; 516 } 517 res.rdr_status = nfs_error(retry); 518 } else { 519#ifdef DEBUG 520 amuDebug(D_TRACE) 521 plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path); 522#endif /* DEBUG */ 523 res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir) 524 (mp, argp->rda_cookie, 525 &res.rdr_u.rdr_reply_u, e_res, argp->rda_count)); 526 mp->am_stats.s_readdir++; 527 } 528 529 return &res; 530} 531 532 533nfsstatfsres * 534nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) 535{ 536 static nfsstatfsres res; 537 am_node *mp; 538 int retry; 539 mntent_t mnt; 540 541#ifdef DEBUG 542 amuDebug(D_TRACE) 543 plog(XLOG_DEBUG, "statfs:"); 544#endif /* DEBUG */ 545 546 mp = fh_to_mp2(argp, &retry); 547 if (mp == 0) { 548 if (retry < 0) { 549 amd_stats.d_drops++; 550 return 0; 551 } 552 res.sfr_status = nfs_error(retry); 553 } else { 554 nfsstatfsokres *fp; 555#ifdef DEBUG 556 amuDebug(D_TRACE) 557 plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path); 558#endif /* DEBUG */ 559 560 /* 561 * just return faked up file system information 562 */ 563 fp = &res.sfr_u.sfr_reply_u; 564 565 fp->sfrok_tsize = 1024; 566 fp->sfrok_bsize = 1024; 567 568 /* check if map is browsable and show_statfs_entries=yes */ 569 if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) && 570 mp->am_mnt && mp->am_mnt->mf_mopts) { 571 mnt.mnt_opts = mp->am_mnt->mf_mopts; 572 if (hasmntopt(&mnt, "browsable")) { 573 count_map_entries(mp, 574 &fp->sfrok_blocks, 575 &fp->sfrok_bfree, 576 &fp->sfrok_bavail); 577 } 578 } else { 579 fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */ 580 fp->sfrok_bfree = 0; 581 fp->sfrok_bavail = 0; 582 } 583 584 res.sfr_status = NFS_OK; 585 mp->am_stats.s_statfs++; 586 } 587 588 return &res; 589} 590 591 592/* 593 * count how many total entries there are in a map, and how many 594 * of them are in use. 595 */ 596static void 597count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail) 598{ 599 u_int blocks, bfree, bavail, i; 600 mntfs *mf; 601 mnt_map *mmp; 602 kv *k; 603 604 blocks = bfree = bavail = 0; 605 if (!mp) 606 goto out; 607 mf = mp->am_mnt; 608 if (!mf) 609 goto out; 610 mmp = (mnt_map *) mf->mf_private; 611 if (!mmp) 612 goto out; 613 614 /* iterate over keys */ 615 for (i = 0; i < NKVHASH; i++) { 616 for (k = mmp->kvhash[i]; k ; k = k->next) { 617 if (!k->key) 618 continue; 619 blocks++; 620 /* 621 * XXX: Need to count how many are actively in use and recompute 622 * bfree and bavail based on it. 623 */ 624 } 625 } 626 627out: 628 *out_blocks = blocks; 629 *out_bfree = bfree; 630 *out_bavail = bavail; 631} 632