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 out: 1249 if ((args->directive & NFSL_SYNC) == 0) { 1250 /* 1251 * Work was performed asynchronously, the caller is 1252 * no longer waiting for us. 1253 * Free the thread arguments and exit. 1254 */ 1255 kmem_free(args->buff, args->buff_len); 1256 kmem_free(tparams, sizeof (*tparams)); 1257 zthread_exit(); 1258 } 1259 1260 tparams->tp_error = error; 1261 } 1262 1263 /* 1264 * Generate buffer_header. 1265 * 'loghdr' points the the buffer_header, and *reclen 1266 * contains the length of the buffer. 1267 */ 1268 static void 1269 create_buffer_header(caddr_t *loghdr, size_t *reclen, size_t *freesize) 1270 { 1271 timestruc_t now; 1272 nfslog_buffer_header lh; 1273 XDR xdrs; 1274 unsigned int final_size; 1275 1276 1277 /* pick some size that will hold the buffer_header */ 1278 *freesize = NFSLOG_SMALL_RECORD_SIZE; 1279 1280 /* 1281 * Fill header 1282 */ 1283 lh.bh_length = 0; /* don't know yet how large it will be */ 1284 lh.bh_version = NFSLOG_BUF_VERSION; 1285 lh.bh_flags = 0; 1286 lh.bh_offset = 0; 1287 gethrestime(&now); 1288 TIMESPEC_TO_TIMESPEC32(&lh.bh_timestamp, &now); 1289 1290 /* 1291 * Encode the header 1292 */ 1293 *loghdr = (caddr_t)kmem_alloc(*freesize, KM_SLEEP); 1294 xdrmem_create(&xdrs, *loghdr, *freesize, XDR_ENCODE); 1295 1296 (void) xdr_nfslog_buffer_header(&xdrs, &lh); 1297 1298 /* 1299 * Reset with final size of the encoded data 1300 */ 1301 final_size = xdr_getpos(&xdrs); 1302 xdr_setpos(&xdrs, 0); 1303 (void) xdr_u_int(&xdrs, &final_size); 1304 1305 *reclen = (size_t)final_size; 1306 } 1307 1308 /* 1309 * **************************************************************** 1310 * RPC dispatch table for logging 1311 * Indexed by program, version, proc 1312 * Based on NFS dispatch table. 1313 */ 1314 struct nfslog_proc_disp { 1315 bool_t (*xdrargs)(); 1316 bool_t (*xdrres)(); 1317 bool_t affects_transactions; /* Operation affects transaction */ 1318 /* processing */ 1319 }; 1320 1321 struct nfslog_vers_disp { 1322 int nfslog_dis_nprocs; /* number of procs */ 1323 struct nfslog_proc_disp *nfslog_dis_proc_table; /* proc array */ 1324 }; 1325 1326 struct nfslog_prog_disp { 1327 int nfslog_dis_prog; /* program number */ 1328 int nfslog_dis_versmin; /* Minimum version value */ 1329 int nfslog_dis_nvers; /* Number of version values */ 1330 struct nfslog_vers_disp *nfslog_dis_vers_table; /* versions array */ 1331 }; 1332 1333 static int rfs_log_bad = 0; /* incremented on bad log attempts */ 1334 static int rfs_log_good = 0; /* incremented on successful log attempts */ 1335 1336 /* 1337 * Define the actions taken per prog/vers/proc: 1338 * 1339 * In some cases, the nl types are the same as the nfs types and a simple 1340 * bcopy should suffice. Rather that define tens of identical procedures, 1341 * simply define these to bcopy. Similarly this takes care of different 1342 * procs that use same parameter struct. 1343 */ 1344 1345 static struct nfslog_proc_disp nfslog_proc_v2[] = { 1346 /* 1347 * NFS VERSION 2 1348 */ 1349 1350 /* RFS_NULL = 0 */ 1351 {xdr_void, xdr_void, FALSE}, 1352 1353 /* RFS_GETATTR = 1 */ 1354 {xdr_fhandle, xdr_nfslog_getattrres, FALSE}, 1355 1356 /* RFS_SETATTR = 2 */ 1357 {xdr_nfslog_setattrargs, xdr_nfsstat, TRUE}, 1358 1359 /* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */ 1360 {xdr_void, xdr_void, FALSE}, 1361 1362 /* RFS_LOOKUP = 4 */ 1363 {xdr_nfslog_diropargs, xdr_nfslog_diropres, TRUE}, 1364 1365 /* RFS_READLINK = 5 */ 1366 {xdr_fhandle, xdr_nfslog_rdlnres, FALSE}, 1367 1368 /* RFS_READ = 6 */ 1369 {xdr_nfslog_nfsreadargs, xdr_nfslog_rdresult, TRUE}, 1370 1371 /* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */ 1372 {xdr_void, xdr_void, FALSE}, 1373 1374 /* RFS_WRITE = 8 */ 1375 {xdr_nfslog_writeargs, xdr_nfslog_writeresult, TRUE}, 1376 1377 /* RFS_CREATE = 9 */ 1378 {xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE}, 1379 1380 /* RFS_REMOVE = 10 */ 1381 {xdr_nfslog_diropargs, xdr_nfsstat, TRUE}, 1382 1383 /* RFS_RENAME = 11 */ 1384 {xdr_nfslog_rnmargs, xdr_nfsstat, TRUE}, 1385 1386 /* RFS_LINK = 12 */ 1387 {xdr_nfslog_linkargs, xdr_nfsstat, TRUE}, 1388 1389 /* RFS_SYMLINK = 13 */ 1390 {xdr_nfslog_symlinkargs, xdr_nfsstat, TRUE}, 1391 1392 /* RFS_MKDIR = 14 */ 1393 {xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE}, 1394 1395 /* RFS_RMDIR = 15 */ 1396 {xdr_nfslog_diropargs, xdr_nfsstat, TRUE}, 1397 1398 /* RFS_READDIR = 16 */ 1399 {xdr_nfslog_rddirargs, xdr_nfslog_rddirres, TRUE}, 1400 1401 /* RFS_STATFS = 17 */ 1402 {xdr_fhandle, xdr_nfslog_statfs, FALSE}, 1403 }; 1404 1405 1406 /* 1407 * NFS VERSION 3 1408 */ 1409 1410 static struct nfslog_proc_disp nfslog_proc_v3[] = { 1411 1412 /* NFSPROC3_NULL = 0 */ 1413 {xdr_void, xdr_void, FALSE}, 1414 1415 /* NFSPROC3_GETATTR = 1 */ 1416 {xdr_nfslog_nfs_fh3, xdr_nfslog_GETATTR3res, FALSE}, 1417 1418 /* NFSPROC3_SETATTR = 2 */ 1419 {xdr_nfslog_SETATTR3args, xdr_nfslog_SETATTR3res, TRUE}, 1420 1421 /* NFSPROC3_LOOKUP = 3 */ 1422 {xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE}, 1423 1424 /* NFSPROC3_ACCESS = 4 */ 1425 {xdr_nfslog_ACCESS3args, xdr_nfslog_ACCESS3res, FALSE}, 1426 1427 /* NFSPROC3_READLINK = 5 */ 1428 {xdr_nfslog_nfs_fh3, xdr_nfslog_READLINK3res, FALSE}, 1429 1430 /* NFSPROC3_READ = 6 */ 1431 {xdr_nfslog_READ3args, xdr_nfslog_READ3res, TRUE}, 1432 1433 /* NFSPROC3_WRITE = 7 */ 1434 {xdr_nfslog_WRITE3args, xdr_nfslog_WRITE3res, TRUE}, 1435 1436 /* NFSPROC3_CREATE = 8 */ 1437 {xdr_nfslog_CREATE3args, xdr_nfslog_CREATE3res, TRUE}, 1438 1439 /* NFSPROC3_MKDIR = 9 */ 1440 {xdr_nfslog_MKDIR3args, xdr_nfslog_MKDIR3res, TRUE}, 1441 1442 /* NFSPROC3_SYMLINK = 10 */ 1443 {xdr_nfslog_SYMLINK3args, xdr_nfslog_SYMLINK3res, TRUE}, 1444 1445 /* NFSPROC3_MKNOD = 11 */ 1446 {xdr_nfslog_MKNOD3args, xdr_nfslog_MKNOD3res, TRUE}, 1447 1448 /* NFSPROC3_REMOVE = 12 */ 1449 {xdr_nfslog_REMOVE3args, xdr_nfslog_REMOVE3res, TRUE}, 1450 1451 /* NFSPROC3_RMDIR = 13 */ 1452 {xdr_nfslog_RMDIR3args, xdr_nfslog_RMDIR3res, TRUE}, 1453 1454 /* NFSPROC3_RENAME = 14 */ 1455 {xdr_nfslog_RENAME3args, xdr_nfslog_RENAME3res, TRUE}, 1456 1457 /* NFSPROC3_LINK = 15 */ 1458 {xdr_nfslog_LINK3args, xdr_nfslog_LINK3res, TRUE}, 1459 1460 /* NFSPROC3_READDIR = 16 */ 1461 {xdr_nfslog_READDIR3args, xdr_nfslog_READDIR3res, TRUE}, 1462 1463 /* NFSPROC3_READDIRPLUS = 17 */ 1464 {xdr_nfslog_READDIRPLUS3args, xdr_nfslog_READDIRPLUS3res, TRUE}, 1465 1466 /* NFSPROC3_FSSTAT = 18 */ 1467 {xdr_nfslog_FSSTAT3args, xdr_nfslog_FSSTAT3res, FALSE}, 1468 1469 /* NFSPROC3_FSINFO = 19 */ 1470 {xdr_nfslog_FSINFO3args, xdr_nfslog_FSINFO3res, FALSE}, 1471 1472 /* NFSPROC3_PATHCONF = 20 */ 1473 {xdr_nfslog_PATHCONF3args, xdr_nfslog_PATHCONF3res, FALSE}, 1474 1475 /* NFSPROC3_COMMIT = 21 */ 1476 {xdr_nfslog_COMMIT3args, xdr_nfslog_COMMIT3res, FALSE}, 1477 }; 1478 1479 static struct nfslog_proc_disp nfslog_proc_v1[] = { 1480 /* 1481 * NFSLOG VERSION 1 1482 */ 1483 1484 /* NFSLOG_NULL = 0 */ 1485 {xdr_void, xdr_void, TRUE}, 1486 1487 /* NFSLOG_SHARE = 1 */ 1488 {xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE}, 1489 1490 /* NFSLOG_UNSHARE = 2 */ 1491 {xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE}, 1492 1493 /* NFSLOG_LOOKUP = 3 */ 1494 {xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE}, 1495 1496 /* NFSLOG_GETFH = 4 */ 1497 {xdr_nfslog_getfhargs, xdr_nfsstat, TRUE}, 1498 }; 1499 1500 static struct nfslog_vers_disp nfslog_vers_disptable[] = { 1501 {sizeof (nfslog_proc_v2) / sizeof (nfslog_proc_v2[0]), 1502 nfslog_proc_v2}, 1503 {sizeof (nfslog_proc_v3) / sizeof (nfslog_proc_v3[0]), 1504 nfslog_proc_v3}, 1505 }; 1506 1507 static struct nfslog_vers_disp nfslog_nfslog_vers_disptable[] = { 1508 {sizeof (nfslog_proc_v1) / sizeof (nfslog_proc_v1[0]), 1509 nfslog_proc_v1}, 1510 }; 1511 1512 static struct nfslog_prog_disp nfslog_dispatch_table[] = { 1513 {NFS_PROGRAM, NFS_VERSMIN, 1514 (sizeof (nfslog_vers_disptable) / 1515 sizeof (nfslog_vers_disptable[0])), 1516 nfslog_vers_disptable}, 1517 1518 {NFSLOG_PROGRAM, NFSLOG_VERSMIN, 1519 (sizeof (nfslog_nfslog_vers_disptable) / 1520 sizeof (nfslog_nfslog_vers_disptable[0])), 1521 nfslog_nfslog_vers_disptable}, 1522 }; 1523 1524 static int nfslog_dispatch_table_arglen = sizeof (nfslog_dispatch_table) / 1525 sizeof (nfslog_dispatch_table[0]); 1526 1527 /* 1528 * This function will determine the appropriate export info struct to use 1529 * and allocate a record id to be used in the written log buffer. 1530 * Usually this is a straightforward operation but the existence of the 1531 * multicomponent lookup and its semantics of crossing file system 1532 * boundaries add to the complexity. See the comments below... 1533 */ 1534 struct exportinfo * 1535 nfslog_get_exi( 1536 nfs_export_t *ne, 1537 struct exportinfo *exi, 1538 struct svc_req *req, 1539 caddr_t res, 1540 unsigned int *nfslog_rec_id) 1541 { 1542 struct log_buffer *lb; 1543 struct exportinfo *exi_ret = NULL; 1544 fhandle_t *fh; 1545 nfs_fh3 *fh3; 1546 1547 if (exi == NULL) 1548 return (NULL); 1549 1550 /* 1551 * If the exi is marked for logging, allocate a record id and return 1552 */ 1553 if (exi->exi_export.ex_flags & EX_LOG) { 1554 lb = exi->exi_logbuffer; 1555 1556 /* obtain the unique record id for the caller */ 1557 *nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1); 1558 1559 /* 1560 * The caller will expect to be able to exi_rele() it, 1561 * so exi->exi_count must be incremented before it can 1562 * be returned, to make it uniform with exi_ret->exi_count 1563 */ 1564 exi_hold(exi); 1565 return (exi); 1566 } 1567 1568 if (exi != ne->exi_public) 1569 return (NULL); 1570 1571 /* 1572 * Here we have an exi that is not marked for logging. 1573 * It is possible that this request is a multicomponent lookup 1574 * that was done from the public file handle (not logged) and 1575 * the resulting file handle being returned to the client exists 1576 * in a file system that is being logged. If this is the case 1577 * we need to log this multicomponent lookup to the appropriate 1578 * log buffer. This will allow for the appropriate path name 1579 * mapping to occur at user level. 1580 */ 1581 if (req->rq_prog == NFS_PROGRAM) { 1582 switch (req->rq_vers) { 1583 case NFS_V3: 1584 if ((req->rq_proc == NFSPROC3_LOOKUP) && 1585 (((LOOKUP3res *)res)->status == NFS3_OK)) { 1586 fh3 = &((LOOKUP3res *)res)->res_u.ok.object; 1587 exi_ret = checkexport(&fh3->fh3_fsid, 1588 FH3TOXFIDP(fh3)); 1589 } 1590 break; 1591 1592 case NFS_VERSION: 1593 if ((req->rq_proc == RFS_LOOKUP) && 1594 (((struct nfsdiropres *) 1595 res)->dr_status == NFS_OK)) { 1596 fh = &((struct nfsdiropres *)res)-> 1597 dr_u.dr_drok_u.drok_fhandle; 1598 exi_ret = checkexport(&fh->fh_fsid, 1599 (fid_t *)&fh->fh_xlen); 1600 } 1601 break; 1602 default: 1603 break; 1604 } 1605 } 1606 1607 if (exi_ret != NULL && exi_ret->exi_export.ex_flags & EX_LOG) { 1608 lb = exi_ret->exi_logbuffer; 1609 /* obtain the unique record id for the caller */ 1610 *nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1); 1611 1612 return (exi_ret); 1613 } 1614 return (NULL); 1615 } 1616 1617 #ifdef DEBUG 1618 static long long rfslog_records_ignored = 0; 1619 #endif 1620 1621 /* 1622 * nfslog_write_record - Fill in the record buffer for writing out. 1623 * If logrecp is null, log it, otherwise, malloc the record and return it. 1624 * 1625 * It is the responsibility of the caller to check whether this exportinfo 1626 * has logging enabled. 1627 * Note that nfslog_share_public_record() only needs to check for the 1628 * existence of at least one logbuffer to which the public filehandle record 1629 * needs to be logged. 1630 */ 1631 void 1632 nfslog_write_record(struct exportinfo *exi, struct svc_req *req, 1633 caddr_t args, caddr_t res, cred_t *cr, struct netbuf *pnb, 1634 unsigned int record_id, unsigned int which_buffers) 1635 { 1636 struct nfslog_prog_disp *progtable; /* prog struct */ 1637 struct nfslog_vers_disp *verstable; /* version struct */ 1638 struct nfslog_proc_disp *disp = NULL; /* proc struct */ 1639 int i, vers; 1640 void *log_cookie; /* for logrecord if */ 1641 caddr_t buffer; 1642 XDR xdrs; 1643 unsigned int final_size; 1644 int encode_ok; 1645 int alloc_indx; 1646 1647 ASSERT(exi != NULL); ASSERT(req != NULL); ASSERT(args != NULL); 1648 ASSERT(res != NULL); ASSERT(cr != NULL); 1649 1650 /* 1651 * Find program element 1652 * Search the list since program can not be used as index 1653 */ 1654 for (i = 0; (i < nfslog_dispatch_table_arglen); i++) { 1655 if (req->rq_prog == nfslog_dispatch_table[i].nfslog_dis_prog) 1656 break; 1657 } 1658 if (i >= nfslog_dispatch_table_arglen) { /* program not logged */ 1659 /* not an error */ 1660 return; 1661 } 1662 1663 /* 1664 * Extract the dispatch functions based on program/version 1665 */ 1666 progtable = &nfslog_dispatch_table[i]; 1667 vers = req->rq_vers - progtable->nfslog_dis_versmin; 1668 verstable = &progtable->nfslog_dis_vers_table[vers]; 1669 disp = &verstable->nfslog_dis_proc_table[req->rq_proc]; 1670 1671 if (!(exi->exi_export.ex_flags & EX_LOG_ALLOPS) && 1672 !disp->affects_transactions) { 1673 /* 1674 * Only interested in logging operations affecting 1675 * transaction generation. This is not one of them. 1676 */ 1677 #ifdef DEBUG 1678 rfslog_records_ignored++; 1679 #endif 1680 return; 1681 } 1682 1683 switch (req->rq_prog) { 1684 case NFS_PROGRAM: 1685 switch (req->rq_vers) { 1686 case NFS_V3: 1687 switch (req->rq_proc) { 1688 case NFSPROC3_READDIRPLUS: 1689 alloc_indx = MEDIUM_INDX; 1690 break; 1691 default: 1692 alloc_indx = SMALL_INDX; 1693 break; 1694 } 1695 break; 1696 default: 1697 alloc_indx = SMALL_INDX; 1698 break; 1699 } 1700 break; 1701 case NFSLOG_PROGRAM: 1702 alloc_indx = MEDIUM_INDX; 1703 break; 1704 default: 1705 alloc_indx = SMALL_INDX; 1706 break; 1707 } 1708 1709 do { 1710 encode_ok = FALSE; 1711 1712 /* Pick the size to alloc; end of the road - return */ 1713 if (nfslog_mem_alloc[alloc_indx].size == (-1)) { 1714 cmn_err(CE_WARN, 1715 "NFSLOG: unable to encode record - prog=%d " 1716 "proc = %d", req->rq_prog, req->rq_proc); 1717 return; 1718 } 1719 1720 buffer = nfslog_record_alloc(exi, alloc_indx, &log_cookie, 0); 1721 if (buffer == NULL) { 1722 /* Error processing - no space alloced */ 1723 rfs_log_bad++; 1724 cmn_err(CE_WARN, "NFSLOG: can't get record"); 1725 return; 1726 } 1727 1728 xdrmem_create(&xdrs, buffer, 1729 nfslog_mem_alloc[alloc_indx].size, XDR_ENCODE); 1730 1731 /* 1732 * Encode the header, args and results of the record 1733 */ 1734 if (xdr_nfslog_request_record(&xdrs, exi, req, cr, pnb, 1735 nfslog_mem_alloc[alloc_indx].size, record_id) && 1736 (*disp->xdrargs)(&xdrs, args) && 1737 (*disp->xdrres)(&xdrs, res)) { 1738 encode_ok = TRUE; 1739 1740 rfs_log_good++; 1741 /* 1742 * Get the final size of the encoded 1743 * data and insert that length at the 1744 * beginning. 1745 */ 1746 final_size = xdr_getpos(&xdrs); 1747 xdr_setpos(&xdrs, 0); 1748 (void) xdr_u_int(&xdrs, &final_size); 1749 } else { 1750 /* Oops, the encode failed so we need to free memory */ 1751 nfslog_record_put(log_cookie, 0, FALSE, which_buffers); 1752 alloc_indx++; 1753 } 1754 1755 } while (encode_ok == FALSE); 1756 1757 1758 /* 1759 * Take the final log record and put it in the log file. 1760 * This may be queued to the file internally and written 1761 * later unless the last parameter is TRUE. 1762 * If the record_id is 0 then this is most likely a share/unshare 1763 * request and it should be written synchronously to the log file. 1764 */ 1765 nfslog_record_put(log_cookie, 1766 final_size, (record_id == 0), which_buffers); 1767 } 1768 1769 static char * 1770 get_publicfh_path(int *alloc_length) 1771 { 1772 char *pubpath; 1773 nfs_export_t *ne = nfs_get_export(); 1774 1775 rw_enter(&ne->exported_lock, RW_READER); 1776 1777 *alloc_length = ne->exi_public->exi_export.ex_pathlen + 1; 1778 pubpath = kmem_alloc(*alloc_length, KM_SLEEP); 1779 1780 (void) strcpy(pubpath, ne->exi_public->exi_export.ex_path); 1781 1782 rw_exit(&ne->exported_lock); 1783 1784 return (pubpath); 1785 } 1786 1787 static void 1788 log_public_record(struct exportinfo *exi, cred_t *cr) 1789 { 1790 struct svc_req req; 1791 struct netbuf nb = {0, 0, NULL}; 1792 int free_length = 0; 1793 diropargs3 args; 1794 LOOKUP3res res; 1795 1796 bzero(&req, sizeof (req)); 1797 req.rq_prog = NFSLOG_PROGRAM; 1798 req.rq_vers = NFSLOG_VERSION; 1799 req.rq_proc = NFSLOG_LOOKUP; 1800 req.rq_cred.oa_flavor = AUTH_NONE; 1801 1802 bzero(&args, sizeof (diropargs3)); 1803 bzero(&res, sizeof (LOOKUP3res)); 1804 1805 args.dir.fh3_length = 0; 1806 if ((args.name = get_publicfh_path(&free_length)) == NULL) 1807 return; 1808 args.dirp = &args.dir; 1809 1810 res.status = NFS3_OK; 1811 res.res_u.ok.object.fh3_length = 0; 1812 1813 /* 1814 * Calling this function with the exi_public 1815 * will have the effect of appending the record 1816 * to each of the open log buffers 1817 */ 1818 nfslog_write_record(exi, &req, 1819 (caddr_t)&args, (caddr_t)&res, cr, &nb, 0, NFSLOG_ALL_BUFFERS); 1820 1821 kmem_free(args.name, free_length); 1822 } 1823 1824 /* 1825 * nfslog_share_record - logs a share request. 1826 * This is not an NFS request, but we pretend here... 1827 */ 1828 void 1829 nfslog_share_record(struct exportinfo *exi, cred_t *cr) 1830 { 1831 struct svc_req req; 1832 int res = 0; 1833 struct netbuf nb = {0, 0, NULL}; 1834 1835 ASSERT(exi != NULL); 1836 1837 if (nfslog_buffer_list == NULL) 1838 return; 1839 1840 if (exi->exi_export.ex_flags & EX_LOG) { 1841 bzero(&req, sizeof (req)); 1842 req.rq_prog = NFSLOG_PROGRAM; 1843 req.rq_vers = NFSLOG_VERSION; 1844 req.rq_proc = NFSLOG_SHARE; 1845 req.rq_cred.oa_flavor = AUTH_NONE; 1846 nfslog_write_record(exi, &req, (caddr_t)exi, (caddr_t)&res, cr, 1847 &nb, 0, NFSLOG_ONE_BUFFER); 1848 } 1849 1850 log_public_record(exi, cr); 1851 } 1852 1853 /* 1854 * nfslog_unshare_record - logs an unshare request. 1855 * This is not an NFS request, but we pretend here... 1856 */ 1857 void 1858 nfslog_unshare_record(struct exportinfo *exi, cred_t *cr) 1859 { 1860 struct svc_req req; 1861 int res = 0; 1862 struct netbuf nb = {0, 0, NULL}; 1863 1864 ASSERT(exi != NULL); 1865 ASSERT(exi->exi_export.ex_flags & EX_LOG); 1866 1867 bzero(&req, sizeof (req)); 1868 req.rq_prog = NFSLOG_PROGRAM; 1869 req.rq_vers = NFSLOG_VERSION; 1870 req.rq_proc = NFSLOG_UNSHARE; 1871 req.rq_cred.oa_flavor = AUTH_NONE; 1872 nfslog_write_record(exi, &req, 1873 (caddr_t)exi, (caddr_t)&res, cr, &nb, 0, NFSLOG_ONE_BUFFER); 1874 } 1875 1876 1877 void 1878 nfslog_getfh(struct exportinfo *exi, fhandle *fh, char *fname, enum uio_seg seg, 1879 cred_t *cr) 1880 { 1881 struct svc_req req; 1882 int res = 0; 1883 struct netbuf nb = {0, 0, NULL}; 1884 int error = 0; 1885 char *namebuf; 1886 size_t len; 1887 nfslog_getfhargs gfh; 1888 1889 ASSERT(exi != NULL); 1890 ASSERT(exi->exi_export.ex_flags & EX_LOG); 1891 1892 bzero(&req, sizeof (req)); 1893 req.rq_prog = NFSLOG_PROGRAM; 1894 req.rq_vers = NFSLOG_VERSION; 1895 req.rq_proc = NFSLOG_GETFH; 1896 req.rq_cred.oa_flavor = AUTH_NONE; 1897 1898 namebuf = kmem_alloc(MAXPATHLEN + 4, KM_SLEEP); 1899 if (seg == UIO_USERSPACE) { 1900 error = copyinstr(fname, namebuf, MAXPATHLEN, &len); 1901 } else { 1902 error = copystr(fname, namebuf, MAXPATHLEN, &len); 1903 } 1904 1905 if (!error) { 1906 gfh.gfh_fh_buf = *fh; 1907 gfh.gfh_path = namebuf; 1908 1909 nfslog_write_record(exi, &req, (caddr_t)&gfh, (caddr_t)&res, 1910 cr, &nb, 0, NFSLOG_ONE_BUFFER); 1911 } 1912 kmem_free(namebuf, MAXPATHLEN + 4); 1913 } 1914