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 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 839 if (uio.uio_resid) 840 error = ENOSPC; 841 if (error) 842 (void) VOP_SETATTR(vp, &va, 0, CRED(), NULL); 843 } else { 844 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 845 if (!(lfp->lf_flags & L_PRINTED)) { 846 cmn_err(CE_WARN, 847 "NFS Logging: buffer file %s exceeds 2GB; " 848 "stopped writing buffer \n", lfp->lf_path); 849 } 850 error = ENOSPC; 851 } 852 } else { 853 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 854 } 855 856 kmem_free(iovp, size_iovecs); 857 858 out: 859 if (error) { 860 if (!(lfp->lf_flags & L_PRINTED)) { 861 nfs_cmn_err(error, CE_WARN, 862 "NFS Logging disabled for buffer %s - " 863 "write error = %m\n", lfp->lf_path); 864 lfp->lf_flags |= L_PRINTED; 865 } 866 } else if (lfp->lf_flags & (L_ERROR | L_PRINTED)) { 867 lfp->lf_flags &= ~(L_ERROR | L_PRINTED); 868 cmn_err(CE_WARN, 869 "NFS Logging re-enabled for buffer %s\n", lfp->lf_path); 870 } 871 872 return (error); 873 } 874 875 static void 876 nfslog_free_logrecords(struct lr_alloc *lrp_writers) 877 { 878 struct lr_alloc *lrp = lrp_writers; 879 struct lr_alloc *lrp_free; 880 881 do { 882 lrp_free = lrp; 883 884 lrp = lrp->next; 885 886 /* 887 * Check to see if we are supposed to free this structure 888 * and relese the log_buffer ref count. 889 * It may be the case that the caller does not want this 890 * structure and its record contents freed just yet. 891 */ 892 if ((lrp_free->lr_flags & LR_ALLOC_NOFREE) == 0) { 893 if (lrp_free->lb != NULL) 894 log_buffer_rele(lrp_free->lb); 895 if (lrp_free->alloc_cache) /* double check */ 896 kmem_cache_free(lrp_free->alloc_cache, 897 (void *)lrp_free); 898 } else { 899 /* 900 * after being pulled from the list the 901 * pointers need to be reinitialized. 902 */ 903 lrp_free->next = lrp_free; 904 lrp_free->prev = lrp_free; 905 } 906 907 } while (lrp != lrp_writers); 908 } 909 910 /* 911 * Rename lbp->lb_logfile to reflect the true name requested by 'share' 912 */ 913 static int 914 nfslog_logbuffer_rename(struct log_buffer *lbp) 915 { 916 struct log_file *lf; 917 int error; 918 struct log_file *logfile; 919 920 /* 921 * Try our best to get the cache records into the log file 922 * before the rename occurs. 923 */ 924 (void) nfslog_records_flush_to_disk(lbp); 925 926 /* 927 * Hold lb_lock before retrieving 928 * lb_logfile. 929 * Hold a reference to the 930 * "lf" structure. this is 931 * same as LOG_FILE_HOLD() 932 */ 933 mutex_enter(&(lbp)->lb_lock); 934 lf = lbp->lb_logfile; 935 mutex_enter(&(lf)->lf_lock); 936 mutex_exit(&(lbp)->lb_lock); 937 lf->lf_refcnt++; 938 mutex_exit(&(lf)->lf_lock); 939 940 LOGGING_DPRINT((10, "nfslog_logbuffer_rename: renaming %s to %s\n", 941 lf->lf_path, lbp->lb_path)); 942 943 /* 944 * rename the current buffer to what the daemon expects 945 */ 946 if (error = nfslog_logfile_rename(lf->lf_path, lbp->lb_path)) 947 goto out; 948 949 /* 950 * Create a new working buffer file and have all new data sent there. 951 */ 952 if (error = log_file_create(lbp->lb_path, &logfile)) { 953 /* Attempt to rename to original */ 954 (void) nfslog_logfile_rename(lbp->lb_path, lf->lf_path); 955 goto out; 956 } 957 958 /* 959 * Hold the lb_lock here, this will make 960 * all the threads trying to access lb->logfile block 961 * and get a new logfile structure instead of old one. 962 */ 963 mutex_enter(&(lbp)->lb_lock); 964 lbp->lb_logfile = logfile; 965 mutex_exit(&(lbp)->lb_lock); 966 967 LOG_FILE_RELE(lf); /* release log_buffer's reference */ 968 969 /* 970 * Wait for log_file to be in a quiescent state before we 971 * return to our caller to let it proceed with the reading of 972 * this file. 973 */ 974 nfslog_logfile_wait(lf); 975 976 out: 977 /* 978 * Release our reference on "lf" in two different cases. 979 * 1. Error condition, release only the reference 980 * that we held at the begining of this 981 * routine on "lf" structure. 982 * 2. Fall through condition, no errors but the old 983 * logfile structure "lf" has been replaced with 984 * the new "logfile" structure, so release the 985 * reference that was part of the creation of 986 * "lf" structure to free up the resources. 987 */ 988 989 LOG_FILE_RELE(lf); 990 991 return (error); 992 } 993 994 /* 995 * Renames the 'from' file to 'new'. 996 */ 997 static int 998 nfslog_logfile_rename(char *from, char *new) 999 { 1000 int error; 1001 1002 if (error = vn_rename(from, new, UIO_SYSSPACE)) { 1003 cmn_err(CE_WARN, 1004 "nfslog_logfile_rename: couldn't rename %s to %s\n", 1005 from, new); 1006 } 1007 return (error); 1008 } 1009 1010 /* 1011 * Wait for the log_file writers to finish before returning 1012 */ 1013 static void 1014 nfslog_logfile_wait(struct log_file *lf) 1015 { 1016 mutex_enter(&lf->lf_lock); 1017 while (lf->lf_writers > 0) { 1018 lf->lf_flags |= L_WAITING; 1019 (void) cv_wait_sig(&lf->lf_cv_waiters, &lf->lf_lock); 1020 } 1021 mutex_exit(&lf->lf_lock); 1022 } 1023 1024 static int 1025 nfslog_record_append2all(struct lr_alloc *lrp) 1026 { 1027 struct log_buffer *lbp, *nlbp; 1028 int error, ret_error = 0; 1029 int lr_flags = lrp->lr_flags; 1030 1031 rw_enter(&nfslog_buffer_list_lock, RW_READER); 1032 if ((lbp = nfslog_buffer_list) != NULL) 1033 LOG_BUFFER_HOLD(lbp); 1034 for (nlbp = NULL; lbp != NULL; lbp = nlbp) { 1035 if ((nlbp = lbp->lb_next) != NULL) { 1036 /* 1037 * Remember next element in the list 1038 */ 1039 LOG_BUFFER_HOLD(nlbp); 1040 } 1041 rw_exit(&nfslog_buffer_list_lock); 1042 1043 /* 1044 * Insert the record on the buffer's list to be written 1045 * and then flush the records to the log file. 1046 * Make sure to set the no free flag so that the 1047 * record can be used for the next write 1048 */ 1049 lrp->lr_flags = LR_ALLOC_NOFREE; 1050 1051 ASSERT(lbp != NULL); 1052 mutex_enter(&lbp->lb_lock); 1053 if (lbp->lb_records == NULL) { 1054 lbp->lb_records = (caddr_t)lrp; 1055 lbp->lb_num_recs = 1; 1056 lbp->lb_size_queued = lrp->size; 1057 } else { 1058 insque(lrp, ((struct lr_alloc *)lbp->lb_records)->prev); 1059 lbp->lb_num_recs++; 1060 lbp->lb_size_queued += lrp->size; 1061 } 1062 1063 /* 1064 * Flush log records to disk. 1065 * Function is called with lb_lock held. 1066 * Function drops the lb_lock on return. 1067 */ 1068 error = nfslog_records_flush_to_disk_nolock(lbp); 1069 1070 if (error) { 1071 ret_error = -1; 1072 nfs_cmn_err(error, CE_WARN, 1073 "rfsl_log_pubfh: could not append record to " 1074 "\"%s\" error = %m\n", lbp->lb_path); 1075 } 1076 log_buffer_rele(lbp); 1077 rw_enter(&nfslog_buffer_list_lock, RW_READER); 1078 } 1079 rw_exit(&nfslog_buffer_list_lock); 1080 1081 lrp->lr_flags = lr_flags; 1082 1083 return (ret_error); 1084 } 1085 1086 #ifdef DEBUG 1087 static int logging_debug = 0; 1088 1089 /* 1090 * 0) no debugging 1091 * 3) current test software 1092 * 10) random stuff 1093 */ 1094 void 1095 nfslog_dprint(const int level, const char *fmt, ...) 1096 { 1097 va_list args; 1098 1099 if (logging_debug == level || 1100 (logging_debug > 10 && (logging_debug - 10) >= level)) { 1101 va_start(args, fmt); 1102 (void) vprintf(fmt, args); 1103 va_end(args); 1104 } 1105 } 1106 1107 #endif /* DEBUG */ 1108 1109 /* 1110 * NFS Log Flush system call 1111 * Caller must check privileges. 1112 */ 1113 /* ARGSUSED */ 1114 int 1115 nfsl_flush(struct nfsl_flush_args *args, model_t model) 1116 { 1117 struct flush_thread_params *tparams; 1118 struct nfsl_flush_args *nfsl_args; 1119 int error = 0; 1120 ulong_t buffer_len; 1121 STRUCT_HANDLE(nfsl_flush_args, uap); 1122 1123 STRUCT_SET_HANDLE(uap, model, args); 1124 1125 tparams = (struct flush_thread_params *) 1126 kmem_zalloc(sizeof (*tparams), KM_SLEEP); 1127 1128 nfsl_args = &tparams->tp_args; 1129 nfsl_args->version = STRUCT_FGET(uap, version); 1130 if (nfsl_args->version != NFSL_FLUSH_ARGS_VERS) { 1131 cmn_err(CE_WARN, "nfsl_flush: exected version %d, got %d", 1132 NFSL_FLUSH_ARGS_VERS, nfsl_args->version); 1133 return (EIO); 1134 } 1135 1136 nfsl_args->directive = STRUCT_FGET(uap, directive); 1137 if ((nfsl_args->directive & NFSL_ALL) == 0) { 1138 /* 1139 * Process a specific buffer 1140 */ 1141 nfsl_args->buff_len = STRUCT_FGET(uap, buff_len); 1142 1143 nfsl_args->buff = (char *) 1144 kmem_alloc(nfsl_args->buff_len, KM_NOSLEEP); 1145 if (nfsl_args->buff == NULL) 1146 return (ENOMEM); 1147 1148 error = copyinstr((const char *)STRUCT_FGETP(uap, buff), 1149 nfsl_args->buff, nfsl_args->buff_len, &buffer_len); 1150 if (error) 1151 return (EFAULT); 1152 1153 if (nfsl_args->buff_len != buffer_len) 1154 return (EFAULT); 1155 } 1156 1157 LOGGING_DPRINT((10, "nfsl_flush: Flushing %s buffer(s)\n", 1158 nfsl_args->directive & NFSL_ALL ? "all" : nfsl_args->buff)); 1159 1160 if (nfsl_args->directive & NFSL_SYNC) { 1161 /* 1162 * Do the work synchronously 1163 */ 1164 nfslog_do_flush(tparams); 1165 error = tparams->tp_error; 1166 kmem_free(nfsl_args->buff, nfsl_args->buff_len); 1167 kmem_free(tparams, sizeof (*tparams)); 1168 } else { 1169 /* 1170 * Do the work asynchronously 1171 */ 1172 (void) thread_create(NULL, 0, nfslog_do_flush, 1173 tparams, 0, &p0, TS_RUN, minclsyspri); 1174 } 1175 1176 return (error); 1177 } 1178 1179 /* 1180 * This is where buffer flushing would occur, but there is no buffering 1181 * at this time. 1182 * Possibly rename the log buffer for processing. 1183 * Sets tparams->ta_error equal to the value of the error that occured, 1184 * 0 otherwise. 1185 * Returns ENOENT if the buffer is not found. 1186 */ 1187 static void 1188 nfslog_do_flush(struct flush_thread_params *tparams) 1189 { 1190 struct nfsl_flush_args *args; 1191 struct log_buffer *lbp; 1192 int error = ENOENT; 1193 int found = 0; 1194 char *buf_inprog; /* name of buff in progress */ 1195 int buf_inprog_len; 1196 1197 /* 1198 * Sanity check on the arguments. 1199 */ 1200 if (!tparams) 1201 return; 1202 args = &tparams->tp_args; 1203 if (!args) 1204 return; 1205 1206 rw_enter(&nfslog_buffer_list_lock, RW_READER); 1207 for (lbp = nfslog_buffer_list; lbp != NULL; lbp = lbp->lb_next) { 1208 if (args->directive & NFSL_ALL) { 1209 (void) nfslog_records_flush_to_disk(lbp); 1210 } else { 1211 if ((strcmp(lbp->lb_path, args->buff) == 0) && 1212 (args->directive & NFSL_RENAME)) { 1213 1214 error = nfslog_logbuffer_rename(lbp); 1215 found++; 1216 break; 1217 } 1218 } 1219 } 1220 rw_exit(&nfslog_buffer_list_lock); 1221 1222 if (!found && ((args->directive & NFSL_ALL) == 0) && 1223 (args->directive & NFSL_RENAME)) { 1224 /* 1225 * The specified buffer is not currently in use, 1226 * simply rename the file indicated. 1227 */ 1228 buf_inprog_len = strlen(args->buff) + 1229 strlen(LOG_INPROG_STRING) + 1; 1230 buf_inprog = (caddr_t)kmem_alloc(buf_inprog_len, KM_SLEEP); 1231 (void) sprintf(buf_inprog, "%s%s", 1232 args->buff, LOG_INPROG_STRING); 1233 1234 error = nfslog_logfile_rename(buf_inprog, args->buff); 1235 1236 kmem_free(buf_inprog, buf_inprog_len); 1237 } 1238 1239 out: 1240 if ((args->directive & NFSL_SYNC) == 0) { 1241 /* 1242 * Work was performed asynchronously, the caller is 1243 * no longer waiting for us. 1244 * Free the thread arguments and exit. 1245 */ 1246 kmem_free(args->buff, args->buff_len); 1247 kmem_free(tparams, sizeof (*tparams)); 1248 thread_exit(); 1249 /* NOTREACHED */ 1250 } 1251 1252 tparams->tp_error = error; 1253 } 1254 1255 /* 1256 * Generate buffer_header. 1257 * 'loghdr' points the the buffer_header, and *reclen 1258 * contains the length of the buffer. 1259 */ 1260 static void 1261 create_buffer_header(caddr_t *loghdr, size_t *reclen, size_t *freesize) 1262 { 1263 timestruc_t now; 1264 nfslog_buffer_header lh; 1265 XDR xdrs; 1266 unsigned int final_size; 1267 1268 1269 /* pick some size that will hold the buffer_header */ 1270 *freesize = NFSLOG_SMALL_RECORD_SIZE; 1271 1272 /* 1273 * Fill header 1274 */ 1275 lh.bh_length = 0; /* don't know yet how large it will be */ 1276 lh.bh_version = NFSLOG_BUF_VERSION; 1277 lh.bh_flags = 0; 1278 lh.bh_offset = 0; 1279 gethrestime(&now); 1280 TIMESPEC_TO_TIMESPEC32(&lh.bh_timestamp, &now); 1281 1282 /* 1283 * Encode the header 1284 */ 1285 *loghdr = (caddr_t)kmem_alloc(*freesize, KM_SLEEP); 1286 xdrmem_create(&xdrs, *loghdr, *freesize, XDR_ENCODE); 1287 1288 (void) xdr_nfslog_buffer_header(&xdrs, &lh); 1289 1290 /* 1291 * Reset with final size of the encoded data 1292 */ 1293 final_size = xdr_getpos(&xdrs); 1294 xdr_setpos(&xdrs, 0); 1295 (void) xdr_u_int(&xdrs, &final_size); 1296 1297 *reclen = (size_t)final_size; 1298 } 1299 1300 /* 1301 * **************************************************************** 1302 * RPC dispatch table for logging 1303 * Indexed by program, version, proc 1304 * Based on NFS dispatch table. 1305 */ 1306 struct nfslog_proc_disp { 1307 bool_t (*xdrargs)(); 1308 bool_t (*xdrres)(); 1309 bool_t affects_transactions; /* Operation affects transaction */ 1310 /* processing */ 1311 }; 1312 1313 struct nfslog_vers_disp { 1314 int nfslog_dis_nprocs; /* number of procs */ 1315 struct nfslog_proc_disp *nfslog_dis_proc_table; /* proc array */ 1316 }; 1317 1318 struct nfslog_prog_disp { 1319 int nfslog_dis_prog; /* program number */ 1320 int nfslog_dis_versmin; /* Minimum version value */ 1321 int nfslog_dis_nvers; /* Number of version values */ 1322 struct nfslog_vers_disp *nfslog_dis_vers_table; /* versions array */ 1323 }; 1324 1325 static int rfs_log_bad = 0; /* incremented on bad log attempts */ 1326 static int rfs_log_good = 0; /* incremented on successful log attempts */ 1327 1328 /* 1329 * Define the actions taken per prog/vers/proc: 1330 * 1331 * In some cases, the nl types are the same as the nfs types and a simple 1332 * bcopy should suffice. Rather that define tens of identical procedures, 1333 * simply define these to bcopy. Similarly this takes care of different 1334 * procs that use same parameter struct. 1335 */ 1336 1337 static struct nfslog_proc_disp nfslog_proc_v2[] = { 1338 /* 1339 * NFS VERSION 2 1340 */ 1341 1342 /* RFS_NULL = 0 */ 1343 {xdr_void, xdr_void, FALSE}, 1344 1345 /* RFS_GETATTR = 1 */ 1346 {xdr_fhandle, xdr_nfslog_getattrres, FALSE}, 1347 1348 /* RFS_SETATTR = 2 */ 1349 {xdr_nfslog_setattrargs, xdr_nfsstat, TRUE}, 1350 1351 /* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */ 1352 {xdr_void, xdr_void, FALSE}, 1353 1354 /* RFS_LOOKUP = 4 */ 1355 {xdr_nfslog_diropargs, xdr_nfslog_diropres, TRUE}, 1356 1357 /* RFS_READLINK = 5 */ 1358 {xdr_fhandle, xdr_nfslog_rdlnres, FALSE}, 1359 1360 /* RFS_READ = 6 */ 1361 {xdr_nfslog_nfsreadargs, xdr_nfslog_rdresult, TRUE}, 1362 1363 /* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */ 1364 {xdr_void, xdr_void, FALSE}, 1365 1366 /* RFS_WRITE = 8 */ 1367 {xdr_nfslog_writeargs, xdr_nfslog_writeresult, TRUE}, 1368 1369 /* RFS_CREATE = 9 */ 1370 {xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE}, 1371 1372 /* RFS_REMOVE = 10 */ 1373 {xdr_nfslog_diropargs, xdr_nfsstat, TRUE}, 1374 1375 /* RFS_RENAME = 11 */ 1376 {xdr_nfslog_rnmargs, xdr_nfsstat, TRUE}, 1377 1378 /* RFS_LINK = 12 */ 1379 {xdr_nfslog_linkargs, xdr_nfsstat, TRUE}, 1380 1381 /* RFS_SYMLINK = 13 */ 1382 {xdr_nfslog_symlinkargs, xdr_nfsstat, TRUE}, 1383 1384 /* RFS_MKDIR = 14 */ 1385 {xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE}, 1386 1387 /* RFS_RMDIR = 15 */ 1388 {xdr_nfslog_diropargs, xdr_nfsstat, TRUE}, 1389 1390 /* RFS_READDIR = 16 */ 1391 {xdr_nfslog_rddirargs, xdr_nfslog_rddirres, TRUE}, 1392 1393 /* RFS_STATFS = 17 */ 1394 {xdr_fhandle, xdr_nfslog_statfs, FALSE}, 1395 }; 1396 1397 1398 /* 1399 * NFS VERSION 3 1400 */ 1401 1402 static struct nfslog_proc_disp nfslog_proc_v3[] = { 1403 1404 /* NFSPROC3_NULL = 0 */ 1405 {xdr_void, xdr_void, FALSE}, 1406 1407 /* NFSPROC3_GETATTR = 1 */ 1408 {xdr_nfslog_nfs_fh3, xdr_nfslog_GETATTR3res, FALSE}, 1409 1410 /* NFSPROC3_SETATTR = 2 */ 1411 {xdr_nfslog_SETATTR3args, xdr_nfslog_SETATTR3res, TRUE}, 1412 1413 /* NFSPROC3_LOOKUP = 3 */ 1414 {xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE}, 1415 1416 /* NFSPROC3_ACCESS = 4 */ 1417 {xdr_nfslog_ACCESS3args, xdr_nfslog_ACCESS3res, FALSE}, 1418 1419 /* NFSPROC3_READLINK = 5 */ 1420 {xdr_nfslog_nfs_fh3, xdr_nfslog_READLINK3res, FALSE}, 1421 1422 /* NFSPROC3_READ = 6 */ 1423 {xdr_nfslog_READ3args, xdr_nfslog_READ3res, TRUE}, 1424 1425 /* NFSPROC3_WRITE = 7 */ 1426 {xdr_nfslog_WRITE3args, xdr_nfslog_WRITE3res, TRUE}, 1427 1428 /* NFSPROC3_CREATE = 8 */ 1429 {xdr_nfslog_CREATE3args, xdr_nfslog_CREATE3res, TRUE}, 1430 1431 /* NFSPROC3_MKDIR = 9 */ 1432 {xdr_nfslog_MKDIR3args, xdr_nfslog_MKDIR3res, TRUE}, 1433 1434 /* NFSPROC3_SYMLINK = 10 */ 1435 {xdr_nfslog_SYMLINK3args, xdr_nfslog_SYMLINK3res, TRUE}, 1436 1437 /* NFSPROC3_MKNOD = 11 */ 1438 {xdr_nfslog_MKNOD3args, xdr_nfslog_MKNOD3res, TRUE}, 1439 1440 /* NFSPROC3_REMOVE = 12 */ 1441 {xdr_nfslog_REMOVE3args, xdr_nfslog_REMOVE3res, TRUE}, 1442 1443 /* NFSPROC3_RMDIR = 13 */ 1444 {xdr_nfslog_RMDIR3args, xdr_nfslog_RMDIR3res, TRUE}, 1445 1446 /* NFSPROC3_RENAME = 14 */ 1447 {xdr_nfslog_RENAME3args, xdr_nfslog_RENAME3res, TRUE}, 1448 1449 /* NFSPROC3_LINK = 15 */ 1450 {xdr_nfslog_LINK3args, xdr_nfslog_LINK3res, TRUE}, 1451 1452 /* NFSPROC3_READDIR = 16 */ 1453 {xdr_nfslog_READDIR3args, xdr_nfslog_READDIR3res, TRUE}, 1454 1455 /* NFSPROC3_READDIRPLUS = 17 */ 1456 {xdr_nfslog_READDIRPLUS3args, xdr_nfslog_READDIRPLUS3res, TRUE}, 1457 1458 /* NFSPROC3_FSSTAT = 18 */ 1459 {xdr_nfslog_FSSTAT3args, xdr_nfslog_FSSTAT3res, FALSE}, 1460 1461 /* NFSPROC3_FSINFO = 19 */ 1462 {xdr_nfslog_FSINFO3args, xdr_nfslog_FSINFO3res, FALSE}, 1463 1464 /* NFSPROC3_PATHCONF = 20 */ 1465 {xdr_nfslog_PATHCONF3args, xdr_nfslog_PATHCONF3res, FALSE}, 1466 1467 /* NFSPROC3_COMMIT = 21 */ 1468 {xdr_nfslog_COMMIT3args, xdr_nfslog_COMMIT3res, FALSE}, 1469 }; 1470 1471 static struct nfslog_proc_disp nfslog_proc_v1[] = { 1472 /* 1473 * NFSLOG VERSION 1 1474 */ 1475 1476 /* NFSLOG_NULL = 0 */ 1477 {xdr_void, xdr_void, TRUE}, 1478 1479 /* NFSLOG_SHARE = 1 */ 1480 {xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE}, 1481 1482 /* NFSLOG_UNSHARE = 2 */ 1483 {xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE}, 1484 1485 /* NFSLOG_LOOKUP = 3 */ 1486 {xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE}, 1487 1488 /* NFSLOG_GETFH = 4 */ 1489 {xdr_nfslog_getfhargs, xdr_nfsstat, TRUE}, 1490 }; 1491 1492 static struct nfslog_vers_disp nfslog_vers_disptable[] = { 1493 {sizeof (nfslog_proc_v2) / sizeof (nfslog_proc_v2[0]), 1494 nfslog_proc_v2}, 1495 {sizeof (nfslog_proc_v3) / sizeof (nfslog_proc_v3[0]), 1496 nfslog_proc_v3}, 1497 }; 1498 1499 static struct nfslog_vers_disp nfslog_nfslog_vers_disptable[] = { 1500 {sizeof (nfslog_proc_v1) / sizeof (nfslog_proc_v1[0]), 1501 nfslog_proc_v1}, 1502 }; 1503 1504 static struct nfslog_prog_disp nfslog_dispatch_table[] = { 1505 {NFS_PROGRAM, NFS_VERSMIN, 1506 (sizeof (nfslog_vers_disptable) / 1507 sizeof (nfslog_vers_disptable[0])), 1508 nfslog_vers_disptable}, 1509 1510 {NFSLOG_PROGRAM, NFSLOG_VERSMIN, 1511 (sizeof (nfslog_nfslog_vers_disptable) / 1512 sizeof (nfslog_nfslog_vers_disptable[0])), 1513 nfslog_nfslog_vers_disptable}, 1514 }; 1515 1516 static int nfslog_dispatch_table_arglen = sizeof (nfslog_dispatch_table) / 1517 sizeof (nfslog_dispatch_table[0]); 1518 1519 /* 1520 * This function will determine the appropriate export info struct to use 1521 * and allocate a record id to be used in the written log buffer. 1522 * Usually this is a straightforward operation but the existence of the 1523 * multicomponent lookup and its semantics of crossing file system 1524 * boundaries add to the complexity. See the comments below... 1525 */ 1526 struct exportinfo * 1527 nfslog_get_exi( 1528 struct exportinfo *exi, 1529 struct svc_req *req, 1530 caddr_t res, 1531 unsigned int *nfslog_rec_id) 1532 { 1533 struct log_buffer *lb; 1534 struct exportinfo *exi_ret = NULL; 1535 fhandle_t *fh; 1536 nfs_fh3 *fh3; 1537 1538 if (exi == NULL) 1539 return (NULL); 1540 1541 /* 1542 * If the exi is marked for logging, allocate a record id and return 1543 */ 1544 if (exi->exi_export.ex_flags & EX_LOG) { 1545 lb = exi->exi_logbuffer; 1546 1547 /* obtain the unique record id for the caller */ 1548 *nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1); 1549 1550 /* 1551 * The caller will expect to be able to exi_rele() it, 1552 * so exi->exi_count must be incremented before it can 1553 * be returned, to make it uniform with exi_ret->exi_count 1554 */ 1555 mutex_enter(&exi->exi_lock); 1556 exi->exi_count++; 1557 mutex_exit(&exi->exi_lock); 1558 1559 return (exi); 1560 } 1561 1562 if (exi != exi_public) 1563 return (NULL); 1564 1565 /* 1566 * Here we have an exi that is not marked for logging. 1567 * It is possible that this request is a multicomponent lookup 1568 * that was done from the public file handle (not logged) and 1569 * the resulting file handle being returned to the client exists 1570 * in a file system that is being logged. If this is the case 1571 * we need to log this multicomponent lookup to the appropriate 1572 * log buffer. This will allow for the appropriate path name 1573 * mapping to occur at user level. 1574 */ 1575 if (req->rq_prog == NFS_PROGRAM) { 1576 switch (req->rq_vers) { 1577 case NFS_V3: 1578 if ((req->rq_proc == NFSPROC3_LOOKUP) && 1579 (((LOOKUP3res *)res)->status == NFS3_OK)) { 1580 fh3 = &((LOOKUP3res *)res)->res_u.ok.object; 1581 exi_ret = checkexport(&fh3->fh3_fsid, 1582 FH3TOXFIDP(fh3)); 1583 } 1584 break; 1585 1586 case NFS_VERSION: 1587 if ((req->rq_proc == RFS_LOOKUP) && 1588 (((struct nfsdiropres *) 1589 res)->dr_status == NFS_OK)) { 1590 fh = 1591 &((struct nfsdiropres *)res)->dr_u.dr_drok_u.drok_fhandle; 1592 exi_ret = checkexport(&fh->fh_fsid, 1593 (fid_t *)&fh->fh_xlen); 1594 } 1595 break; 1596 default: 1597 break; 1598 } 1599 } 1600 1601 if (exi_ret != NULL && exi_ret->exi_export.ex_flags & EX_LOG) { 1602 lb = exi_ret->exi_logbuffer; 1603 /* obtain the unique record id for the caller */ 1604 *nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1); 1605 1606 return (exi_ret); 1607 } 1608 return (NULL); 1609 } 1610 1611 #ifdef DEBUG 1612 static long long rfslog_records_ignored = 0; 1613 #endif 1614 1615 /* 1616 * nfslog_write_record - Fill in the record buffer for writing out. 1617 * If logrecp is null, log it, otherwise, malloc the record and return it. 1618 * 1619 * It is the responsibility of the caller to check whether this exportinfo 1620 * has logging enabled. 1621 * Note that nfslog_share_public_record() only needs to check for the 1622 * existence of at least one logbuffer to which the public filehandle record 1623 * needs to be logged. 1624 */ 1625 void 1626 nfslog_write_record(struct exportinfo *exi, struct svc_req *req, 1627 caddr_t args, caddr_t res, cred_t *cr, struct netbuf *pnb, 1628 unsigned int record_id, unsigned int which_buffers) 1629 { 1630 struct nfslog_prog_disp *progtable; /* prog struct */ 1631 struct nfslog_vers_disp *verstable; /* version struct */ 1632 struct nfslog_proc_disp *disp = NULL; /* proc struct */ 1633 int i, vers; 1634 void *log_cookie; /* for logrecord if */ 1635 caddr_t buffer; 1636 XDR xdrs; 1637 unsigned int final_size; 1638 int encode_ok; 1639 int alloc_indx; 1640 1641 ASSERT(exi != NULL); ASSERT(req != NULL); ASSERT(args != NULL); 1642 ASSERT(res != NULL); ASSERT(cr != NULL); 1643 1644 /* 1645 * Find program element 1646 * Search the list since program can not be used as index 1647 */ 1648 for (i = 0; (i < nfslog_dispatch_table_arglen); i++) { 1649 if (req->rq_prog == nfslog_dispatch_table[i].nfslog_dis_prog) 1650 break; 1651 } 1652 if (i >= nfslog_dispatch_table_arglen) { /* program not logged */ 1653 /* not an error */ 1654 return; 1655 } 1656 1657 /* 1658 * Extract the dispatch functions based on program/version 1659 */ 1660 progtable = &nfslog_dispatch_table[i]; 1661 vers = req->rq_vers - progtable->nfslog_dis_versmin; 1662 verstable = &progtable->nfslog_dis_vers_table[vers]; 1663 disp = &verstable->nfslog_dis_proc_table[req->rq_proc]; 1664 1665 if (!(exi->exi_export.ex_flags & EX_LOG_ALLOPS) && 1666 !disp->affects_transactions) { 1667 /* 1668 * Only interested in logging operations affecting 1669 * transaction generation. This is not one of them. 1670 */ 1671 #ifdef DEBUG 1672 rfslog_records_ignored++; 1673 #endif 1674 return; 1675 } 1676 1677 switch (req->rq_prog) { 1678 case NFS_PROGRAM: 1679 switch (req->rq_vers) { 1680 case NFS_V3: 1681 switch (req->rq_proc) { 1682 case NFSPROC3_READDIRPLUS: 1683 alloc_indx = MEDIUM_INDX; 1684 break; 1685 default: 1686 alloc_indx = SMALL_INDX; 1687 break; 1688 } 1689 break; 1690 default: 1691 alloc_indx = SMALL_INDX; 1692 break; 1693 } 1694 break; 1695 case NFSLOG_PROGRAM: 1696 alloc_indx = MEDIUM_INDX; 1697 break; 1698 default: 1699 alloc_indx = SMALL_INDX; 1700 break; 1701 } 1702 1703 do { 1704 encode_ok = FALSE; 1705 1706 /* Pick the size to alloc; end of the road - return */ 1707 if (nfslog_mem_alloc[alloc_indx].size == (-1)) { 1708 cmn_err(CE_WARN, 1709 "NFSLOG: unable to encode record - prog=%d " 1710 "proc = %d", req->rq_prog, req->rq_proc); 1711 return; 1712 } 1713 1714 buffer = nfslog_record_alloc(exi, alloc_indx, &log_cookie, 0); 1715 if (buffer == NULL) { 1716 /* Error processing - no space alloced */ 1717 rfs_log_bad++; 1718 cmn_err(CE_WARN, "NFSLOG: can't get record"); 1719 return; 1720 } 1721 1722 xdrmem_create(&xdrs, buffer, 1723 nfslog_mem_alloc[alloc_indx].size, XDR_ENCODE); 1724 1725 /* 1726 * Encode the header, args and results of the record 1727 */ 1728 if (xdr_nfslog_request_record(&xdrs, exi, req, cr, pnb, 1729 nfslog_mem_alloc[alloc_indx].size, record_id) && 1730 (*disp->xdrargs)(&xdrs, args) && 1731 (*disp->xdrres)(&xdrs, res)) { 1732 encode_ok = TRUE; 1733 1734 rfs_log_good++; 1735 /* 1736 * Get the final size of the encoded 1737 * data and insert that length at the 1738 * beginning. 1739 */ 1740 final_size = xdr_getpos(&xdrs); 1741 xdr_setpos(&xdrs, 0); 1742 (void) xdr_u_int(&xdrs, &final_size); 1743 } else { 1744 /* Oops, the encode failed so we need to free memory */ 1745 nfslog_record_put(log_cookie, 0, FALSE, which_buffers); 1746 alloc_indx++; 1747 } 1748 1749 } while (encode_ok == FALSE); 1750 1751 1752 /* 1753 * Take the final log record and put it in the log file. 1754 * This may be queued to the file internally and written 1755 * later unless the last parameter is TRUE. 1756 * If the record_id is 0 then this is most likely a share/unshare 1757 * request and it should be written synchronously to the log file. 1758 */ 1759 nfslog_record_put(log_cookie, final_size, 1760 (record_id == 0), which_buffers); 1761 } 1762 1763 static char * 1764 get_publicfh_path(int *alloc_length) 1765 { 1766 extern struct exportinfo *exi_public; 1767 char *pubpath; 1768 1769 rw_enter(&exported_lock, RW_READER); 1770 1771 *alloc_length = exi_public->exi_export.ex_pathlen + 1; 1772 pubpath = kmem_alloc(*alloc_length, KM_SLEEP); 1773 1774 (void) strcpy(pubpath, exi_public->exi_export.ex_path); 1775 1776 rw_exit(&exported_lock); 1777 1778 return (pubpath); 1779 } 1780 1781 static void 1782 log_public_record(struct exportinfo *exi, cred_t *cr) 1783 { 1784 struct svc_req req; 1785 struct netbuf nb = {0, 0, NULL}; 1786 int free_length = 0; 1787 diropargs3 args; 1788 LOOKUP3res res; 1789 1790 bzero(&req, sizeof (req)); 1791 req.rq_prog = NFSLOG_PROGRAM; 1792 req.rq_vers = NFSLOG_VERSION; 1793 req.rq_proc = NFSLOG_LOOKUP; 1794 req.rq_cred.oa_flavor = AUTH_NONE; 1795 1796 bzero(&args, sizeof (diropargs3)); 1797 bzero(&res, sizeof (LOOKUP3res)); 1798 1799 args.dir.fh3_length = 0; 1800 if ((args.name = get_publicfh_path(&free_length)) == NULL) 1801 return; 1802 args.dirp = &args.dir; 1803 1804 res.status = NFS3_OK; 1805 res.res_u.ok.object.fh3_length = 0; 1806 1807 /* 1808 * Calling this function with the exi_public 1809 * will have the effect of appending the record 1810 * to each of the open log buffers 1811 */ 1812 nfslog_write_record(exi, &req, 1813 (caddr_t)&args, (caddr_t)&res, cr, &nb, 0, NFSLOG_ALL_BUFFERS); 1814 1815 kmem_free(args.name, free_length); 1816 } 1817 1818 /* 1819 * nfslog_share_record - logs a share request. 1820 * This is not an NFS request, but we pretend here... 1821 */ 1822 void 1823 nfslog_share_record(struct exportinfo *exi, cred_t *cr) 1824 { 1825 struct svc_req req; 1826 int res = 0; 1827 struct netbuf nb = {0, 0, NULL}; 1828 1829 ASSERT(exi != NULL); 1830 1831 if (nfslog_buffer_list == NULL) 1832 return; 1833 1834 if (exi->exi_export.ex_flags & EX_LOG) { 1835 bzero(&req, sizeof (req)); 1836 req.rq_prog = NFSLOG_PROGRAM; 1837 req.rq_vers = NFSLOG_VERSION; 1838 req.rq_proc = NFSLOG_SHARE; 1839 req.rq_cred.oa_flavor = AUTH_NONE; 1840 nfslog_write_record(exi, &req, 1841 (caddr_t)exi, (caddr_t)&res, cr, 1842 &nb, 0, NFSLOG_ONE_BUFFER); 1843 } 1844 1845 log_public_record(exi, cr); 1846 } 1847 1848 /* 1849 * nfslog_unshare_record - logs an unshare request. 1850 * This is not an NFS request, but we pretend here... 1851 */ 1852 void 1853 nfslog_unshare_record(struct exportinfo *exi, cred_t *cr) 1854 { 1855 struct svc_req req; 1856 int res = 0; 1857 struct netbuf nb = {0, 0, NULL}; 1858 1859 ASSERT(exi != NULL); 1860 ASSERT(exi->exi_export.ex_flags & EX_LOG); 1861 1862 bzero(&req, sizeof (req)); 1863 req.rq_prog = NFSLOG_PROGRAM; 1864 req.rq_vers = NFSLOG_VERSION; 1865 req.rq_proc = NFSLOG_UNSHARE; 1866 req.rq_cred.oa_flavor = AUTH_NONE; 1867 nfslog_write_record(exi, &req, 1868 (caddr_t)exi, (caddr_t)&res, cr, &nb, 0, NFSLOG_ONE_BUFFER); 1869 } 1870 1871 1872 void 1873 nfslog_getfh(struct exportinfo *exi, 1874 fhandle *fh, 1875 char *fname, 1876 enum uio_seg seg, 1877 cred_t *cr) 1878 { 1879 struct svc_req req; 1880 int res = 0; 1881 struct netbuf nb = {0, 0, NULL}; 1882 int error = 0; 1883 char *namebuf; 1884 size_t len; 1885 nfslog_getfhargs gfh; 1886 1887 ASSERT(exi != NULL); 1888 ASSERT(exi->exi_export.ex_flags & EX_LOG); 1889 1890 bzero(&req, sizeof (req)); 1891 req.rq_prog = NFSLOG_PROGRAM; 1892 req.rq_vers = NFSLOG_VERSION; 1893 req.rq_proc = NFSLOG_GETFH; 1894 req.rq_cred.oa_flavor = AUTH_NONE; 1895 1896 namebuf = kmem_alloc(MAXPATHLEN + 4, KM_SLEEP); 1897 if (seg == UIO_USERSPACE) { 1898 error = copyinstr(fname, namebuf, MAXPATHLEN, &len); 1899 } else { 1900 error = copystr(fname, namebuf, MAXPATHLEN, &len); 1901 } 1902 1903 if (!error) { 1904 gfh.gfh_fh_buf = *fh; 1905 gfh.gfh_path = namebuf; 1906 1907 nfslog_write_record(exi, &req, 1908 (caddr_t)&gfh, (caddr_t)&res, cr, &nb, 0, 1909 NFSLOG_ONE_BUFFER); 1910 } 1911 kmem_free(namebuf, MAXPATHLEN + 4); 1912 } 1913