nfs_subr.c revision 38494
1/* 2 * Copyright (c) 1997-1998 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 acknowledgement: 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 5.2.2.1 1992/02/09 15:08:53 jsp beta $ 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 112#ifdef DEBUG 113 amuDebug(D_TRACE) 114 plog(XLOG_DEBUG, "getattr:"); 115#endif /* DEBUG */ 116 117 mp = fh_to_mp2(argp, &retry); 118 if (mp == 0) { 119 120#ifdef DEBUG 121 amuDebug(D_TRACE) 122 plog(XLOG_DEBUG, "\tretry=%d", retry); 123#endif /* DEBUG */ 124 125 if (retry < 0) 126 return 0; 127 res.ns_status = nfs_error(retry); 128 } else { 129 nfsattrstat *attrp = &mp->am_attr; 130 131#ifdef DEBUG 132 amuDebug(D_TRACE) 133 plog(XLOG_DEBUG, "\tstat(%s), size = %d", mp->am_path, 134 attrp->ns_u.ns_attr_u.na_size); 135#endif /* DEBUG */ 136 137 mp->am_stats.s_getattr++; 138 return attrp; 139 } 140 141#ifndef MNT2_NFS_OPT_SYMTTL 142 /* 143 * This code is needed to defeat Solaris 2.4's (and newer) symlink values 144 * cache. It forces the last-modifed time of the symlink to be current. 145 * It is not needed if the O/S has an nfs flag to turn off the 146 * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez. 147 */ 148 if (++res.ns_u.ns_attr_u.na_mtime.nt_useconds == 0) 149 ++res.ns_u.ns_attr_u.na_mtime.nt_seconds; 150#endif /* not MNT2_NFS_OPT_SYMTTL */ 151 152 return &res; 153} 154 155 156nfsattrstat * 157nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp) 158{ 159 static nfsattrstat res; 160 161 if (!fh_to_mp(&argp->sag_fhandle)) 162 res.ns_status = nfs_error(ESTALE); 163 else 164 res.ns_status = nfs_error(EROFS); 165 166 return &res; 167} 168 169 170voidp 171nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp) 172{ 173 static char res; 174 175 return (voidp) &res; 176} 177 178 179nfsdiropres * 180nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) 181{ 182 static nfsdiropres res; 183 am_node *mp; 184 int retry; 185 186#ifdef DEBUG 187 amuDebug(D_TRACE) 188 plog(XLOG_DEBUG, "lookup:"); 189#endif /* DEBUG */ 190 191 mp = fh_to_mp2(&argp->da_fhandle, &retry); 192 if (mp == 0) { 193 if (retry < 0) 194 return 0; 195 res.dr_status = nfs_error(retry); 196 } else { 197 int error; 198 am_node *ap; 199#ifdef DEBUG 200 amuDebug(D_TRACE) 201 plog(XLOG_DEBUG, "\tlookuppn(%s, %s)", mp->am_path, argp->da_name); 202#endif /* DEBUG */ 203 ap = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &error, VLOOK_CREATE); 204 if (ap == 0) { 205 if (error < 0) { 206#ifdef DEBUG 207 dlog("Not sending RPC reply"); 208#endif /* DEBUG */ 209 amd_stats.d_drops++; 210 return 0; 211 } 212 res.dr_status = nfs_error(error); 213 } else { 214 mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle); 215 res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr; 216 res.dr_status = NFS_OK; 217 } 218 mp->am_stats.s_lookup++; 219 /* reschedule_timeout_mp(); */ 220 } 221 222 return &res; 223} 224 225 226void 227quick_reply(am_node *mp, int error) 228{ 229 SVCXPRT *transp = mp->am_transp; 230 nfsdiropres res; 231 xdrproc_t xdr_result = (xdrproc_t) xdr_diropres; 232 233 /* 234 * If there's a transp structure then we can reply to the client's 235 * nfs lookup request. 236 */ 237 if (transp) { 238 if (error == 0) { 239 /* 240 * Construct a valid reply to a lookup request. Same 241 * code as in nfsproc_lookup_2_svc() above. 242 */ 243 mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle); 244 res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr; 245 res.dr_status = NFS_OK; 246 } else 247 /* 248 * Return the error that was passed to us. 249 */ 250 res.dr_status = nfs_error(error); 251 252 /* 253 * Send off our reply 254 */ 255 if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res)) 256 svcerr_systemerr(transp); 257 258 /* 259 * Free up transp. It's only used for one reply. 260 */ 261 XFREE(transp); 262 mp->am_transp = NULL; 263#ifdef DEBUG 264 dlog("Quick reply sent for %s", mp->am_mnt->mf_mount); 265#endif /* DEBUG */ 266 } 267} 268 269 270nfsreadlinkres * 271nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) 272{ 273 static nfsreadlinkres res; 274 am_node *mp; 275 int retry; 276 277#ifdef DEBUG 278 amuDebug(D_TRACE) 279 plog(XLOG_DEBUG, "readlink:"); 280#endif /* DEBUG */ 281 282 mp = fh_to_mp2(argp, &retry); 283 if (mp == 0) { 284 readlink_retry: 285 if (retry < 0) 286 return 0; 287 res.rlr_status = nfs_error(retry); 288 } else { 289 char *ln = do_readlink(mp, &retry, (nfsattrstat **) 0); 290 if (ln == 0) 291 goto readlink_retry; 292 res.rlr_status = NFS_OK; 293#ifdef DEBUG 294 amuDebug(D_TRACE) 295 if (ln) 296 plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln); 297#endif /* DEBUG */ 298 res.rlr_u.rlr_data_u = ln; 299 mp->am_stats.s_readlink++; 300 } 301 302 return &res; 303} 304 305 306nfsreadres * 307nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp) 308{ 309 static nfsreadres res; 310 311 memset((char *) &res, 0, sizeof(res)); 312 res.rr_status = nfs_error(EACCES); 313 314 return &res; 315} 316 317 318voidp 319nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp) 320{ 321 static char res; 322 323 return (voidp) &res; 324} 325 326 327nfsattrstat * 328nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp) 329{ 330 static nfsattrstat res; 331 332 if (!fh_to_mp(&argp->wra_fhandle)) 333 res.ns_status = nfs_error(ESTALE); 334 else 335 res.ns_status = nfs_error(EROFS); 336 337 return &res; 338} 339 340 341nfsdiropres * 342nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp) 343{ 344 static nfsdiropres res; 345 346 if (!fh_to_mp(&argp->ca_where.da_fhandle)) 347 res.dr_status = nfs_error(ESTALE); 348 else 349 res.dr_status = nfs_error(EROFS); 350 351 return &res; 352} 353 354 355static nfsstat * 356unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp) 357{ 358 static nfsstat res; 359 int retry; 360 361 am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE); 362 if (mp == 0) { 363 if (retry < 0) 364 return 0; 365 res = nfs_error(retry); 366 goto out; 367 } 368 369 if (mp->am_fattr.na_type != NFDIR) { 370 res = nfs_error(ENOTDIR); 371 goto out; 372 } 373 374#ifdef DEBUG 375 amuDebug(D_TRACE) 376 plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name); 377#endif /* DEBUG */ 378 379 mp = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &retry, VLOOK_DELETE); 380 if (mp == 0) { 381 /* 382 * Ignore retries... 383 */ 384 if (retry < 0) 385 retry = 0; 386 /* 387 * Usual NFS workaround... 388 */ 389 else if (retry == ENOENT) 390 retry = 0; 391 res = nfs_error(retry); 392 } else { 393 forcibly_timeout_mp(mp); 394 res = NFS_OK; 395 } 396 397out: 398 return &res; 399} 400 401 402nfsstat * 403nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) 404{ 405 return unlink_or_rmdir(argp, rqstp, TRUE); 406} 407 408 409nfsstat * 410nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp) 411{ 412 static nfsstat res; 413 414 if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle)) 415 res = nfs_error(ESTALE); 416 /* 417 * If the kernel is doing clever things with referenced files 418 * then let it pretend... 419 */ 420 else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4)) 421 res = NFS_OK; 422 /* 423 * otherwise a failure 424 */ 425 else 426 res = nfs_error(EROFS); 427 428 return &res; 429} 430 431 432nfsstat * 433nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp) 434{ 435 static nfsstat res; 436 437 if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle)) 438 res = nfs_error(ESTALE); 439 else 440 res = nfs_error(EROFS); 441 442 return &res; 443} 444 445 446nfsstat * 447nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp) 448{ 449 static nfsstat res; 450 451 if (!fh_to_mp(&argp->sla_from.da_fhandle)) 452 res = nfs_error(ESTALE); 453 else 454 res = nfs_error(EROFS); 455 456 return &res; 457} 458 459 460nfsdiropres * 461nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp) 462{ 463 static nfsdiropres res; 464 465 if (!fh_to_mp(&argp->ca_where.da_fhandle)) 466 res.dr_status = nfs_error(ESTALE); 467 else 468 res.dr_status = nfs_error(EROFS); 469 470 return &res; 471} 472 473 474nfsstat * 475nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) 476{ 477 return unlink_or_rmdir(argp, rqstp, FALSE); 478} 479 480 481nfsreaddirres * 482nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp) 483{ 484 static nfsreaddirres res; 485 static nfsentry e_res[MAX_READDIR_ENTRIES]; 486 am_node *mp; 487 int retry; 488 489#ifdef DEBUG 490 amuDebug(D_TRACE) 491 plog(XLOG_DEBUG, "readdir:"); 492#endif /* DEBUG */ 493 494 mp = fh_to_mp2(&argp->rda_fhandle, &retry); 495 if (mp == 0) { 496 if (retry < 0) 497 return 0; 498 res.rdr_status = nfs_error(retry); 499 } else { 500#ifdef DEBUG 501 amuDebug(D_TRACE) 502 plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path); 503#endif /* DEBUG */ 504 res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir) 505 (mp, argp->rda_cookie, 506 &res.rdr_u.rdr_reply_u, e_res, argp->rda_count)); 507 mp->am_stats.s_readdir++; 508 } 509 510 return &res; 511} 512 513 514nfsstatfsres * 515nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) 516{ 517 static nfsstatfsres res; 518 am_node *mp; 519 int retry; 520 mntent_t mnt; 521 522#ifdef DEBUG 523 amuDebug(D_TRACE) 524 plog(XLOG_DEBUG, "statfs:"); 525#endif /* DEBUG */ 526 527 mp = fh_to_mp2(argp, &retry); 528 if (mp == 0) { 529 if (retry < 0) 530 return 0; 531 res.sfr_status = nfs_error(retry); 532 } else { 533 nfsstatfsokres *fp; 534#ifdef DEBUG 535 amuDebug(D_TRACE) 536 plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path); 537#endif /* DEBUG */ 538 539 /* 540 * just return faked up file system information 541 */ 542 fp = &res.sfr_u.sfr_reply_u; 543 544 fp->sfrok_tsize = 1024; 545 fp->sfrok_bsize = 1024; 546 547 /* check if map is browsable and show_statfs_entries=yes */ 548 if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) && 549 mp->am_mnt && mp->am_mnt->mf_mopts) { 550 mnt.mnt_opts = mp->am_mnt->mf_mopts; 551 if (hasmntopt(&mnt, "browsable")) { 552 count_map_entries(mp, 553 &fp->sfrok_blocks, 554 &fp->sfrok_bfree, 555 &fp->sfrok_bavail); 556 } 557 } else { 558 fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */ 559 fp->sfrok_bfree = 0; 560 fp->sfrok_bavail = 0; 561 } 562 563 res.sfr_status = NFS_OK; 564 mp->am_stats.s_statfs++; 565 } 566 567 return &res; 568} 569 570 571/* 572 * count how many total entries there are in a map, and how many 573 * of them are in use. 574 */ 575static void 576count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail) 577{ 578 u_int blocks, bfree, bavail, i; 579 mntfs *mf; 580 mnt_map *mmp; 581 kv *k; 582 583 blocks = bfree = bavail = 0; 584 if (!mp) 585 goto out; 586 mf = mp->am_mnt; 587 if (!mf) 588 goto out; 589 mmp = (mnt_map *) mf->mf_private; 590 if (!mmp) 591 goto out; 592 593 /* iterate over keys */ 594 for (i = 0; i < NKVHASH; i++) { 595 for (k = mmp->kvhash[i]; k ; k = k->next) { 596 if (!k->key) 597 continue; 598 blocks++; 599 /* 600 * XXX: Need to count how many are actively in use and recompute 601 * bfree and bavail based on it. 602 */ 603 } 604 } 605 606out: 607 *out_blocks = blocks; 608 *out_bfree = bfree; 609 *out_bavail = bavail; 610} 611