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