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
nfslog_init()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
nfslog_setup(struct exportinfo * exi)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
nfslog_disable(struct exportinfo * exi)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 *
log_buffer_create(caddr_t name)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
log_buffer_rele(struct log_buffer * lbp)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
log_file_create(caddr_t origname,struct log_file ** lfpp)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
log_file_rele(struct log_file * lfp)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 *
nfslog_record_alloc(struct exportinfo * exi,int alloc_indx,void ** cookie,int flags)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
nfslog_record_put(void * cookie,size_t size,bool_t sync,unsigned int which_buffers)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
nfslog_records_flush_to_disk(struct log_buffer * lbp)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
nfslog_records_flush_to_disk_nolock(struct log_buffer * lbp)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
nfslog_write_logrecords(struct log_file * lfp,struct lr_alloc * lrp_writers,int num_recs)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
nfslog_free_logrecords(struct lr_alloc * lrp_writers)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
nfslog_logbuffer_rename(struct log_buffer * lbp)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
nfslog_logfile_rename(char * from,char * new)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
nfslog_logfile_wait(struct log_file * lf)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
nfslog_record_append2all(struct lr_alloc * lrp)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
nfslog_dprint(const int level,const char * fmt,...)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
nfsl_flush(struct nfsl_flush_args * args,model_t model)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
nfslog_do_flush(struct flush_thread_params * tparams)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
create_buffer_header(caddr_t * loghdr,size_t * reclen,size_t * freesize)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 *
nfslog_get_exi(nfs_export_t * ne,struct exportinfo * exi,struct svc_req * req,caddr_t res,unsigned int * nfslog_rec_id)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
nfslog_write_record(struct exportinfo * exi,struct svc_req * req,caddr_t args,caddr_t res,cred_t * cr,struct netbuf * pnb,unsigned int record_id,unsigned int which_buffers)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 *
get_publicfh_path(int * alloc_length)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
log_public_record(struct exportinfo * exi,cred_t * cr)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
nfslog_share_record(struct exportinfo * exi,cred_t * cr)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
nfslog_unshare_record(struct exportinfo * exi,cred_t * cr)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
nfslog_getfh(struct exportinfo * exi,fhandle * fh,char * fname,enum uio_seg seg,cred_t * cr)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