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