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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/cred.h> 29 #include <sys/cmn_err.h> 30 #include <sys/debug.h> 31 #include <sys/systm.h> 32 #include <sys/kmem.h> 33 #include <sys/disp.h> 34 #include <sys/atomic.h> 35 #include <rpc/types.h> 36 #include <nfs/nfs.h> 37 #include <nfs/nfssys.h> 38 #include <nfs/export.h> 39 #include <nfs/rnode.h> 40 #include <rpc/auth.h> 41 #include <rpc/svc.h> 42 #include <rpc/xdr.h> 43 #include <rpc/clnt.h> 44 #include <nfs/nfs_log.h> 45 46 #define NUM_RECORDS_TO_WRITE 256 47 #define NUM_BYTES_TO_WRITE 65536 48 49 extern krwlock_t exported_lock; 50 51 static int nfslog_num_records_to_write = NUM_RECORDS_TO_WRITE; 52 static int nfslog_num_bytes_to_write = NUM_BYTES_TO_WRITE; 53 54 /* 55 * This struct is used to 'hide' the details of managing the log 56 * records internally to the logging code. Allocation routines 57 * are used to obtain pieces of memory for XDR encoding. This struct 58 * is a 'header' to those areas and a opaque cookie is used to pass 59 * this data structure between the allocating function and the put 60 * function. 61 */ 62 struct lr_alloc { 63 struct lr_alloc *next; /* links for write queuing */ 64 struct lr_alloc *prev; 65 #define LR_ALLOC_NOFREE 0x1 /* not present, call free */ 66 int lr_flags; 67 caddr_t log_record; /* address to XDR encoding */ 68 size_t size; /* final size of encoding */ 69 struct kmem_cache *alloc_cache; /* keep track of cache ptr */ 70 struct exportinfo *exi; /* who are we related to? */ 71 struct log_buffer *lb; 72 }; 73 74 struct flush_thread_params { 75 struct nfsl_flush_args tp_args; 76 int tp_error; 77 }; 78 79 static int log_file_create(caddr_t, struct log_file **); 80 static void log_file_rele(struct log_file *); 81 static struct log_buffer *log_buffer_create(caddr_t); 82 static void log_buffer_rele(struct log_buffer *); 83 static int nfslog_record_append2all(struct lr_alloc *); 84 static int nfslog_logbuffer_rename(struct log_buffer *); 85 static void nfslog_logfile_wait(struct log_file *); 86 static int nfslog_logfile_rename(char *, char *); 87 static void nfslog_do_flush(struct flush_thread_params *); 88 static void create_buffer_header(caddr_t *, size_t *, size_t *); 89 90 static int nfslog_write_logrecords(struct log_file *, struct lr_alloc *, int); 91 static void nfslog_free_logrecords(struct lr_alloc *); 92 static int nfslog_records_flush_to_disk(struct log_buffer *); 93 static int nfslog_records_flush_to_disk_nolock(struct log_buffer *); 94 95 /* 96 * Read/Write lock that protects 'nfslog_buffer_list'. 97 * This lock must be held when searching or modifying 'nfslog_buffer_list'. 98 */ 99 static krwlock_t nfslog_buffer_list_lock; 100 101 /* 102 * The list of "log_buffer" structures. 103 */ 104 struct log_buffer *nfslog_buffer_list = NULL; 105 106 107 #define LOG_BUFFER_HOLD(lbp) { \ 108 mutex_enter(&(lbp)->lb_lock); \ 109 (lbp)->lb_refcnt++; \ 110 mutex_exit(&(lbp)->lb_lock); \ 111 } 112 113 #define LOG_FILE_HOLD(lfp) { \ 114 mutex_enter(&(lfp)->lf_lock); \ 115 (lfp)->lf_refcnt++; \ 116 mutex_exit(&(lfp)->lf_lock); \ 117 } 118 119 #define LOG_FILE_RELE(lfp) { \ 120 log_file_rele(lfp); \ 121 } 122 123 /* 124 * These two macros are used to prep a logfile data structure and 125 * associated file for writing data. Note that the lf_lock is 126 * held as a result of the call to the first macro. This is used 127 * for serialization correctness between the logbuffer struct and 128 * the logfile struct. 129 */ 130 #define LOG_FILE_LOCK_TO_WRITE(lfp) { \ 131 mutex_enter(&(lfp)->lf_lock); \ 132 (lfp)->lf_refcnt++; \ 133 (lfp)->lf_writers++; \ 134 } 135 136 #define LOG_FILE_UNLOCK_FROM_WRITE(lfp) { \ 137 (lfp)->lf_writers--; \ 138 if ((lfp)->lf_writers == 0 && ((lfp)->lf_flags & L_WAITING)) { \ 139 (lfp)->lf_flags &= ~L_WAITING; \ 140 cv_broadcast(&(lfp)->lf_cv_waiters); \ 141 } \ 142 mutex_exit(&(lfp)->lf_lock); \ 143 log_file_rele(lfp); \ 144 } 145 146 int rfsl_log_buffer = 0; 147 static int rfsl_log_file = 0; 148 149 /* This array is used for memory allocation of record encoding spaces */ 150 static struct { 151 int size; 152 struct kmem_cache *mem_cache; 153 char *cache_name; 154 } nfslog_mem_alloc[] = { 155 #define SMALL_INDX 0 156 { NFSLOG_SMALL_RECORD_SIZE - sizeof (struct lr_alloc), 157 NULL, NFSLOG_SMALL_REC_NAME }, 158 #define MEDIUM_INDX 1 159 { NFSLOG_MEDIUM_RECORD_SIZE - sizeof (struct lr_alloc), 160 NULL, NFSLOG_MEDIUM_REC_NAME }, 161 #define LARGE_INDX 2 162 { NFSLOG_LARGE_RECORD_SIZE - sizeof (struct lr_alloc), 163 NULL, NFSLOG_LARGE_REC_NAME }, 164 { (-1), NULL } 165 }; 166 167 /* Used to calculate the 'real' allocation size */ 168 #define ALLOC_SIZE(index) \ 169 (nfslog_mem_alloc[index].size + sizeof (struct lr_alloc)) 170 171 /* 172 * Initialize logging data buffer cache 173 */ 174 void 175 nfslog_init() 176 { 177 int indx; 178 179 rw_init(&nfslog_buffer_list_lock, NULL, RW_DEFAULT, NULL); 180 181 /* 182 * Initialize the kmem caches for encoding 183 */ 184 for (indx = 0; nfslog_mem_alloc[indx].size != (-1); indx++) { 185 nfslog_mem_alloc[indx].mem_cache = 186 kmem_cache_create(nfslog_mem_alloc[indx].cache_name, 187 ALLOC_SIZE(indx), 188 0, NULL, NULL, NULL, NULL, NULL, 0); 189 } 190 } 191 192 /* 193 * Sets up the necessary log file and related buffers to enable logging 194 * on the given export point. 195 * Returns 0 on success, non-zero on failure. 196 */ 197 int 198 nfslog_setup(struct exportinfo *exi) 199 { 200 struct exportdata *kex; 201 struct log_buffer *lbp; 202 struct log_buffer *nlbp; 203 204 kex = &exi->exi_export; 205 ASSERT(kex->ex_flags & EX_LOG); 206 207 /* 208 * Logging is enabled for the new export point, check 209 * the existing log_buffer structures to see if the 210 * desired buffer has already been opened. If so, point 211 * the new exportinfo's exi_logbuffer to the existing 212 * one. 213 */ 214 rw_enter(&nfslog_buffer_list_lock, RW_READER); 215 for (lbp = nfslog_buffer_list; lbp != NULL; lbp = lbp->lb_next) { 216 LOGGING_DPRINT((10, 217 "searching for buffer... found log_buffer '%s'\n", 218 lbp->lb_path)); 219 if (strcmp(lbp->lb_path, kex->ex_log_buffer) == 0) { 220 /* Found our match. Ref it and return */ 221 LOG_BUFFER_HOLD(lbp); 222 exi->exi_logbuffer = lbp; 223 LOGGING_DPRINT((10, "\tfound log_buffer for '%s'\n", 224 kex->ex_log_buffer)); 225 rw_exit(&nfslog_buffer_list_lock); 226 return (0); 227 } 228 } 229 rw_exit(&nfslog_buffer_list_lock); 230 231 /* 232 * New buffer needed, allocate it. 233 * The buffer list lock has been dropped so we will need to search 234 * the list again to ensure that another thread has not added 235 * a matching buffer. 236 */ 237 if ((nlbp = log_buffer_create(kex->ex_log_buffer)) == NULL) { 238 /* 239 * Failed the buffer creation for some reason so we 240 * will need to return. 241 */ 242 return (EIO); 243 } 244 245 rw_enter(&nfslog_buffer_list_lock, RW_WRITER); 246 for (lbp = nfslog_buffer_list; lbp != NULL; 247 lbp = lbp->lb_next) { 248 if (strcmp(lbp->lb_path, kex->ex_log_buffer) == 0) { 249 /* 250 * A log_buffer already exists for the 251 * indicated buffer, use it instead. 252 */ 253 LOG_BUFFER_HOLD(lbp); 254 255 exi->exi_logbuffer = lbp; 256 257 LOGGING_DPRINT((10, 258 "found log_buffer for '%s' " 259 "after allocation\n", 260 kex->ex_log_buffer)); 261 262 rw_exit(&nfslog_buffer_list_lock); 263 264 log_buffer_rele(nlbp); 265 266 return (0); 267 } 268 } 269 /* 270 * Didn't find an existing log_buffer for this buffer, 271 * use the the newly created one, and add to list. We 272 * increment the reference count because the node is 273 * entered into the global list. 274 */ 275 LOGGING_DPRINT((10, "exportfs: adding nlbp=%p to list\n", 276 nlbp)); 277 278 nlbp->lb_next = nfslog_buffer_list; 279 nfslog_buffer_list = nlbp; 280 281 LOG_BUFFER_HOLD(nlbp); /* hold is for export entry */ 282 exi->exi_logbuffer = nlbp; 283 284 rw_exit(&nfslog_buffer_list_lock); 285 286 return (0); 287 } 288 289 /* 290 * Disables logging for the given export point. 291 */ 292 void 293 nfslog_disable(struct exportinfo *exi) 294 { 295 log_buffer_rele(exi->exi_logbuffer); 296 } 297 298 /* 299 * Creates the corresponding log_buffer and log_file structures 300 * for the the buffer named 'name'. 301 * Returns a pointer to the log_buffer structure with reference one. 302 */ 303 static struct log_buffer * 304 log_buffer_create(caddr_t name) 305 { 306 struct log_buffer *buffer; 307 struct log_file *logfile; 308 int namelen = strlen(name); 309 310 LOGGING_DPRINT((10, "log_buffer_create: %s\n", name)); 311 if (log_file_create(name, &logfile)) 312 return (NULL); 313 314 buffer = (struct log_buffer *)kmem_alloc(sizeof (*buffer), KM_SLEEP); 315 buffer->lb_refcnt = 1; 316 buffer->lb_rec_id = 0; 317 buffer->lb_path = (caddr_t)kmem_alloc(namelen + 1, KM_SLEEP); 318 bcopy(name, buffer->lb_path, namelen + 1); 319 buffer->lb_logfile = logfile; 320 buffer->lb_records = NULL; 321 buffer->lb_num_recs = 0; 322 buffer->lb_size_queued = 0; 323 mutex_init(&buffer->lb_lock, NULL, MUTEX_DEFAULT, NULL); 324 rfsl_log_buffer++; 325 326 return (buffer); 327 } 328 329 /* 330 * Release a log_buffer structure 331 */ 332 static void 333 log_buffer_rele(struct log_buffer *lbp) 334 { 335 int len; 336 337 mutex_enter(&lbp->lb_lock); 338 if (--lbp->lb_refcnt > 1) { 339 mutex_exit(&lbp->lb_lock); 340 return; 341 } 342 343 if (lbp->lb_refcnt < 0) { 344 panic("log_rele: log_buffer refcnt < 0"); 345 /*NOTREACHED*/ 346 } 347 348 /* 349 * Need to drop the lb_lock before acquiring the 350 * nfslog_buffer_list_lock. To avoid double free we need 351 * to hold an additional reference to the log buffer. 352 * This will ensure that no two threads will simultaneously 353 * be trying to free the same log buffer. 354 */ 355 356 if (lbp->lb_refcnt == 1) { 357 358 /* 359 * If the ref count is 1, then the last 360 * unshare/reference has been given up and we need to 361 * clean up the buffer and remove it from the buffer 362 * list. 363 */ 364 LOGGING_DPRINT((10, 365 "log_buffer_rele lbp=%p disconnecting\n", lbp)); 366 /* 367 * Hold additional reference before dropping the lb_lock 368 */ 369 370 lbp->lb_refcnt++; 371 mutex_exit(&lbp->lb_lock); 372 373 /* 374 * Make sure that all of the buffered records are written. 375 * Don't bother checking the write return value since there 376 * isn't much we can do at this point. 377 */ 378 (void) nfslog_records_flush_to_disk(lbp); 379 380 rw_enter(&nfslog_buffer_list_lock, RW_WRITER); 381 mutex_enter(&lbp->lb_lock); 382 /* 383 * Drop the reference count held above. 384 * If the ref count is still > 1 then someone has 385 * stepped in to use this log buffer. unlock and return. 386 */ 387 if (--lbp->lb_refcnt > 1) { 388 mutex_exit(&lbp->lb_lock); 389 rw_exit(&nfslog_buffer_list_lock); 390 return; 391 } 392 393 if (lbp == nfslog_buffer_list) { 394 nfslog_buffer_list = lbp->lb_next; 395 } else { 396 struct log_buffer *tlbp; 397 398 /* Drop the log_buffer from the master list */ 399 for (tlbp = nfslog_buffer_list; tlbp->lb_next != NULL; 400 tlbp = tlbp->lb_next) { 401 if (tlbp->lb_next == lbp) { 402 tlbp->lb_next = lbp->lb_next; 403 break; 404 } 405 } 406 } 407 408 mutex_exit(&lbp->lb_lock); 409 rw_exit(&nfslog_buffer_list_lock); 410 } 411 /* 412 * ref count zero; finish clean up. 413 */ 414 LOGGING_DPRINT((10, "log_buffer_rele lbp=%p freeing\n", lbp)); 415 416 log_file_rele(lbp->lb_logfile); 417 len = strlen(lbp->lb_path) + 1; 418 kmem_free(lbp->lb_path, len); 419 kmem_free(lbp, sizeof (*lbp)); 420 rfsl_log_buffer--; 421 } 422 423 /* 424 * Creates the corresponding log_file structure for the buffer 425 * named 'log_file_name'. 426 * 'log_file_name' is created by concatenating 'origname' and LOG_INPROG_STRING. 427 * 'logfile' is set to be the log_file structure with reference one. 428 */ 429 static int 430 log_file_create(caddr_t origname, struct log_file **lfpp) 431 { 432 vnode_t *vp = NULL; 433 char *name; 434 int namelen; 435 int error; 436 struct log_file *logfile = NULL; 437 vattr_t va; 438 caddr_t loghdr = NULL; 439 size_t loghdr_len = 0; 440 size_t loghdr_free = 0; 441 442 namelen = strlen(origname) + strlen(LOG_INPROG_STRING); 443 name = (caddr_t)kmem_alloc(namelen + 1, KM_SLEEP); 444 (void) sprintf(name, "%s%s", origname, LOG_INPROG_STRING); 445 446 LOGGING_DPRINT((3, "log_file_create: %s\n", name)); 447 if (error = vn_open(name, UIO_SYSSPACE, FCREAT|FWRITE|FOFFMAX, 448 LOG_MODE, &vp, CRCREAT, 0)) { 449 nfs_cmn_err(error, CE_WARN, 450 "log_file_create: Can not open %s - error %m", name); 451 goto out; 452 } 453 LOGGING_DPRINT((3, "log_file_create: %s vp=%p v_count=%d\n", 454 name, vp, vp->v_count)); 455 456 logfile = (struct log_file *)kmem_zalloc(sizeof (*logfile), KM_SLEEP); 457 logfile->lf_path = name; 458 /* 459 * No need to bump the vnode reference count since it is set 460 * to one by vn_open(). 461 */ 462 logfile->lf_vp = vp; 463 logfile->lf_refcnt = 1; 464 mutex_init(&logfile->lf_lock, NULL, MUTEX_DEFAULT, NULL); 465 rfsl_log_file++; 466 467 va.va_mask = AT_SIZE; 468 error = VOP_GETATTR(vp, &va, 0, CRED()); 469 if (error) { 470 nfs_cmn_err(error, CE_WARN, 471 "log_file_create: Can not stat %s - error = %m", 472 name); 473 goto out; 474 } 475 476 if (va.va_size == 0) { 477 struct lr_alloc lr; 478 479 /* 480 * Write Header. 481 */ 482 create_buffer_header(&loghdr, &loghdr_len, &loghdr_free); 483 /* 484 * Dummy up a lr_alloc struct for the write 485 */ 486 lr.next = lr.prev = &lr; 487 lr.lr_flags = 0; 488 lr.log_record = loghdr; 489 lr.size = loghdr_len; 490 lr.alloc_cache = NULL; 491 lr.exi = NULL; 492 lr.lb = NULL; 493 494 mutex_enter(&logfile->lf_lock); 495 496 error = nfslog_write_logrecords(logfile, &lr, 1); 497 498 mutex_exit(&logfile->lf_lock); 499 500 if (error != 0) { 501 nfs_cmn_err(error, CE_WARN, 502 "log_file_create: Can not write header " 503 "on %s - error = %m", name); 504 goto out; 505 } 506 } 507 *lfpp = logfile; 508 509 if (loghdr != NULL) 510 kmem_free(loghdr, loghdr_free); 511 512 return (0); 513 514 out: 515 if (vp != NULL) { 516 int error1; 517 error1 = VOP_CLOSE(vp, FCREAT|FWRITE|FOFFMAX, 1, (offset_t)0, 518 CRED()); 519 if (error1) { 520 nfs_cmn_err(error1, CE_WARN, 521 "log_file_create: Can not close %s - " 522 "error = %m", name); 523 } 524 VN_RELE(vp); 525 } 526 527 kmem_free(name, namelen + 1); 528 if (logfile != NULL) { 529 mutex_destroy(&logfile->lf_lock); 530 kmem_free(logfile, sizeof (*logfile)); 531 rfsl_log_file--; 532 } 533 if (loghdr != NULL) 534 kmem_free(loghdr, loghdr_free); 535 536 return (error); 537 } 538 539 /* 540 * Release a log_file structure 541 */ 542 static void 543 log_file_rele(struct log_file *lfp) 544 { 545 int len; 546 int error; 547 548 mutex_enter(&lfp->lf_lock); 549 if (--lfp->lf_refcnt > 0) { 550 LOGGING_DPRINT((10, 551 "log_file_rele lfp=%p decremented refcnt to %d\n", 552 lfp, lfp->lf_refcnt)); 553 mutex_exit(&lfp->lf_lock); 554 return; 555 } 556 if (lfp->lf_refcnt < 0) { 557 panic("log_file_rele: log_file refcnt < 0"); 558 /*NOTREACHED*/ 559 } 560 561 LOGGING_DPRINT((10, "log_file_rele lfp=%p freeing node\n", lfp)); 562 563 lfp->lf_flags &= ~(L_PRINTED | L_ERROR); 564 565 ASSERT(lfp->lf_flags == 0); 566 ASSERT(lfp->lf_writers == 0); 567 568 if (error = VOP_CLOSE(lfp->lf_vp, FCREAT|FWRITE|FOFFMAX, 1, (offset_t)0, 569 CRED())) { 570 nfs_cmn_err(error, CE_WARN, 571 "NFS: Could not close log buffer %s - error = %m", 572 lfp->lf_path); 573 #ifdef DEBUG 574 } else { 575 LOGGING_DPRINT((3, 576 "log_file_rele: %s has been closed vp=%p " 577 "v_count=%d\n", 578 lfp->lf_path, lfp->lf_vp, lfp->lf_vp->v_count)); 579 #endif 580 } 581 VN_RELE(lfp->lf_vp); 582 583 len = strlen(lfp->lf_path) + 1; 584 kmem_free(lfp->lf_path, len); 585 kmem_free(lfp, sizeof (*lfp)); 586 rfsl_log_file--; 587 } 588 589 /* 590 * Allocates a record of the size specified. 591 * 'exi' identifies the exportinfo structure being logged. 592 * 'size' indicates how much memory should be allocated 593 * 'cookie' is used to store an opaque value for the caller for later use 594 * 'flags' currently ignored. 595 * 596 * Returns a pointer to the beginning of the allocated memory. 597 * 'cookie' is a pointer to the 'lr_alloc' struct; this will be used 598 * to keep track of the encoded record and contains all the info 599 * for enqueuing the record on the log buffer for later writing. 600 * 601 * nfslog_record_put() must be used to 'free' this record or allocation. 602 */ 603 /* ARGSUSED */ 604 void * 605 nfslog_record_alloc( 606 struct exportinfo *exi, 607 int alloc_indx, 608 void **cookie, 609 int flags) 610 { 611 struct lr_alloc *lrp; 612 613 lrp = (struct lr_alloc *) 614 kmem_cache_alloc(nfslog_mem_alloc[alloc_indx].mem_cache, 615 KM_NOSLEEP); 616 617 if (lrp == NULL) { 618 *cookie = NULL; 619 return (NULL); 620 } 621 622 lrp->next = lrp; 623 lrp->prev = lrp; 624 lrp->lr_flags = 0; 625 626 lrp->log_record = (caddr_t)((uintptr_t)lrp + 627 (uintptr_t)sizeof (struct lr_alloc)); 628 lrp->size = nfslog_mem_alloc[alloc_indx].size; 629 lrp->alloc_cache = nfslog_mem_alloc[alloc_indx].mem_cache; 630 lrp->exi = exi; 631 632 if (exi->exi_export.ex_flags & EX_LOG) { 633 LOG_BUFFER_HOLD(exi->exi_logbuffer); 634 lrp->lb = exi->exi_logbuffer; 635 } else { 636 lrp->lb = NULL; 637 } 638 639 *cookie = (void *)lrp; 640 641 LOGGING_DPRINT((3, 642 "nfslog_record_alloc(log_buffer=%p mem=%p size=%lu)\n", 643 exi->exi_logbuffer, lrp->log_record, lrp->size)); 644 return (lrp->log_record); 645 } 646 647 /* 648 * After the above nfslog_record_alloc() has been called and a record 649 * encoded into the buffer that was returned, this function is called 650 * to handle appropriate disposition of the newly created record. 651 * The cookie value is the one that was returned from nfslog_record_alloc(). 652 * Size is the actual size of the record that was encoded. This is 653 * passed in because the size used for the alloc was just an approximation. 654 * The sync parameter is used to tell us if we need to force this record 655 * to disk and if not it will be queued for later writing. 656 * 657 * Note that if the size parameter has a value of 0, then the record is 658 * not written to the log and the associated data structures are released. 659 */ 660 void 661 nfslog_record_put(void *cookie, size_t size, bool_t sync, 662 unsigned int which_buffers) 663 { 664 struct lr_alloc *lrp = (struct lr_alloc *)cookie; 665 struct log_buffer *lbp = lrp->lb; 666 667 /* 668 * If the caller has nothing to write or if there is 669 * an apparent error, rele the buffer and free. 670 */ 671 if (size == 0 || size > lrp->size) { 672 nfslog_free_logrecords(lrp); 673 return; 674 } 675 676 /* 677 * Reset the size to what actually needs to be written 678 * This is used later on when the iovec is built for 679 * writing the records to the log file. 680 */ 681 lrp->size = size; 682 683 /* append to all if public exi */ 684 if (which_buffers == NFSLOG_ALL_BUFFERS) { 685 (void) nfslog_record_append2all(lrp); 686 nfslog_free_logrecords(lrp); 687 return; 688 } 689 690 /* Insert the record on the list to be written */ 691 mutex_enter(&lbp->lb_lock); 692 if (lbp->lb_records == NULL) { 693 lbp->lb_records = (caddr_t)lrp; 694 lbp->lb_num_recs = 1; 695 lbp->lb_size_queued = lrp->size; 696 } else { 697 insque(lrp, ((struct lr_alloc *)lbp->lb_records)->prev); 698 lbp->lb_num_recs++; 699 lbp->lb_size_queued += lrp->size; 700 } 701 702 /* 703 * Determine if the queue for this log buffer should be flushed. 704 * This is done by either the number of records queued, the total 705 * size of all records queued or by the request of the caller 706 * via the sync parameter. 707 */ 708 if (lbp->lb_size_queued >= nfslog_num_bytes_to_write || 709 lbp->lb_num_recs > nfslog_num_records_to_write || 710 sync == TRUE) { 711 mutex_exit(&lbp->lb_lock); 712 (void) nfslog_records_flush_to_disk(lbp); 713 } else { 714 mutex_exit(&lbp->lb_lock); 715 } 716 717 } 718 719 /* 720 * Examine the log_buffer struct to see if there are queue log records 721 * that need to be written to disk. If some exist, pull them off of 722 * the log buffer and write them to the log file. 723 */ 724 static int 725 nfslog_records_flush_to_disk(struct log_buffer *lbp) 726 { 727 728 mutex_enter(&lbp->lb_lock); 729 730 if (lbp->lb_records == NULL) { 731 mutex_exit(&lbp->lb_lock); 732 return (0); 733 } 734 return (nfslog_records_flush_to_disk_nolock(lbp)); 735 } 736 737 /* 738 * Function requires that the caller holds lb_lock. 739 * Function flushes any records in the log buffer to the disk. 740 * Function drops the lb_lock on return. 741 */ 742 743 static int 744 nfslog_records_flush_to_disk_nolock(struct log_buffer *lbp) 745 { 746 struct log_file *lfp = NULL; 747 struct lr_alloc *lrp_writers; 748 int num_recs; 749 int error = 0; 750 751 ASSERT(MUTEX_HELD(&lbp->lb_lock)); 752 753 lfp = lbp->lb_logfile; 754 755 LOG_FILE_LOCK_TO_WRITE(lfp); 756 ASSERT(lbp->lb_records != NULL); 757 758 lrp_writers = (struct lr_alloc *)lbp->lb_records; 759 lbp->lb_records = NULL; 760 num_recs = lbp->lb_num_recs; 761 lbp->lb_num_recs = 0; 762 lbp->lb_size_queued = 0; 763 mutex_exit(&lbp->lb_lock); 764 error = nfslog_write_logrecords(lfp, lrp_writers, num_recs); 765 766 LOG_FILE_UNLOCK_FROM_WRITE(lfp); 767 768 nfslog_free_logrecords(lrp_writers); 769 return (error); 770 } 771 772 773 /* 774 * Take care of writing the provided log record(s) to the log file. 775 * We group the log records with an iovec and use VOP_WRITE to append 776 * them to the end of the log file. 777 */ 778 static int 779 nfslog_write_logrecords(struct log_file *lfp, 780 struct lr_alloc *lrp_writers, int num_recs) 781 { 782 struct uio uio; 783 struct iovec *iovp; 784 int size_iovecs; 785 vnode_t *vp; 786 struct vattr va; 787 struct lr_alloc *lrp; 788 int i; 789 ssize_t len; 790 int ioflag = FAPPEND; 791 int error = 0; 792 793 ASSERT(MUTEX_HELD(&lfp->lf_lock)); 794 795 vp = lfp->lf_vp; 796 797 size_iovecs = sizeof (struct iovec) * num_recs; 798 iovp = (struct iovec *)kmem_alloc(size_iovecs, KM_NOSLEEP); 799 800 if (iovp == NULL) { 801 error = ENOMEM; 802 goto out; 803 } 804 805 /* Build the iovec based on the list of log records */ 806 i = 0; 807 len = 0; 808 lrp = lrp_writers; 809 do { 810 iovp[i].iov_base = lrp->log_record; 811 iovp[i].iov_len = lrp->size; 812 len += lrp->size; 813 lrp = lrp->next; 814 i++; 815 } while (lrp != lrp_writers); 816 817 ASSERT(i == num_recs); 818 819 uio.uio_iov = iovp; 820 uio.uio_iovcnt = num_recs; 821 uio.uio_loffset = 0; 822 uio.uio_segflg = (short)UIO_SYSSPACE; 823 uio.uio_resid = len; 824 uio.uio_llimit = (rlim64_t)MAXOFFSET_T; 825 uio.uio_fmode = FWRITE; 826 uio.uio_extflg = UIO_COPY_DEFAULT; 827 828 /* 829 * Save the size. If the write fails, reset the size to avoid 830 * corrupted log buffer files. 831 */ 832 va.va_mask = AT_SIZE; 833 834 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); /* UIO_WRITE */ 835 if ((error = VOP_GETATTR(vp, &va, 0, CRED())) == 0) { 836 if ((len + va.va_size) < (MAXOFF32_T)) { 837 error = VOP_WRITE(vp, &uio, ioflag, CRED(), NULL); 838 if (uio.uio_resid) 839 error = ENOSPC; 840 if (error) 841 (void) VOP_SETATTR(vp, &va, 0, CRED(), NULL); 842 } else { 843 if (!(lfp->lf_flags & L_PRINTED)) { 844 cmn_err(CE_WARN, 845 "NFS Logging: buffer file %s exceeds 2GB; " 846 "stopped writing buffer \n", lfp->lf_path); 847 } 848 error = ENOSPC; 849 } 850 } 851 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 852 853 kmem_free(iovp, size_iovecs); 854 855 out: 856 if (error) { 857 if (!(lfp->lf_flags & L_PRINTED)) { 858 nfs_cmn_err(error, CE_WARN, 859 "NFS Logging disabled for buffer %s - " 860 "write error = %m\n", lfp->lf_path); 861 lfp->lf_flags |= L_PRINTED; 862 } 863 } else if (lfp->lf_flags & (L_ERROR | L_PRINTED)) { 864 lfp->lf_flags &= ~(L_ERROR | L_PRINTED); 865 cmn_err(CE_WARN, 866 "NFS Logging re-enabled for buffer %s\n", lfp->lf_path); 867 } 868 869 return (error); 870 } 871 872 static void 873 nfslog_free_logrecords(struct lr_alloc *lrp_writers) 874 { 875 struct lr_alloc *lrp = lrp_writers; 876 struct lr_alloc *lrp_free; 877 878 do { 879 lrp_free = lrp; 880 881 lrp = lrp->next; 882 883 /* 884 * Check to see if we are supposed to free this structure 885 * and relese the log_buffer ref count. 886 * It may be the case that the caller does not want this 887 * structure and its record contents freed just yet. 888 */ 889 if ((lrp_free->lr_flags & LR_ALLOC_NOFREE) == 0) { 890 if (lrp_free->lb != NULL) 891 log_buffer_rele(lrp_free->lb); 892 if (lrp_free->alloc_cache) /* double check */ 893 kmem_cache_free(lrp_free->alloc_cache, 894 (void *)lrp_free); 895 } else { 896 /* 897 * after being pulled from the list the 898 * pointers need to be reinitialized. 899 */ 900 lrp_free->next = lrp_free; 901 lrp_free->prev = lrp_free; 902 } 903 904 } while (lrp != lrp_writers); 905 } 906 907 /* 908 * Rename lbp->lb_logfile to reflect the true name requested by 'share' 909 */ 910 static int 911 nfslog_logbuffer_rename(struct log_buffer *lbp) 912 { 913 struct log_file *lf; 914 int error; 915 struct log_file *logfile; 916 917 /* 918 * Try our best to get the cache records into the log file 919 * before the rename occurs. 920 */ 921 (void) nfslog_records_flush_to_disk(lbp); 922 923 /* 924 * Hold lb_lock before retrieving 925 * lb_logfile. 926 * Hold a reference to the 927 * "lf" structure. this is 928 * same as LOG_FILE_HOLD() 929 */ 930 mutex_enter(&(lbp)->lb_lock); 931 lf = lbp->lb_logfile; 932 mutex_enter(&(lf)->lf_lock); 933 mutex_exit(&(lbp)->lb_lock); 934 lf->lf_refcnt++; 935 mutex_exit(&(lf)->lf_lock); 936 937 LOGGING_DPRINT((10, "nfslog_logbuffer_rename: renaming %s to %s\n", 938 lf->lf_path, lbp->lb_path)); 939 940 /* 941 * rename the current buffer to what the daemon expects 942 */ 943 if (error = nfslog_logfile_rename(lf->lf_path, lbp->lb_path)) 944 goto out; 945 946 /* 947 * Create a new working buffer file and have all new data sent there. 948 */ 949 if (error = log_file_create(lbp->lb_path, &logfile)) { 950 /* Attempt to rename to original */ 951 (void) nfslog_logfile_rename(lbp->lb_path, lf->lf_path); 952 goto out; 953 } 954 955 /* 956 * Hold the lb_lock here, this will make 957 * all the threads trying to access lb->logfile block 958 * and get a new logfile structure instead of old one. 959 */ 960 mutex_enter(&(lbp)->lb_lock); 961 lbp->lb_logfile = logfile; 962 mutex_exit(&(lbp)->lb_lock); 963 964 LOG_FILE_RELE(lf); /* release log_buffer's reference */ 965 966 /* 967 * Wait for log_file to be in a quiescent state before we 968 * return to our caller to let it proceed with the reading of 969 * this file. 970 */ 971 nfslog_logfile_wait(lf); 972 973 out: 974 /* 975 * Release our reference on "lf" in two different cases. 976 * 1. Error condition, release only the reference 977 * that we held at the begining of this 978 * routine on "lf" structure. 979 * 2. Fall through condition, no errors but the old 980 * logfile structure "lf" has been replaced with 981 * the new "logfile" structure, so release the 982 * reference that was part of the creation of 983 * "lf" structure to free up the resources. 984 */ 985 986 LOG_FILE_RELE(lf); 987 988 return (error); 989 } 990 991 /* 992 * Renames the 'from' file to 'new'. 993 */ 994 static int 995 nfslog_logfile_rename(char *from, char *new) 996 { 997 int error; 998 999 if (error = vn_rename(from, new, UIO_SYSSPACE)) { 1000 cmn_err(CE_WARN, 1001 "nfslog_logfile_rename: couldn't rename %s to %s\n", 1002 from, new); 1003 } 1004 return (error); 1005 } 1006 1007 /* 1008 * Wait for the log_file writers to finish before returning 1009 */ 1010 static void 1011 nfslog_logfile_wait(struct log_file *lf) 1012 { 1013 mutex_enter(&lf->lf_lock); 1014 while (lf->lf_writers > 0) { 1015 lf->lf_flags |= L_WAITING; 1016 (void) cv_wait_sig(&lf->lf_cv_waiters, &lf->lf_lock); 1017 } 1018 mutex_exit(&lf->lf_lock); 1019 } 1020 1021 static int 1022 nfslog_record_append2all(struct lr_alloc *lrp) 1023 { 1024 struct log_buffer *lbp, *nlbp; 1025 int error, ret_error = 0; 1026 int lr_flags = lrp->lr_flags; 1027 1028 rw_enter(&nfslog_buffer_list_lock, RW_READER); 1029 if ((lbp = nfslog_buffer_list) != NULL) 1030 LOG_BUFFER_HOLD(lbp); 1031 for (nlbp = NULL; lbp != NULL; lbp = nlbp) { 1032 if ((nlbp = lbp->lb_next) != NULL) { 1033 /* 1034 * Remember next element in the list 1035 */ 1036 LOG_BUFFER_HOLD(nlbp); 1037 } 1038 rw_exit(&nfslog_buffer_list_lock); 1039 1040 /* 1041 * Insert the record on the buffer's list to be written 1042 * and then flush the records to the log file. 1043 * Make sure to set the no free flag so that the 1044 * record can be used for the next write 1045 */ 1046 lrp->lr_flags = LR_ALLOC_NOFREE; 1047 1048 ASSERT(lbp != NULL); 1049 mutex_enter(&lbp->lb_lock); 1050 if (lbp->lb_records == NULL) { 1051 lbp->lb_records = (caddr_t)lrp; 1052 lbp->lb_num_recs = 1; 1053 lbp->lb_size_queued = lrp->size; 1054 } else { 1055 insque(lrp, ((struct lr_alloc *)lbp->lb_records)->prev); 1056 lbp->lb_num_recs++; 1057 lbp->lb_size_queued += lrp->size; 1058 } 1059 1060 /* 1061 * Flush log records to disk. 1062 * Function is called with lb_lock held. 1063 * Function drops the lb_lock on return. 1064 */ 1065 error = nfslog_records_flush_to_disk_nolock(lbp); 1066 1067 if (error) { 1068 ret_error = -1; 1069 nfs_cmn_err(error, CE_WARN, 1070 "rfsl_log_pubfh: could not append record to " 1071 "\"%s\" error = %m\n", lbp->lb_path); 1072 } 1073 log_buffer_rele(lbp); 1074 rw_enter(&nfslog_buffer_list_lock, RW_READER); 1075 } 1076 rw_exit(&nfslog_buffer_list_lock); 1077 1078 lrp->lr_flags = lr_flags; 1079 1080 return (ret_error); 1081 } 1082 1083 #ifdef DEBUG 1084 static int logging_debug = 0; 1085 1086 /* 1087 * 0) no debugging 1088 * 3) current test software 1089 * 10) random stuff 1090 */ 1091 void 1092 nfslog_dprint(const int level, const char *fmt, ...) 1093 { 1094 va_list args; 1095 1096 if (logging_debug == level || 1097 (logging_debug > 10 && (logging_debug - 10) >= level)) { 1098 va_start(args, fmt); 1099 (void) vprintf(fmt, args); 1100 va_end(args); 1101 } 1102 } 1103 1104 #endif /* DEBUG */ 1105 1106 /* 1107 * NFS Log Flush system call 1108 * Caller must check privileges. 1109 */ 1110 /* ARGSUSED */ 1111 int 1112 nfsl_flush(struct nfsl_flush_args *args, model_t model) 1113 { 1114 struct flush_thread_params *tparams; 1115 struct nfsl_flush_args *nfsl_args; 1116 int error = 0; 1117 ulong_t buffer_len; 1118 STRUCT_HANDLE(nfsl_flush_args, uap); 1119 1120 STRUCT_SET_HANDLE(uap, model, args); 1121 1122 tparams = (struct flush_thread_params *) 1123 kmem_zalloc(sizeof (*tparams), KM_SLEEP); 1124 1125 nfsl_args = &tparams->tp_args; 1126 nfsl_args->version = STRUCT_FGET(uap, version); 1127 if (nfsl_args->version != NFSL_FLUSH_ARGS_VERS) { 1128 cmn_err(CE_WARN, "nfsl_flush: exected version %d, got %d", 1129 NFSL_FLUSH_ARGS_VERS, nfsl_args->version); 1130 return (EIO); 1131 } 1132 1133 nfsl_args->directive = STRUCT_FGET(uap, directive); 1134 if ((nfsl_args->directive & NFSL_ALL) == 0) { 1135 /* 1136 * Process a specific buffer 1137 */ 1138 nfsl_args->buff_len = STRUCT_FGET(uap, buff_len); 1139 1140 nfsl_args->buff = (char *) 1141 kmem_alloc(nfsl_args->buff_len, KM_NOSLEEP); 1142 if (nfsl_args->buff == NULL) 1143 return (ENOMEM); 1144 1145 error = copyinstr((const char *)STRUCT_FGETP(uap, buff), 1146 nfsl_args->buff, nfsl_args->buff_len, &buffer_len); 1147 if (error) 1148 return (EFAULT); 1149 1150 if (nfsl_args->buff_len != buffer_len) 1151 return (EFAULT); 1152 } 1153 1154 LOGGING_DPRINT((10, "nfsl_flush: Flushing %s buffer(s)\n", 1155 nfsl_args->directive & NFSL_ALL ? "all" : nfsl_args->buff)); 1156 1157 if (nfsl_args->directive & NFSL_SYNC) { 1158 /* 1159 * Do the work synchronously 1160 */ 1161 nfslog_do_flush(tparams); 1162 error = tparams->tp_error; 1163 kmem_free(nfsl_args->buff, nfsl_args->buff_len); 1164 kmem_free(tparams, sizeof (*tparams)); 1165 } else { 1166 /* 1167 * Do the work asynchronously 1168 */ 1169 (void) thread_create(NULL, 0, nfslog_do_flush, 1170 tparams, 0, &p0, TS_RUN, minclsyspri); 1171 } 1172 1173 return (error); 1174 } 1175 1176 /* 1177 * This is where buffer flushing would occur, but there is no buffering 1178 * at this time. 1179 * Possibly rename the log buffer for processing. 1180 * Sets tparams->ta_error equal to the value of the error that occured, 1181 * 0 otherwise. 1182 * Returns ENOENT if the buffer is not found. 1183 */ 1184 static void 1185 nfslog_do_flush(struct flush_thread_params *tparams) 1186 { 1187 struct nfsl_flush_args *args; 1188 struct log_buffer *lbp; 1189 int error = ENOENT; 1190 int found = 0; 1191 char *buf_inprog; /* name of buff in progress */ 1192 int buf_inprog_len; 1193 1194 /* 1195 * Sanity check on the arguments. 1196 */ 1197 if (!tparams) 1198 return; 1199 args = &tparams->tp_args; 1200 if (!args) 1201 return; 1202 1203 rw_enter(&nfslog_buffer_list_lock, RW_READER); 1204 for (lbp = nfslog_buffer_list; lbp != NULL; lbp = lbp->lb_next) { 1205 if (args->directive & NFSL_ALL) { 1206 (void) nfslog_records_flush_to_disk(lbp); 1207 } else { 1208 if ((strcmp(lbp->lb_path, args->buff) == 0) && 1209 (args->directive & NFSL_RENAME)) { 1210 1211 error = nfslog_logbuffer_rename(lbp); 1212 found++; 1213 break; 1214 } 1215 } 1216 } 1217 rw_exit(&nfslog_buffer_list_lock); 1218 1219 if (!found && ((args->directive & NFSL_ALL) == 0) && 1220 (args->directive & NFSL_RENAME)) { 1221 /* 1222 * The specified buffer is not currently in use, 1223 * simply rename the file indicated. 1224 */ 1225 buf_inprog_len = strlen(args->buff) + 1226 strlen(LOG_INPROG_STRING) + 1; 1227 buf_inprog = (caddr_t)kmem_alloc(buf_inprog_len, KM_SLEEP); 1228 (void) sprintf(buf_inprog, "%s%s", 1229 args->buff, LOG_INPROG_STRING); 1230 1231 error = nfslog_logfile_rename(buf_inprog, args->buff); 1232 1233 kmem_free(buf_inprog, buf_inprog_len); 1234 } 1235 1236 out: 1237 if ((args->directive & NFSL_SYNC) == 0) { 1238 /* 1239 * Work was performed asynchronously, the caller is 1240 * no longer waiting for us. 1241 * Free the thread arguments and exit. 1242 */ 1243 kmem_free(args->buff, args->buff_len); 1244 kmem_free(tparams, sizeof (*tparams)); 1245 thread_exit(); 1246 /* NOTREACHED */ 1247 } 1248 1249 tparams->tp_error = error; 1250 } 1251 1252 /* 1253 * Generate buffer_header. 1254 * 'loghdr' points the the buffer_header, and *reclen 1255 * contains the length of the buffer. 1256 */ 1257 static void 1258 create_buffer_header(caddr_t *loghdr, size_t *reclen, size_t *freesize) 1259 { 1260 timestruc_t now; 1261 nfslog_buffer_header lh; 1262 XDR xdrs; 1263 unsigned int final_size; 1264 1265 1266 /* pick some size that will hold the buffer_header */ 1267 *freesize = NFSLOG_SMALL_RECORD_SIZE; 1268 1269 /* 1270 * Fill header 1271 */ 1272 lh.bh_length = 0; /* don't know yet how large it will be */ 1273 lh.bh_version = NFSLOG_BUF_VERSION; 1274 lh.bh_flags = 0; 1275 lh.bh_offset = 0; 1276 gethrestime(&now); 1277 TIMESPEC_TO_TIMESPEC32(&lh.bh_timestamp, &now); 1278 1279 /* 1280 * Encode the header 1281 */ 1282 *loghdr = (caddr_t)kmem_alloc(*freesize, KM_SLEEP); 1283 xdrmem_create(&xdrs, *loghdr, *freesize, XDR_ENCODE); 1284 1285 (void) xdr_nfslog_buffer_header(&xdrs, &lh); 1286 1287 /* 1288 * Reset with final size of the encoded data 1289 */ 1290 final_size = xdr_getpos(&xdrs); 1291 xdr_setpos(&xdrs, 0); 1292 (void) xdr_u_int(&xdrs, &final_size); 1293 1294 *reclen = (size_t)final_size; 1295 } 1296 1297 /* 1298 * **************************************************************** 1299 * RPC dispatch table for logging 1300 * Indexed by program, version, proc 1301 * Based on NFS dispatch table. 1302 */ 1303 struct nfslog_proc_disp { 1304 bool_t (*xdrargs)(); 1305 bool_t (*xdrres)(); 1306 bool_t affects_transactions; /* Operation affects transaction */ 1307 /* processing */ 1308 }; 1309 1310 struct nfslog_vers_disp { 1311 int nfslog_dis_nprocs; /* number of procs */ 1312 struct nfslog_proc_disp *nfslog_dis_proc_table; /* proc array */ 1313 }; 1314 1315 struct nfslog_prog_disp { 1316 int nfslog_dis_prog; /* program number */ 1317 int nfslog_dis_versmin; /* Minimum version value */ 1318 int nfslog_dis_nvers; /* Number of version values */ 1319 struct nfslog_vers_disp *nfslog_dis_vers_table; /* versions array */ 1320 }; 1321 1322 static int rfs_log_bad = 0; /* incremented on bad log attempts */ 1323 static int rfs_log_good = 0; /* incremented on successful log attempts */ 1324 1325 /* 1326 * Define the actions taken per prog/vers/proc: 1327 * 1328 * In some cases, the nl types are the same as the nfs types and a simple 1329 * bcopy should suffice. Rather that define tens of identical procedures, 1330 * simply define these to bcopy. Similarly this takes care of different 1331 * procs that use same parameter struct. 1332 */ 1333 1334 static struct nfslog_proc_disp nfslog_proc_v2[] = { 1335 /* 1336 * NFS VERSION 2 1337 */ 1338 1339 /* RFS_NULL = 0 */ 1340 {xdr_void, xdr_void, FALSE}, 1341 1342 /* RFS_GETATTR = 1 */ 1343 {xdr_fhandle, xdr_nfslog_getattrres, FALSE}, 1344 1345 /* RFS_SETATTR = 2 */ 1346 {xdr_nfslog_setattrargs, xdr_nfsstat, TRUE}, 1347 1348 /* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */ 1349 {xdr_void, xdr_void, FALSE}, 1350 1351 /* RFS_LOOKUP = 4 */ 1352 {xdr_nfslog_diropargs, xdr_nfslog_diropres, TRUE}, 1353 1354 /* RFS_READLINK = 5 */ 1355 {xdr_fhandle, xdr_nfslog_rdlnres, FALSE}, 1356 1357 /* RFS_READ = 6 */ 1358 {xdr_nfslog_nfsreadargs, xdr_nfslog_rdresult, TRUE}, 1359 1360 /* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */ 1361 {xdr_void, xdr_void, FALSE}, 1362 1363 /* RFS_WRITE = 8 */ 1364 {xdr_nfslog_writeargs, xdr_nfslog_writeresult, TRUE}, 1365 1366 /* RFS_CREATE = 9 */ 1367 {xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE}, 1368 1369 /* RFS_REMOVE = 10 */ 1370 {xdr_nfslog_diropargs, xdr_nfsstat, TRUE}, 1371 1372 /* RFS_RENAME = 11 */ 1373 {xdr_nfslog_rnmargs, xdr_nfsstat, TRUE}, 1374 1375 /* RFS_LINK = 12 */ 1376 {xdr_nfslog_linkargs, xdr_nfsstat, TRUE}, 1377 1378 /* RFS_SYMLINK = 13 */ 1379 {xdr_nfslog_symlinkargs, xdr_nfsstat, TRUE}, 1380 1381 /* RFS_MKDIR = 14 */ 1382 {xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE}, 1383 1384 /* RFS_RMDIR = 15 */ 1385 {xdr_nfslog_diropargs, xdr_nfsstat, TRUE}, 1386 1387 /* RFS_READDIR = 16 */ 1388 {xdr_nfslog_rddirargs, xdr_nfslog_rddirres, TRUE}, 1389 1390 /* RFS_STATFS = 17 */ 1391 {xdr_fhandle, xdr_nfslog_statfs, FALSE}, 1392 }; 1393 1394 1395 /* 1396 * NFS VERSION 3 1397 */ 1398 1399 static struct nfslog_proc_disp nfslog_proc_v3[] = { 1400 1401 /* NFSPROC3_NULL = 0 */ 1402 {xdr_void, xdr_void, FALSE}, 1403 1404 /* NFSPROC3_GETATTR = 1 */ 1405 {xdr_nfslog_nfs_fh3, xdr_nfslog_GETATTR3res, FALSE}, 1406 1407 /* NFSPROC3_SETATTR = 2 */ 1408 {xdr_nfslog_SETATTR3args, xdr_nfslog_SETATTR3res, TRUE}, 1409 1410 /* NFSPROC3_LOOKUP = 3 */ 1411 {xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE}, 1412 1413 /* NFSPROC3_ACCESS = 4 */ 1414 {xdr_nfslog_ACCESS3args, xdr_nfslog_ACCESS3res, FALSE}, 1415 1416 /* NFSPROC3_READLINK = 5 */ 1417 {xdr_nfslog_nfs_fh3, xdr_nfslog_READLINK3res, FALSE}, 1418 1419 /* NFSPROC3_READ = 6 */ 1420 {xdr_nfslog_READ3args, xdr_nfslog_READ3res, TRUE}, 1421 1422 /* NFSPROC3_WRITE = 7 */ 1423 {xdr_nfslog_WRITE3args, xdr_nfslog_WRITE3res, TRUE}, 1424 1425 /* NFSPROC3_CREATE = 8 */ 1426 {xdr_nfslog_CREATE3args, xdr_nfslog_CREATE3res, TRUE}, 1427 1428 /* NFSPROC3_MKDIR = 9 */ 1429 {xdr_nfslog_MKDIR3args, xdr_nfslog_MKDIR3res, TRUE}, 1430 1431 /* NFSPROC3_SYMLINK = 10 */ 1432 {xdr_nfslog_SYMLINK3args, xdr_nfslog_SYMLINK3res, TRUE}, 1433 1434 /* NFSPROC3_MKNOD = 11 */ 1435 {xdr_nfslog_MKNOD3args, xdr_nfslog_MKNOD3res, TRUE}, 1436 1437 /* NFSPROC3_REMOVE = 12 */ 1438 {xdr_nfslog_REMOVE3args, xdr_nfslog_REMOVE3res, TRUE}, 1439 1440 /* NFSPROC3_RMDIR = 13 */ 1441 {xdr_nfslog_RMDIR3args, xdr_nfslog_RMDIR3res, TRUE}, 1442 1443 /* NFSPROC3_RENAME = 14 */ 1444 {xdr_nfslog_RENAME3args, xdr_nfslog_RENAME3res, TRUE}, 1445 1446 /* NFSPROC3_LINK = 15 */ 1447 {xdr_nfslog_LINK3args, xdr_nfslog_LINK3res, TRUE}, 1448 1449 /* NFSPROC3_READDIR = 16 */ 1450 {xdr_nfslog_READDIR3args, xdr_nfslog_READDIR3res, TRUE}, 1451 1452 /* NFSPROC3_READDIRPLUS = 17 */ 1453 {xdr_nfslog_READDIRPLUS3args, xdr_nfslog_READDIRPLUS3res, TRUE}, 1454 1455 /* NFSPROC3_FSSTAT = 18 */ 1456 {xdr_nfslog_FSSTAT3args, xdr_nfslog_FSSTAT3res, FALSE}, 1457 1458 /* NFSPROC3_FSINFO = 19 */ 1459 {xdr_nfslog_FSINFO3args, xdr_nfslog_FSINFO3res, FALSE}, 1460 1461 /* NFSPROC3_PATHCONF = 20 */ 1462 {xdr_nfslog_PATHCONF3args, xdr_nfslog_PATHCONF3res, FALSE}, 1463 1464 /* NFSPROC3_COMMIT = 21 */ 1465 {xdr_nfslog_COMMIT3args, xdr_nfslog_COMMIT3res, FALSE}, 1466 }; 1467 1468 static struct nfslog_proc_disp nfslog_proc_v1[] = { 1469 /* 1470 * NFSLOG VERSION 1 1471 */ 1472 1473 /* NFSLOG_NULL = 0 */ 1474 {xdr_void, xdr_void, TRUE}, 1475 1476 /* NFSLOG_SHARE = 1 */ 1477 {xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE}, 1478 1479 /* NFSLOG_UNSHARE = 2 */ 1480 {xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE}, 1481 1482 /* NFSLOG_LOOKUP = 3 */ 1483 {xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE}, 1484 1485 /* NFSLOG_GETFH = 4 */ 1486 {xdr_nfslog_getfhargs, xdr_nfsstat, TRUE}, 1487 }; 1488 1489 static struct nfslog_vers_disp nfslog_vers_disptable[] = { 1490 {sizeof (nfslog_proc_v2) / sizeof (nfslog_proc_v2[0]), 1491 nfslog_proc_v2}, 1492 {sizeof (nfslog_proc_v3) / sizeof (nfslog_proc_v3[0]), 1493 nfslog_proc_v3}, 1494 }; 1495 1496 static struct nfslog_vers_disp nfslog_nfslog_vers_disptable[] = { 1497 {sizeof (nfslog_proc_v1) / sizeof (nfslog_proc_v1[0]), 1498 nfslog_proc_v1}, 1499 }; 1500 1501 static struct nfslog_prog_disp nfslog_dispatch_table[] = { 1502 {NFS_PROGRAM, NFS_VERSMIN, 1503 (sizeof (nfslog_vers_disptable) / 1504 sizeof (nfslog_vers_disptable[0])), 1505 nfslog_vers_disptable}, 1506 1507 {NFSLOG_PROGRAM, NFSLOG_VERSMIN, 1508 (sizeof (nfslog_nfslog_vers_disptable) / 1509 sizeof (nfslog_nfslog_vers_disptable[0])), 1510 nfslog_nfslog_vers_disptable}, 1511 }; 1512 1513 static int nfslog_dispatch_table_arglen = sizeof (nfslog_dispatch_table) / 1514 sizeof (nfslog_dispatch_table[0]); 1515 1516 /* 1517 * This function will determine the appropriate export info struct to use 1518 * and allocate a record id to be used in the written log buffer. 1519 * Usually this is a straightforward operation but the existence of the 1520 * multicomponent lookup and its semantics of crossing file system 1521 * boundaries add to the complexity. See the comments below... 1522 */ 1523 struct exportinfo * 1524 nfslog_get_exi( 1525 struct exportinfo *exi, 1526 struct svc_req *req, 1527 caddr_t res, 1528 unsigned int *nfslog_rec_id) 1529 { 1530 struct log_buffer *lb; 1531 struct exportinfo *exi_ret = NULL; 1532 fhandle_t *fh; 1533 nfs_fh3 *fh3; 1534 1535 if (exi == NULL) 1536 return (NULL); 1537 1538 /* 1539 * If the exi is marked for logging, allocate a record id and return 1540 */ 1541 if (exi->exi_export.ex_flags & EX_LOG) { 1542 lb = exi->exi_logbuffer; 1543 1544 /* obtain the unique record id for the caller */ 1545 *nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1); 1546 1547 /* 1548 * The caller will expect to be able to exi_rele() it, 1549 * so exi->exi_count must be incremented before it can 1550 * be returned, to make it uniform with exi_ret->exi_count 1551 */ 1552 mutex_enter(&exi->exi_lock); 1553 exi->exi_count++; 1554 mutex_exit(&exi->exi_lock); 1555 1556 return (exi); 1557 } 1558 1559 if (exi != exi_public) 1560 return (NULL); 1561 1562 /* 1563 * Here we have an exi that is not marked for logging. 1564 * It is possible that this request is a multicomponent lookup 1565 * that was done from the public file handle (not logged) and 1566 * the resulting file handle being returned to the client exists 1567 * in a file system that is being logged. If this is the case 1568 * we need to log this multicomponent lookup to the appropriate 1569 * log buffer. This will allow for the appropriate path name 1570 * mapping to occur at user level. 1571 */ 1572 if (req->rq_prog == NFS_PROGRAM) { 1573 switch (req->rq_vers) { 1574 case NFS_V3: 1575 if ((req->rq_proc == NFSPROC3_LOOKUP) && 1576 (((LOOKUP3res *)res)->status == NFS3_OK)) { 1577 fh3 = &((LOOKUP3res *)res)->res_u.ok.object; 1578 exi_ret = checkexport(&fh3->fh3_fsid, 1579 FH3TOXFIDP(fh3)); 1580 } 1581 break; 1582 1583 case NFS_VERSION: 1584 if ((req->rq_proc == RFS_LOOKUP) && 1585 (((struct nfsdiropres *) 1586 res)->dr_status == NFS_OK)) { 1587 fh = 1588 &((struct nfsdiropres *)res)->dr_u.dr_drok_u.drok_fhandle; 1589 exi_ret = checkexport(&fh->fh_fsid, 1590 (fid_t *)&fh->fh_xlen); 1591 } 1592 break; 1593 default: 1594 break; 1595 } 1596 } 1597 1598 if (exi_ret != NULL && exi_ret->exi_export.ex_flags & EX_LOG) { 1599 lb = exi_ret->exi_logbuffer; 1600 /* obtain the unique record id for the caller */ 1601 *nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1); 1602 1603 return (exi_ret); 1604 } 1605 return (NULL); 1606 } 1607 1608 #ifdef DEBUG 1609 static long long rfslog_records_ignored = 0; 1610 #endif 1611 1612 /* 1613 * nfslog_write_record - Fill in the record buffer for writing out. 1614 * If logrecp is null, log it, otherwise, malloc the record and return it. 1615 * 1616 * It is the responsibility of the caller to check whether this exportinfo 1617 * has logging enabled. 1618 * Note that nfslog_share_public_record() only needs to check for the 1619 * existence of at least one logbuffer to which the public filehandle record 1620 * needs to be logged. 1621 */ 1622 void 1623 nfslog_write_record(struct exportinfo *exi, struct svc_req *req, 1624 caddr_t args, caddr_t res, cred_t *cr, struct netbuf *pnb, 1625 unsigned int record_id, unsigned int which_buffers) 1626 { 1627 struct nfslog_prog_disp *progtable; /* prog struct */ 1628 struct nfslog_vers_disp *verstable; /* version struct */ 1629 struct nfslog_proc_disp *disp = NULL; /* proc struct */ 1630 int i, vers; 1631 void *log_cookie; /* for logrecord if */ 1632 caddr_t buffer; 1633 XDR xdrs; 1634 unsigned int final_size; 1635 int encode_ok; 1636 int alloc_indx; 1637 1638 ASSERT(exi != NULL); ASSERT(req != NULL); ASSERT(args != NULL); 1639 ASSERT(res != NULL); ASSERT(cr != NULL); 1640 1641 /* 1642 * Find program element 1643 * Search the list since program can not be used as index 1644 */ 1645 for (i = 0; (i < nfslog_dispatch_table_arglen); i++) { 1646 if (req->rq_prog == nfslog_dispatch_table[i].nfslog_dis_prog) 1647 break; 1648 } 1649 if (i >= nfslog_dispatch_table_arglen) { /* program not logged */ 1650 /* not an error */ 1651 return; 1652 } 1653 1654 /* 1655 * Extract the dispatch functions based on program/version 1656 */ 1657 progtable = &nfslog_dispatch_table[i]; 1658 vers = req->rq_vers - progtable->nfslog_dis_versmin; 1659 verstable = &progtable->nfslog_dis_vers_table[vers]; 1660 disp = &verstable->nfslog_dis_proc_table[req->rq_proc]; 1661 1662 if (!(exi->exi_export.ex_flags & EX_LOG_ALLOPS) && 1663 !disp->affects_transactions) { 1664 /* 1665 * Only interested in logging operations affecting 1666 * transaction generation. This is not one of them. 1667 */ 1668 #ifdef DEBUG 1669 rfslog_records_ignored++; 1670 #endif 1671 return; 1672 } 1673 1674 switch (req->rq_prog) { 1675 case NFS_PROGRAM: 1676 switch (req->rq_vers) { 1677 case NFS_V3: 1678 switch (req->rq_proc) { 1679 case NFSPROC3_READDIRPLUS: 1680 alloc_indx = MEDIUM_INDX; 1681 break; 1682 default: 1683 alloc_indx = SMALL_INDX; 1684 break; 1685 } 1686 break; 1687 default: 1688 alloc_indx = SMALL_INDX; 1689 break; 1690 } 1691 break; 1692 case NFSLOG_PROGRAM: 1693 alloc_indx = MEDIUM_INDX; 1694 break; 1695 default: 1696 alloc_indx = SMALL_INDX; 1697 break; 1698 } 1699 1700 do { 1701 encode_ok = FALSE; 1702 1703 /* Pick the size to alloc; end of the road - return */ 1704 if (nfslog_mem_alloc[alloc_indx].size == (-1)) { 1705 cmn_err(CE_WARN, 1706 "NFSLOG: unable to encode record - prog=%d " 1707 "proc = %d", req->rq_prog, req->rq_proc); 1708 return; 1709 } 1710 1711 buffer = nfslog_record_alloc(exi, alloc_indx, &log_cookie, 0); 1712 if (buffer == NULL) { 1713 /* Error processing - no space alloced */ 1714 rfs_log_bad++; 1715 cmn_err(CE_WARN, "NFSLOG: can't get record"); 1716 return; 1717 } 1718 1719 xdrmem_create(&xdrs, buffer, 1720 nfslog_mem_alloc[alloc_indx].size, XDR_ENCODE); 1721 1722 /* 1723 * Encode the header, args and results of the record 1724 */ 1725 if (xdr_nfslog_request_record(&xdrs, exi, req, cr, pnb, 1726 nfslog_mem_alloc[alloc_indx].size, record_id) && 1727 (*disp->xdrargs)(&xdrs, args) && 1728 (*disp->xdrres)(&xdrs, res)) { 1729 encode_ok = TRUE; 1730 1731 rfs_log_good++; 1732 /* 1733 * Get the final size of the encoded 1734 * data and insert that length at the 1735 * beginning. 1736 */ 1737 final_size = xdr_getpos(&xdrs); 1738 xdr_setpos(&xdrs, 0); 1739 (void) xdr_u_int(&xdrs, &final_size); 1740 } else { 1741 /* Oops, the encode failed so we need to free memory */ 1742 nfslog_record_put(log_cookie, 0, FALSE, which_buffers); 1743 alloc_indx++; 1744 } 1745 1746 } while (encode_ok == FALSE); 1747 1748 1749 /* 1750 * Take the final log record and put it in the log file. 1751 * This may be queued to the file internally and written 1752 * later unless the last parameter is TRUE. 1753 * If the record_id is 0 then this is most likely a share/unshare 1754 * request and it should be written synchronously to the log file. 1755 */ 1756 nfslog_record_put(log_cookie, final_size, 1757 (record_id == 0), which_buffers); 1758 } 1759 1760 static char * 1761 get_publicfh_path(int *alloc_length) 1762 { 1763 extern struct exportinfo *exi_public; 1764 char *pubpath; 1765 1766 rw_enter(&exported_lock, RW_READER); 1767 1768 *alloc_length = exi_public->exi_export.ex_pathlen + 1; 1769 pubpath = kmem_alloc(*alloc_length, KM_SLEEP); 1770 1771 (void) strcpy(pubpath, exi_public->exi_export.ex_path); 1772 1773 rw_exit(&exported_lock); 1774 1775 return (pubpath); 1776 } 1777 1778 static void 1779 log_public_record(struct exportinfo *exi, cred_t *cr) 1780 { 1781 struct svc_req req; 1782 struct netbuf nb = {0, 0, NULL}; 1783 int free_length = 0; 1784 diropargs3 args; 1785 LOOKUP3res res; 1786 1787 bzero(&req, sizeof (req)); 1788 req.rq_prog = NFSLOG_PROGRAM; 1789 req.rq_vers = NFSLOG_VERSION; 1790 req.rq_proc = NFSLOG_LOOKUP; 1791 req.rq_cred.oa_flavor = AUTH_NONE; 1792 1793 bzero(&args, sizeof (diropargs3)); 1794 bzero(&res, sizeof (LOOKUP3res)); 1795 1796 args.dir.fh3_length = 0; 1797 if ((args.name = get_publicfh_path(&free_length)) == NULL) 1798 return; 1799 args.dirp = &args.dir; 1800 1801 res.status = NFS3_OK; 1802 res.res_u.ok.object.fh3_length = 0; 1803 1804 /* 1805 * Calling this function with the exi_public 1806 * will have the effect of appending the record 1807 * to each of the open log buffers 1808 */ 1809 nfslog_write_record(exi, &req, 1810 (caddr_t)&args, (caddr_t)&res, cr, &nb, 0, NFSLOG_ALL_BUFFERS); 1811 1812 kmem_free(args.name, free_length); 1813 } 1814 1815 /* 1816 * nfslog_share_record - logs a share request. 1817 * This is not an NFS request, but we pretend here... 1818 */ 1819 void 1820 nfslog_share_record(struct exportinfo *exi, cred_t *cr) 1821 { 1822 struct svc_req req; 1823 int res = 0; 1824 struct netbuf nb = {0, 0, NULL}; 1825 1826 ASSERT(exi != NULL); 1827 1828 if (nfslog_buffer_list == NULL) 1829 return; 1830 1831 if (exi->exi_export.ex_flags & EX_LOG) { 1832 bzero(&req, sizeof (req)); 1833 req.rq_prog = NFSLOG_PROGRAM; 1834 req.rq_vers = NFSLOG_VERSION; 1835 req.rq_proc = NFSLOG_SHARE; 1836 req.rq_cred.oa_flavor = AUTH_NONE; 1837 nfslog_write_record(exi, &req, 1838 (caddr_t)exi, (caddr_t)&res, cr, 1839 &nb, 0, NFSLOG_ONE_BUFFER); 1840 } 1841 1842 log_public_record(exi, cr); 1843 } 1844 1845 /* 1846 * nfslog_unshare_record - logs an unshare request. 1847 * This is not an NFS request, but we pretend here... 1848 */ 1849 void 1850 nfslog_unshare_record(struct exportinfo *exi, cred_t *cr) 1851 { 1852 struct svc_req req; 1853 int res = 0; 1854 struct netbuf nb = {0, 0, NULL}; 1855 1856 ASSERT(exi != NULL); 1857 ASSERT(exi->exi_export.ex_flags & EX_LOG); 1858 1859 bzero(&req, sizeof (req)); 1860 req.rq_prog = NFSLOG_PROGRAM; 1861 req.rq_vers = NFSLOG_VERSION; 1862 req.rq_proc = NFSLOG_UNSHARE; 1863 req.rq_cred.oa_flavor = AUTH_NONE; 1864 nfslog_write_record(exi, &req, 1865 (caddr_t)exi, (caddr_t)&res, cr, &nb, 0, NFSLOG_ONE_BUFFER); 1866 } 1867 1868 1869 void 1870 nfslog_getfh(struct exportinfo *exi, 1871 fhandle *fh, 1872 char *fname, 1873 enum uio_seg seg, 1874 cred_t *cr) 1875 { 1876 struct svc_req req; 1877 int res = 0; 1878 struct netbuf nb = {0, 0, NULL}; 1879 int error = 0; 1880 char *namebuf; 1881 size_t len; 1882 nfslog_getfhargs gfh; 1883 1884 ASSERT(exi != NULL); 1885 ASSERT(exi->exi_export.ex_flags & EX_LOG); 1886 1887 bzero(&req, sizeof (req)); 1888 req.rq_prog = NFSLOG_PROGRAM; 1889 req.rq_vers = NFSLOG_VERSION; 1890 req.rq_proc = NFSLOG_GETFH; 1891 req.rq_cred.oa_flavor = AUTH_NONE; 1892 1893 namebuf = kmem_alloc(MAXPATHLEN + 4, KM_SLEEP); 1894 if (seg == UIO_USERSPACE) { 1895 error = copyinstr(fname, namebuf, MAXPATHLEN, &len); 1896 } else { 1897 error = copystr(fname, namebuf, MAXPATHLEN, &len); 1898 } 1899 1900 if (!error) { 1901 gfh.gfh_fh_buf = *fh; 1902 gfh.gfh_path = namebuf; 1903 1904 nfslog_write_record(exi, &req, 1905 (caddr_t)&gfh, (caddr_t)&res, cr, &nb, 0, 1906 NFSLOG_ONE_BUFFER); 1907 } 1908 kmem_free(namebuf, MAXPATHLEN + 4); 1909 } 1910