1/*
2 * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29/*
30 * Copyright (c) 1989, 1993
31 *	The Regents of the University of California.  All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 *    must display the following acknowledgement:
46 *	This product includes software developed by the University of
47 *	California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 *    may be used to endorse or promote products derived from this software
50 *    without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 *	@(#)nfsnode.h	8.9 (Berkeley) 5/14/95
65 * FreeBSD-Id: nfsnode.h,v 1.24 1997/10/28 14:06:25 bde Exp $
66 */
67
68
69#ifndef _NFS_NFSNODE_H_
70#define _NFS_NFSNODE_H_
71
72#include <sys/appleapiopts.h>
73
74#ifdef __APPLE_API_PRIVATE
75#ifndef _NFS_NFS_H_
76#include <nfs/nfs.h>
77#endif
78#include <sys/kauth.h>
79
80/*
81 * Silly rename structure that hangs off the nfsnode until the name
82 * can be removed by nfs_vnop_inactive()
83 */
84struct nfs_sillyrename {
85	kauth_cred_t	nsr_cred;
86	struct nfsnode	*nsr_dnp;
87	int		nsr_namlen;
88	char		nsr_name[20];
89};
90
91/*
92 * The nfsbuf is the nfs equivalent to a struct buf.
93 */
94struct nfsbuf {
95	LIST_ENTRY(nfsbuf)	nb_hash;	/* hash chain */
96	LIST_ENTRY(nfsbuf)	nb_vnbufs;	/* nfsnode's nfsbuf chain */
97	TAILQ_ENTRY(nfsbuf)	nb_free;	/* free list position if not active. */
98	volatile uint32_t	nb_flags;	/* NB_* flags. */
99	volatile uint32_t	nb_lflags;	/* NBL_* flags. */
100	volatile uint32_t	nb_refs;	/* outstanding references. */
101	uint32_t		nb_bufsize;	/* buffer size */
102	daddr64_t		nb_lblkno;	/* logical block number. */
103	uint64_t		nb_verf;	/* V3 write verifier */
104	int			nb_commitlevel;	/* lowest write commit level */
105	time_t			nb_timestamp;	/* buffer timestamp */
106	int			nb_error;	/* errno value. */
107	u_int32_t		nb_valid;	/* valid pages in buf */
108	u_int32_t		nb_dirty;	/* dirty pages in buf */
109	int			nb_validoff;	/* offset in buffer of valid region. */
110	int			nb_validend;	/* offset of end of valid region. */
111	int			nb_dirtyoff;	/* offset in buffer of dirty region. */
112	int			nb_dirtyend;	/* offset of end of dirty region. */
113	int			nb_offio;	/* offset in buffer of I/O region. */
114	int			nb_endio;	/* offset of end of I/O region. */
115	int			nb_rpcs;	/* Count of RPCs remaining for this buffer. */
116	caddr_t			nb_data;	/* mapped buffer */
117	nfsnode_t		nb_np;		/* nfsnode buffer belongs to */
118	kauth_cred_t		nb_rcred;	/* read credentials reference */
119	kauth_cred_t		nb_wcred;	/* write credentials reference */
120	void *			nb_pagelist;	/* upl */
121};
122
123#define NFS_MAXBSIZE	(32 * PAGE_SIZE)	/* valid/dirty page masks limit buffer size */
124
125#define NFS_A_LOT_OF_NEEDCOMMITS	256			/* max# uncommitted buffers for a node */
126#define NFS_A_LOT_OF_DELAYED_WRITES	MAX(nfsbufcnt/8,512)	/* max# "delwri" buffers in system */
127
128/*
129 * These flags are kept in b_lflags...
130 * nfs_buf_mutex must be held before examining/updating
131 */
132#define	NBL_BUSY	0x00000001	/* I/O in progress. */
133#define	NBL_WANTED	0x00000002	/* Process wants this buffer. */
134
135/*
136 * These flags are kept in nb_flags and they're (purposefully)
137 * very similar to the B_* flags for struct buf.
138 * nfs_buf_mutex is not needed to examine/update these.
139 */
140#define	NB_STALEWVERF	0x00000001	/* write verifier changed on us */
141#define	NB_NEEDCOMMIT	0x00000002	/* buffer needs to be committed */
142#define	NB_ASYNC	0x00000004	/* Start I/O, do not wait. */
143#define	NB_CACHE	0x00000020	/* buffer data found in the cache */
144#define	NB_STABLE	0x00000040	/* write FILESYNC not UNSTABLE */
145#define	NB_DELWRI	0x00000080	/* delayed write: dirty range needs to be written */
146#define	NB_DONE		0x00000200	/* I/O completed. */
147#define	NB_EINTR	0x00000400	/* I/O was interrupted */
148#define	NB_ERROR	0x00000800	/* I/O error occurred. */
149#define	NB_INVAL	0x00002000	/* Does not contain valid info. */
150#define	NB_NCRDAHEAD	0x00004000	/* "nocache readahead" data */
151#define	NB_NOCACHE	0x00008000	/* Do not cache block after use. */
152#define	NB_WRITE	0x00000000	/* Write buffer (pseudo flag). */
153#define	NB_READ		0x00100000	/* Read buffer. */
154#define	NB_MULTASYNCRPC	0x00200000	/* multiple async RPCs issued for buffer */
155#define	NB_PAGELIST	0x00400000	/* Buffer describes pagelist I/O. */
156#define	NB_WRITEINPROG	0x01000000	/* Write in progress. */
157#define	NB_META		0x40000000	/* buffer contains meta-data. */
158
159/* Flags for operation type in nfs_buf_get() */
160#define	NBLK_READ	0x00000001	/* buffer for read */
161#define	NBLK_WRITE	0x00000002	/* buffer for write */
162#define	NBLK_META	0x00000004	/* buffer for metadata */
163#define	NBLK_OPMASK	0x00000007	/* operation mask */
164/* modifiers for above flags... */
165#define NBLK_NOWAIT	0x40000000	/* don't wait on busy buffer */
166#define NBLK_ONLYVALID	0x80000000	/* only return cached buffer */
167
168/* These flags are used for nfsbuf iterating */
169#define NBI_ITER		0x01	/* iteration in progress */
170#define NBI_ITERWANT		0x02	/* waiting to iterate */
171#define NBI_CLEAN		0x04	/* requesting clean buffers */
172#define NBI_DIRTY		0x08	/* requesting dirty buffers */
173#define NBI_NOWAIT		0x10	/* don't block on NBI_ITER */
174
175/* Flags for nfs_buf_acquire */
176#define NBAC_NOWAIT		0x01	/* Don't wait if buffer is busy */
177#define NBAC_REMOVE		0x02	/* Remove from free list once buffer is acquired */
178
179/* some convenience macros...  */
180#define NBOFF(BP)			((off_t)(BP)->nb_lblkno * (off_t)(BP)->nb_bufsize)
181#define NBPGVALID(BP,P)			(((BP)->nb_valid >> (P)) & 0x1)
182#define NBPGDIRTY(BP,P)			(((BP)->nb_dirty >> (P)) & 0x1)
183#define NBPGVALID_SET(BP,P)		((BP)->nb_valid |= (1 << (P)))
184#define NBPGDIRTY_SET(BP,P)		((BP)->nb_dirty |= (1 << (P)))
185
186#define NBUFSTAMPVALID(BP)		((BP)->nb_timestamp != ~0)
187#define NBUFSTAMPINVALIDATE(BP)		((BP)->nb_timestamp = ~0)
188
189#define NFS_BUF_MAP(BP) \
190	do { \
191		if (!(BP)->nb_data && nfs_buf_map(BP)) \
192			panic("nfs_buf_map failed"); \
193	} while (0)
194
195LIST_HEAD(nfsbuflists, nfsbuf);
196TAILQ_HEAD(nfsbuffreehead, nfsbuf);
197
198#define NFSNOLIST ((void*)0xdeadbeef)
199
200extern lck_mtx_t *nfs_buf_mutex;
201extern int nfsbufcnt, nfsbufmin, nfsbufmax, nfsbufmetacnt, nfsbufmetamax;
202extern int nfsbuffreecnt, nfsbuffreemetacnt, nfsbufdelwricnt, nfsneedbuffer;
203extern int nfs_nbdwrite;
204extern struct nfsbuffreehead nfsbuffree, nfsbufdelwri;
205
206#ifdef NFSBUFDEBUG
207#define NFSBUFCNTCHK() \
208	do { \
209	if (	(nfsbufcnt < 0) || \
210		(nfsbufcnt > nfsbufmax) || \
211		(nfsbufmetacnt < 0) || \
212		(nfsbufmetacnt > nfsbufmetamax) || \
213		(nfsbufmetacnt > nfsbufcnt) || \
214		(nfsbuffreecnt < 0) || \
215		(nfsbuffreecnt > nfsbufmax) || \
216		(nfsbuffreecnt > nfsbufcnt) || \
217		(nfsbuffreemetacnt < 0) || \
218		(nfsbuffreemetacnt > nfsbufmax) || \
219		(nfsbuffreemetacnt > nfsbufcnt) || \
220		(nfsbuffreemetacnt > nfsbufmetamax) || \
221		(nfsbuffreemetacnt > nfsbufmetacnt) || \
222		(nfsbufdelwricnt < 0) || \
223		(nfsbufdelwricnt > nfsbufmax) || \
224		(nfsbufdelwricnt > nfsbufcnt) || \
225		(nfs_nbdwrite < 0) || \
226		(nfs_nbdwrite > nfsbufcnt) || \
227		0) \
228		panic("nfsbuf count error: max %d meta %d cnt %d meta %d free %d meta %d delwr %d bdw %d\n", \
229			nfsbufmax, nfsbufmetamax, nfsbufcnt, nfsbufmetacnt, nfsbuffreecnt, nfsbuffreemetacnt, \
230			nfsbufdelwricnt, nfs_nbdwrite); \
231	} while (0)
232#else
233#define NFSBUFCNTCHK()
234#endif
235
236/*
237 * NFS directory buffer
238 *
239 * Each buffer for a directory consists of:
240 *
241 * - a small header
242 * - a packed list of direntry structures
243 *   (if RDIRPLUS is enabled, a file handle and attrstamp are
244 *   packed after the direntry name.)
245 * - free/unused space
246 * - if RDIRPLUS is enabled, an array of attributes
247 *   that is indexed backwards from the end of the buffer.
248 */
249struct nfs_dir_buf_header {
250	uint16_t	ndbh_flags;	/* flags (see below) */
251	uint16_t	ndbh_count;	/* # of entries */
252	uint32_t	ndbh_entry_end;	/* end offset of direntry data */
253	uint32_t	ndbh_ncgen;	/* name cache generation# */
254	uint32_t	ndbh_pad;	/* reserved */
255};
256/* ndbh_flags */
257#define NDB_FULL	0x0001	/* buffer has been filled */
258#define NDB_EOF		0x0002	/* buffer contains EOF */
259#define NDB_PLUS	0x0004	/* buffer contains RDIRPLUS data */
260
261#define NFS_DIR_BUF_FIRST_DIRENTRY(BP) \
262	((struct direntry*)((char*)((BP)->nb_data) + sizeof(*ndbhp)))
263#define NFS_DIR_BUF_NVATTR(BP, IDX) \
264	(&((struct nfs_vattr*)((char*)((BP)->nb_data) + (BP)->nb_bufsize))[-((IDX)+1)])
265#define NFS_DIRENTRY_LEN(namlen) \
266	((sizeof(struct direntry) + (namlen) - (MAXPATHLEN-1) + 7) & ~7)
267#define NFS_DIRENT_LEN(namlen) \
268	((sizeof(struct dirent) - (NAME_MAX+1)) + (((namlen) + 1 + 3) &~ 3))
269#define NFS_DIRENTRY_NEXT(DP) \
270	((struct direntry*)((char*)(DP) + (DP)->d_reclen))
271#define NFS_DIR_COOKIE_POTENTIALLY_TRUNCATED(C) \
272	((C) && ((((C) >> 32) == 0) || (((C) & 0x80000000ULL) && (((C) >> 32) == 0xffffffff))))
273#define NFS_DIR_COOKIE_SAME32(C1, C2) \
274	(((C1) & 0xffffffffULL) == ((C2) & 0xffffffffULL))
275
276/*
277 * NFS directory cookie cache
278 *
279 * This structure is used to cache cookie-to-buffer mappings for
280 * cookies recently returned from READDIR.  The entries are kept in an
281 * array.  The most-recently-used (MRU) list is headed by the entry at
282 * index "mru".  The index of the next entry in the list is kept in the
283 * "next" array.  (An index value of -1 marks an invalid entry.)
284 */
285#define NFSNUMCOOKIES		14
286struct nfsdmap {
287	int8_t		free;			/* next unused slot */
288	int8_t		mru;			/* head of MRU list */
289	int8_t		next[NFSNUMCOOKIES];	/* MRU list links */
290	struct {
291	    uint64_t	key;			/* cookie */
292	    uint64_t	lbn;			/* lbn of buffer */
293	} cookies[NFSNUMCOOKIES];		/* MRU list entries */
294};
295
296/*
297 * NFS vnode attribute structure
298 */
299#define NFSTIME_ACCESS	0	/* time of last access */
300#define NFSTIME_MODIFY	1	/* time of last modification */
301#define NFSTIME_CHANGE	2	/* time file changed */
302#define NFSTIME_CREATE	3	/* time file created */
303#define NFSTIME_BACKUP	4	/* time of last backup */
304#define NFSTIME_COUNT	5
305
306#define NFS_COMPARE_MTIME(TVP, NVAP, CMP) \
307	(((TVP)->tv_sec == (NVAP)->nva_timesec[NFSTIME_MODIFY]) ?	\
308	 ((TVP)->tv_nsec CMP (NVAP)->nva_timensec[NFSTIME_MODIFY]) :	\
309	 ((TVP)->tv_sec CMP (NVAP)->nva_timesec[NFSTIME_MODIFY]))
310#define NFS_COPY_TIME(TVP, NVAP, WHICH) \
311	do { \
312	(TVP)->tv_sec = (NVAP)->nva_timesec[NFSTIME_##WHICH]; \
313	(TVP)->tv_nsec = (NVAP)->nva_timensec[NFSTIME_##WHICH]; \
314	} while (0)
315
316struct nfs_vattr {
317	enum vtype	nva_type;	/* vnode type (for create) */
318	uint32_t	nva_mode;	/* file's access mode (and type) */
319	uid_t		nva_uid;	/* owner user id */
320	gid_t		nva_gid;	/* owner group id */
321	guid_t		nva_uuuid;	/* owner user UUID */
322	guid_t		nva_guuid;	/* owner group UUID */
323	kauth_acl_t	nva_acl;	/* access control list */
324	nfs_specdata	nva_rawdev;	/* device the special file represents */
325	uint32_t	nva_flags;	/* file flags (see below) */
326	uint32_t	nva_maxlink;	/* maximum # of links (v4) */
327	uint64_t	nva_nlink;	/* number of references to file */
328	uint64_t	nva_fileid;	/* file id */
329	nfs_fsid	nva_fsid;	/* file system id */
330	uint64_t	nva_size;	/* file size in bytes */
331	uint64_t	nva_bytes;	/* bytes of disk space held by file */
332	uint64_t	nva_change;	/* change attribute */
333	int64_t		nva_timesec[NFSTIME_COUNT];
334	int32_t		nva_timensec[NFSTIME_COUNT];
335	uint32_t 	nva_bitmap[NFS_ATTR_BITMAP_LEN]; /* attributes that are valid */
336};
337
338/* nva_flags */
339#define NFS_FFLAG_ARCHIVED		0x0001
340#define NFS_FFLAG_HIDDEN		0x0002
341#define NFS_FFLAG_HAS_NAMED_ATTRS	0x0004	/* file has named attributes */
342#define NFS_FFLAG_TRIGGER		0x0008	/* node is a trigger/mirror mount point */
343#define NFS_FFLAG_TRIGGER_REFERRAL	0x0010	/* trigger is a referral */
344#define NFS_FFLAG_IS_ATTR		0x8000	/* file is a named attribute file/directory */
345
346/* flags for nfs_getattr() */
347#define NGA_CACHED	0x0001	/* use cached attributes (if still valid) */
348#define NGA_UNCACHED	0x0002	/* fetch new attributes */
349#define NGA_ACL		0x0004	/* fetch ACL */
350#define NGA_MONITOR	0x0008	/* vnode monitor attr update poll */
351
352/* macros for initting/cleaning up nfs_vattr structures */
353#define	NVATTR_INIT(NVAP) \
354	do { \
355		NFS_CLEAR_ATTRIBUTES((NVAP)->nva_bitmap); \
356		(NVAP)->nva_flags = 0; \
357		(NVAP)->nva_acl = NULL; \
358	} while (0)
359#define	NVATTR_CLEANUP(NVAP) \
360	do { \
361		NFS_CLEAR_ATTRIBUTES((NVAP)->nva_bitmap); \
362		if ((NVAP)->nva_acl) { \
363			kauth_acl_free((NVAP)->nva_acl); \
364			(NVAP)->nva_acl = NULL; \
365		} \
366	} while (0)
367
368/*
369 * macros for detecting node changes
370 *
371 * These macros help us determine if a file has been changed on the server and
372 * thus whether or not we need to invalidate any cached data.
373 *
374 * For NFSv2/v3, the modification time is used.
375 * For NFSv4, the change attribute is used.
376 */
377#define NFS_CHANGED(VERS, NP, NVAP) \
378		(((VERS) >= NFS_VER4) ? \
379			((NP)->n_change != (NVAP)->nva_change) : \
380			NFS_COMPARE_MTIME(&(NP)->n_mtime, (NVAP), !=))
381#define NFS_CHANGED_NC(VERS, NP, NVAP) \
382		(((VERS) >= NFS_VER4) ? \
383			((NP)->n_ncchange != (NVAP)->nva_change) : \
384			NFS_COMPARE_MTIME(&(NP)->n_ncmtime, (NVAP), !=))
385#define NFS_CHANGED_UPDATE(VERS, NP, NVAP) \
386	do { \
387		if ((VERS) >= NFS_VER4) \
388			(NP)->n_change = (NVAP)->nva_change; \
389		else \
390			NFS_COPY_TIME(&(NP)->n_mtime, (NVAP), MODIFY); \
391	} while (0)
392#define NFS_CHANGED_UPDATE_NC(VERS, NP, NVAP) \
393	do { \
394		if ((VERS) >= NFS_VER4) \
395			(NP)->n_ncchange = (NVAP)->nva_change; \
396		else \
397			NFS_COPY_TIME(&(NP)->n_ncmtime, (NVAP), MODIFY); \
398	} while (0)
399
400
401extern lck_grp_t *nfs_open_grp;
402extern uint32_t nfs_open_owner_seqnum, nfs_lock_owner_seqnum;
403
404/*
405 * NFSv4 open owner structure - one per cred per mount
406 */
407struct nfs_open_owner {
408	TAILQ_ENTRY(nfs_open_owner)	noo_link;	/* List of open owners (on mount) */
409	lck_mtx_t			noo_lock;	/* owner mutex */
410	struct nfsmount *		noo_mount;	/* NFS mount */
411	uint32_t			noo_refcnt;	/* # outstanding references */
412	uint32_t			noo_flags;	/* see below */
413	kauth_cred_t			noo_cred;	/* credentials of open owner */
414	uint32_t			noo_name;	/* unique name used otw */
415	uint32_t			noo_seqid;	/* client-side sequence ID */
416	TAILQ_HEAD(,nfs_open_file)	noo_opens;	/* list of open files */
417};
418/* noo_flags */
419#define NFS_OPEN_OWNER_LINK	0x1	/* linked into mount's open owner list */
420#define NFS_OPEN_OWNER_BUSY	0x2	/* open state-modifying operation in progress */
421#define NFS_OPEN_OWNER_WANT	0x4	/* someone else wants to mark busy */
422
423/*
424 * NFS open file structure - one per open owner per nfsnode
425 */
426struct nfs_open_file {
427	lck_mtx_t			nof_lock;		/* open file mutex */
428	TAILQ_ENTRY(nfs_open_file)	nof_link;		/* list of open files */
429	TAILQ_ENTRY(nfs_open_file)	nof_oolink;		/* list of open owner's open files */
430	struct nfs_open_owner *		nof_owner;		/* open owner */
431	nfsnode_t			nof_np;			/* nfsnode this open is for */
432	nfs_stateid			nof_stateid;		/* open stateid */
433	thread_t			nof_creator;		/* thread that created file */
434	uint32_t			nof_opencnt;		/* open file count */
435	uint16_t			nof_flags;		/* see below */
436	uint8_t				nof_access:4;		/* access mode for this open */
437	uint8_t				nof_deny:4;		/* deny mode for this open */
438	uint8_t				nof_mmap_access:4;	/* mmap open access mode */
439	uint8_t				nof_mmap_deny:4;	/* mmap open deny mode */
440	/* counts of access/deny mode open combinations */
441	uint32_t			nof_r;			/* read opens (deny none) */
442	uint32_t			nof_w;			/* write opens (deny none) */
443	uint32_t			nof_rw;			/* read/write opens (deny none) */
444	uint32_t			nof_r_dw;		/* read deny-write opens */
445	/* the rest of the counts have a max of 2 (1 for open + 1 for mmap) */
446	uint32_t			nof_w_dw:2;		/* write deny-write opens (max 2) */
447	uint32_t			nof_rw_dw:2;		/* read/write deny-write opens (max 2) */
448	uint32_t			nof_r_drw:2;		/* read deny-read/write opens (max 2) */
449	uint32_t			nof_w_drw:2;		/* write deny-read/write opens (max 2) */
450	uint32_t			nof_rw_drw:2;		/* read/write deny-read/write opens (max 2) */
451	/* counts of DELEGATED access/deny mode open combinations */
452	uint32_t			nof_d_w_dw:2;		/* write deny-write opens (max 2) */
453	uint32_t			nof_d_rw_dw:2;		/* read/write deny-write opens (max 2) */
454	uint32_t			nof_d_r_drw:2;		/* read deny-read/write opens (max 2) */
455	uint32_t			nof_d_w_drw:2;		/* write deny-read/write opens (max 2) */
456	uint32_t			nof_d_rw_drw:2;		/* read/write deny-read/write opens (max 2) */
457	uint32_t			nof_d_r;		/* read opens (deny none) */
458	uint32_t			nof_d_w;		/* write opens (deny none) */
459	uint32_t			nof_d_rw;		/* read/write opens (deny none) */
460	uint32_t			nof_d_r_dw;		/* read deny-write opens */
461};
462/* nof_flags */
463#define NFS_OPEN_FILE_BUSY	0x0001	/* open state-modifying operation in progress */
464#define NFS_OPEN_FILE_WANT	0x0002	/* someone else wants to mark busy */
465#define NFS_OPEN_FILE_CREATE	0x0004	/* has an open(RW) from a "CREATE" call */
466#define NFS_OPEN_FILE_NEEDCLOSE	0x0008	/* has an open(R) from an (unopen) VNOP_READ or VNOP_MMAP call */
467#define NFS_OPEN_FILE_SETATTR	0x0020	/* has an open(W) to perform a SETATTR(size) */
468#define NFS_OPEN_FILE_POSIXLOCK	0x0040	/* server supports POSIX locking semantics */
469#define NFS_OPEN_FILE_LOST	0x0080	/* open state has been lost */
470#define NFS_OPEN_FILE_REOPEN	0x0100	/* file needs to be reopened */
471#define NFS_OPEN_FILE_REOPENING	0x0200	/* file is being reopened */
472
473struct nfs_lock_owner;
474/*
475 * NFS file lock
476 *
477 * Each lock request (pending or granted) has an
478 * nfs_file_lock structure representing its state.
479 */
480struct nfs_file_lock {
481	TAILQ_ENTRY(nfs_file_lock)	nfl_link;	/* List of locks on nfsnode */
482	TAILQ_ENTRY(nfs_file_lock)	nfl_lolink;	/* List of locks held by locker */
483	struct nfs_lock_owner *		nfl_owner;	/* lock owner that holds this lock */
484	uint64_t			nfl_start;	/* starting offset */
485	uint64_t			nfl_end;	/* ending offset (inclusive) */
486	uint32_t			nfl_blockcnt;	/* # locks blocked on this lock */
487	uint16_t			nfl_flags;	/* see below */
488	uint8_t				nfl_type;	/* lock type: read/write */
489};
490/* nfl_flags */
491#define NFS_FILE_LOCK_ALLOC		0x01	/* lock was allocated */
492#define NFS_FILE_LOCK_STYLE_POSIX	0x02	/* POSIX-style fcntl() lock */
493#define NFS_FILE_LOCK_STYLE_FLOCK	0x04	/* flock(2)-style lock */
494#define NFS_FILE_LOCK_STYLE_MASK	0x06	/* lock style mask */
495#define NFS_FILE_LOCK_WAIT		0x08	/* may block on conflicting locks */
496#define NFS_FILE_LOCK_BLOCKED		0x10	/* request is blocked */
497#define NFS_FILE_LOCK_DEAD		0x20	/* lock (request) no longer exists */
498#define NFS_FILE_LOCK_DELEGATED		0x40	/* lock acquired via delegation */
499
500TAILQ_HEAD(nfs_file_lock_queue, nfs_file_lock);
501
502/*
503 * Calculate length of lock range given the endpoints.
504 * Note that struct flock has "to EOF" reported as 0 but
505 * the NFSv4 protocol has "to EOF" reported as UINT64_MAX.
506 */
507#define NFS_FLOCK_LENGTH(S, E)	(((E) == UINT64_MAX) ? 0 : ((E) - (S) + 1))
508#define NFS_LOCK_LENGTH(S, E)	(((E) == UINT64_MAX) ? UINT64_MAX : ((E) - (S) + 1))
509
510/*
511 * NFSv4 lock owner structure - per open owner per process per nfsnode
512 *
513 * A lock owner is a process + an nfsnode.
514 *
515 * Note that flock(2) locks technically should have the lock owner be
516 * an fglob pointer instead of a process.  However, implementing that
517 * correctly would not be trivial.  So, for now, flock(2) locks are
518 * essentially treated like whole-file POSIX locks.
519 */
520struct nfs_lock_owner {
521	lck_mtx_t			nlo_lock;	/* owner mutex */
522	TAILQ_ENTRY(nfs_lock_owner)	nlo_link;	/* List of lock owners (on nfsnode) */
523	struct nfs_open_owner *		nlo_open_owner;	/* corresponding open owner */
524	struct nfs_file_lock_queue	nlo_locks;	/* list of locks held */
525	struct nfs_file_lock		nlo_alock;	/* most lockers will only ever have one */
526	struct timeval			nlo_pid_start;	/* Start time of process id */
527	pid_t				nlo_pid;	/* lock-owning process ID */
528	uint32_t			nlo_refcnt;	/* # outstanding references */
529	uint32_t			nlo_flags;	/* see below */
530	uint32_t			nlo_name;	/* unique name used otw */
531	uint32_t			nlo_seqid;	/* client-side sequence ID */
532	uint32_t			nlo_stategenid;	/* mount state generation ID */
533	nfs_stateid			nlo_stateid;	/* lock stateid */
534};
535/* nlo_flags */
536#define NFS_LOCK_OWNER_LINK	0x1	/* linked into mount's lock owner list */
537#define NFS_LOCK_OWNER_BUSY	0x2	/* lock state-modifying operation in progress */
538#define NFS_LOCK_OWNER_WANT	0x4	/* someone else wants to mark busy */
539
540/*
541 * The nfsnode is the NFS equivalent of an inode.
542 * There is a unique nfsnode for each NFS vnode.
543 * An nfsnode is 'named' by its file handle. (nget/nfs_node.c)
544 * NB: Hopefully the current order of the fields is such that everything will
545 *     be well aligned and, therefore, tightly packed.
546 */
547
548#define NFS_ACCESS_CACHE_SIZE	3
549
550struct nfsnode {
551	lck_mtx_t		n_lock;		/* nfs node lock */
552	lck_rw_t		n_datalock;	/* nfs node data lock */
553	void			*n_datalockowner;/* nfs node data lock owner (exclusive) */
554	LIST_ENTRY(nfsnode)	n_hash;		/* Hash chain */
555	LIST_ENTRY(nfsnode)	n_monlink;	/* list of monitored nodes */
556	u_quad_t		n_size;		/* Current size of file */
557	u_quad_t		n_newsize;	/* new size of file (pending update) */
558	u_int64_t		n_xid;		/* last xid to loadattr */
559	struct nfs_vattr	n_vattr;	/* Vnode attribute cache */
560	time_t			n_attrstamp;	/* Attr. cache timestamp */
561	time_t			n_aclstamp;	/* ACL cache timestamp */
562	time_t			n_evtstamp;	/* last vnode event timestamp */
563	uint32_t		n_events;	/* pending vnode events */
564	u_int8_t		n_access[NFS_ACCESS_CACHE_SIZE+1];	/* ACCESS cache */
565	uid_t                   n_accessuid[NFS_ACCESS_CACHE_SIZE];	/* credentials having access */
566	time_t                  n_accessstamp[NFS_ACCESS_CACHE_SIZE];	/* access cache timestamp */
567	union {
568	    struct {
569		struct timespec	n3_mtime;	/* Prev modify time. */
570		struct timespec	n3_ncmtime;	/* namecache modify time. */
571	    } v3;
572	    struct {
573		uint64_t	n4_change;	/* prev change attribute */
574		uint64_t	n4_ncchange;	/* namecache change attribute */
575		u_char		*n4_attrdirfh;	/* associated attr directory fh */
576		struct timeval	n4_lastio;	/* time of most recent I/O on attr */
577	    } v4;
578	} n_un4;
579	vnode_t			n_parent;	/* this node's parent */
580	u_char			*n_fhp;		/* NFS File Handle */
581	vnode_t			n_vnode;	/* associated vnode */
582	mount_t			n_mount;	/* associated mount (NHINIT) */
583	int			n_error;	/* Save write error value */
584	union {
585		struct timespec	ns_atim;	/* Special file times */
586		daddr64_t	nf_lastread;	/* last block# read from (for readahead) */
587		uint64_t	nd_cookieverf;	/* Cookie verifier (dir only) */
588	} n_un1;
589	union {
590		struct timespec	ns_mtim;	/* Special file times */
591		daddr64_t	nf_lastrahead;	/* last block# read ahead */
592		uint64_t	nd_eofcookie;	/* Dir. EOF cookie cache */
593	} n_un2;
594	union {
595		struct nfs_sillyrename *nf_silly;/* Ptr to silly rename struct */
596		struct nfsdmap *nd_cookiecache; /* dir cookie cache */
597	} n_un3;
598	uint32_t		n_flag;		/* node flags */
599	u_short			n_fhsize;	/* size in bytes, of fh */
600	u_short			n_hflag;	/* node hash flags */
601	u_short			n_bflag;	/* node buffer flags */
602	u_short			n_mflag;	/* node mount flags */
603	u_char			n_fh[NFS_SMALLFH];/* Small File Handle */
604	uint32_t		n_auth;		/* security flavor used for this node */
605	struct nfsbuflists	n_cleanblkhd;	/* clean blocklist head */
606	struct nfsbuflists	n_dirtyblkhd;	/* dirty blocklist head */
607	union {
608		int		nf_wrbusy;	/* # threads in write/fsync */
609		uint32_t	nd_ncgen;	/* dir name cache generation# */
610	} n_un5;
611	union {
612		int		nf_needcommitcnt;/* # bufs that need committing */
613		daddr64_t	nd_lastdbl;	/* last dir buf lookup block# */
614	} n_un6;
615	int			n_bufiterflags;	/* buf iterator flags */
616	union {
617		int		nf_numoutput;	/* write I/Os in progress */
618		int		nd_trigseq;	/* vnode trigger seq# */
619	} n_un7;
620	/* open state */
621	lck_mtx_t		n_openlock;	/* nfs node open lock */
622	uint32_t		n_openflags;	/* open state flags */
623	uint32_t		n_openrefcnt;	/* # non-file opens */
624	TAILQ_HEAD(,nfs_open_file) n_opens;	/* list of open files */
625	/* lock state */
626	TAILQ_HEAD(, nfs_lock_owner) n_lock_owners; /* list of lock owners */
627	struct nfs_file_lock_queue n_locks;	/* list of locks */
628	/* delegation state */
629	nfs_stateid		n_dstateid;	/* delegation stateid */
630	TAILQ_ENTRY(nfsnode)	n_dlink;	/* delegation list link */
631	TAILQ_ENTRY(nfsnode)	n_dreturn;	/* delegation return list link */
632	struct kauth_ace	n_dace;		/* delegation ACE */
633};
634
635#define NFS_DATA_LOCK_SHARED	1
636#define NFS_DATA_LOCK_EXCLUSIVE	2
637
638#define nfstimespeccmp(tvp, uvp, cmp)		\
639	(((tvp)->tv_sec == (uvp)->tv_sec) ?	\
640	 ((tvp)->tv_nsec cmp (uvp)->tv_nsec) :	\
641	 ((tvp)->tv_sec cmp (uvp)->tv_sec))
642
643#define CHECK_NEEDCOMMITCNT(np) \
644	do { \
645		if ((np)->n_needcommitcnt < 0) { \
646			printf("nfs: n_needcommitcnt negative\n"); \
647			(np)->n_needcommitcnt = 0; \
648		} \
649	} while (0)
650
651#define n_atim			n_un1.ns_atim
652#define n_mtim			n_un2.ns_mtim
653#define n_lastread		n_un1.nf_lastread
654#define n_lastrahead		n_un2.nf_lastrahead
655#define n_sillyrename		n_un3.nf_silly
656#define n_wrbusy		n_un5.nf_wrbusy
657#define n_needcommitcnt		n_un6.nf_needcommitcnt
658#define n_numoutput		n_un7.nf_numoutput
659#define n_cookieverf		n_un1.nd_cookieverf
660#define n_eofcookie		n_un2.nd_eofcookie
661#define n_cookiecache		n_un3.nd_cookiecache
662#define n_ncgen			n_un5.nd_ncgen
663#define n_lastdbl		n_un6.nd_lastdbl
664#define n_trigseq		n_un7.nd_trigseq
665#define n_mtime			n_un4.v3.n3_mtime
666#define n_ncmtime		n_un4.v3.n3_ncmtime
667#define n_change		n_un4.v4.n4_change
668#define n_ncchange		n_un4.v4.n4_ncchange
669#define n_attrdirfh		n_un4.v4.n4_attrdirfh
670#define n_lastio		n_un4.v4.n4_lastio
671
672/*
673 * Flags for n_flag
674 */
675#define	NUPDATESIZE	0x00001	/* size of file needs updating */
676#define	NREVOKE		0x00002	/* node revoked */
677#define	NMODIFIED	0x00004	/* Might have a modified buffer in bio */
678#define	NWRITEERR	0x00008	/* Flag write errors so close will know */
679#define	NNEEDINVALIDATE	0x00010	/* need to call vinvalbuf() */
680#define	NGETATTRINPROG	0x00020	/* GETATTR RPC in progress */
681#define	NGETATTRWANT	0x00040	/* waiting for GETATTR RPC */
682#define	NACC		0x00100	/* Special file accessed */
683#define	NUPD		0x00200	/* Special file updated */
684#define	NCHG		0x00400	/* Special file times changed */
685#define	NNEGNCENTRIES	0x00800	/* directory has negative name cache entries */
686#define	NBUSY		0x01000	/* node is busy */
687#define	NBUSYWANT	0x02000	/* waiting on busy node */
688#define NISDOTZFS	0x04000	/* a ".zfs" directory */
689#define NISDOTZFSCHILD	0x08000	/* a child of a ".zfs" directory */
690#define NISMAPPED	0x10000	/* node is mmapped   */
691
692/*
693 * Flags for n_hflag
694 * Note: protected by nfs_node_hash_mutex
695 */
696#define NHHASHED	0x0001  /* node is in hash table */
697#define NHINIT		0x0002  /* node is being initialized */
698#define NHLOCKED	0x0004  /* node is locked (initting or deleting) */
699#define NHLOCKWANT	0x0008  /* someone wants to lock */
700
701/*
702 * Flags for n_bflag
703 * Note: protected by nfs_buf_mutex
704 */
705#define	NBFLUSHINPROG	0x0001	/* Avoid multiple calls to nfs_flush() */
706#define	NBFLUSHWANT	0x0002	/* waiting for nfs_flush() to complete */
707#define	NBINVALINPROG	0x0004	/* Avoid multiple calls to nfs_vinvalbuf() */
708#define	NBINVALWANT	0x0008	/* waiting for nfs_vinvalbuf() to complete */
709
710/*
711 * Flags for n_mflag
712 * Note: protected by nfsmount's nm_lock
713 */
714#define	NMMONSCANINPROG	0x0001	/* monitored node is currently updating attributes */
715#define	NMMONSCANWANT	0x0002	/* waiting for attribute update to complete */
716
717/*
718 * n_openflags
719 * Note: protected by n_openlock
720 */
721#define N_OPENBUSY		0x0001	/* open state is busy - being updated */
722#define N_OPENWANT		0x0002	/* someone wants to mark busy */
723#define N_DELEG_READ		0x0004	/* we have a read delegation */
724#define N_DELEG_WRITE		0x0008	/* we have a write delegation */
725#define N_DELEG_MASK		0x000c	/* delegation mask */
726#define N_DELEG_RETURN		0x0010	/* delegation queued for return */
727#define N_DELEG_RETURNING	0x0020	/* delegation being returned */
728
729/* attr/access/ACL cache timestamp macros */
730#define NATTRVALID(np)		((np)->n_attrstamp != ~0)
731#define NATTRINVALIDATE(np)	((np)->n_attrstamp = ~0)
732#define NACCESSVALID(np, slot)	(((slot) >= 0) && ((slot) < NFS_ACCESS_CACHE_SIZE) && ((np)->n_accessstamp[(slot)] != ~0))
733#define NACCESSINVALIDATE(np) \
734	do { \
735		int __i; \
736		for (__i=0; __i < NFS_ACCESS_CACHE_SIZE; __i++) \
737			(np)->n_accessstamp[__i] = ~0; \
738		(np)->n_access[NFS_ACCESS_CACHE_SIZE] = 0; \
739	} while (0)
740#define NACLVALID(np)		((np)->n_aclstamp != ~0)
741#define NACLINVALIDATE(np)	((np)->n_aclstamp = ~0)
742
743/*
744 * NFS-specific flags for nfs_vinvalbuf/nfs_flush
745 */
746#define V_IGNORE_WRITEERR	0x8000
747
748/*
749 * Flags for nfs_nget()
750 */
751#define	NG_MARKROOT	0x0001	/* mark vnode as root of FS */
752#define	NG_MAKEENTRY	0x0002	/* add name cache entry for vnode */
753#define	NG_NOCREATE	0x0004	/* don't create a new node, return existing one */
754
755/*
756 * Convert between nfsnode pointers and vnode pointers
757 */
758#define VTONFS(vp)	((nfsnode_t)vnode_fsnode(vp))
759#define NFSTOV(np)	((np)->n_vnode)
760
761/* nfsnode hash table mutex */
762extern lck_mtx_t *nfs_node_hash_mutex;
763
764/*
765 * printf-like helper macro that also outputs node name.
766 */
767#define NP(NP, FMT, ...) \
768	do { \
769		const char *__vname = (NP) ? vnode_getname(NFSTOV(NP)) : NULL; \
770		printf(FMT " %s\n", ##__VA_ARGS__, __vname ? __vname : "???"); \
771		if (__vname) vnode_putname(__vname); \
772	} while (0)
773
774/*
775 * nfsiod structures
776 */
777struct nfsiod {
778	TAILQ_ENTRY(nfsiod)	niod_link;	/* List of nfsiods */
779	struct nfsmount *	niod_nmp;	/* mount point for this nfsiod */
780};
781TAILQ_HEAD(nfsiodlist, nfsiod);
782TAILQ_HEAD(nfsiodmountlist, nfsmount);
783extern struct nfsiodlist nfsiodfree, nfsiodwork;
784extern struct nfsiodmountlist nfsiodmounts;
785extern lck_mtx_t *nfsiod_mutex;
786
787#if defined(KERNEL)
788
789typedef int     vnop_t(void *);
790extern	vnop_t	**fifo_nfsv2nodeop_p;
791extern	vnop_t	**nfsv2_vnodeop_p;
792extern	vnop_t	**spec_nfsv2nodeop_p;
793extern	vnop_t	**fifo_nfsv4nodeop_p;
794extern	vnop_t	**nfsv4_vnodeop_p;
795extern	vnop_t	**spec_nfsv4nodeop_p;
796
797/*
798 * Prototypes for NFS vnode operations
799 */
800#define nfs_vnop_revoke nop_revoke
801int	nfs_vnop_inactive(struct vnop_inactive_args *);
802int	nfs_vnop_reclaim(struct vnop_reclaim_args *);
803
804int nfs_node_lock(nfsnode_t);
805int nfs_node_lock_internal(nfsnode_t, int);
806void nfs_node_lock_force(nfsnode_t);
807void nfs_node_unlock(nfsnode_t);
808int nfs_node_lock2(nfsnode_t, nfsnode_t);
809void nfs_node_unlock2(nfsnode_t, nfsnode_t);
810int nfs_node_set_busy(nfsnode_t, thread_t);
811int nfs_node_set_busy2(nfsnode_t, nfsnode_t, thread_t);
812int nfs_node_set_busy4(nfsnode_t, nfsnode_t, nfsnode_t, nfsnode_t, thread_t);
813void nfs_node_clear_busy(nfsnode_t);
814void nfs_node_clear_busy2(nfsnode_t, nfsnode_t);
815void nfs_node_clear_busy4(nfsnode_t, nfsnode_t, nfsnode_t, nfsnode_t);
816void nfs_data_lock(nfsnode_t, int);
817void nfs_data_lock_noupdate(nfsnode_t, int);
818void nfs_data_lock_internal(nfsnode_t, int, int);
819void nfs_data_unlock(nfsnode_t);
820void nfs_data_unlock_noupdate(nfsnode_t);
821void nfs_data_unlock_internal(nfsnode_t, int);
822void nfs_data_update_size(nfsnode_t, int);
823
824/* other stuff */
825int nfs_removeit(struct nfs_sillyrename *);
826int nfs_nget(mount_t,nfsnode_t,struct componentname *,u_char *,int,struct nfs_vattr *,u_int64_t *,uint32_t,int,nfsnode_t*);
827int nfs_mount_is_dirty(mount_t);
828void nfs_dir_cookie_cache(nfsnode_t, uint64_t, uint64_t);
829int nfs_dir_cookie_to_lbn(nfsnode_t, uint64_t, int *, uint64_t *);
830void nfs_invaldir(nfsnode_t);
831uint32_t nfs_dir_buf_freespace(struct nfsbuf *, int);
832
833/* nfsbuf functions */
834void nfs_nbinit(void);
835void nfs_buf_timer(void *, void *);
836void nfs_buf_remfree(struct nfsbuf *);
837boolean_t nfs_buf_is_incore(nfsnode_t, daddr64_t);
838struct nfsbuf * nfs_buf_incore(nfsnode_t, daddr64_t);
839int nfs_buf_get(nfsnode_t, daddr64_t, uint32_t, thread_t, int, struct nfsbuf **);
840int nfs_buf_upl_setup(struct nfsbuf *bp);
841void nfs_buf_upl_check(struct nfsbuf *bp);
842void nfs_buf_normalize_valid_range(nfsnode_t, struct nfsbuf *);
843int nfs_buf_map(struct nfsbuf *);
844void nfs_buf_release(struct nfsbuf *, int);
845int nfs_buf_iowait(struct nfsbuf *);
846void nfs_buf_iodone(struct nfsbuf *);
847void nfs_buf_write_delayed(struct nfsbuf *);
848void nfs_buf_check_write_verifier(nfsnode_t, struct nfsbuf *);
849void nfs_buf_freeup(int);
850void nfs_buf_refget(struct nfsbuf *bp);
851void nfs_buf_refrele(struct nfsbuf *bp);
852void nfs_buf_drop(struct nfsbuf *);
853errno_t nfs_buf_acquire(struct nfsbuf *, int, int, int);
854int nfs_buf_iterprepare(nfsnode_t, struct nfsbuflists *, int);
855void nfs_buf_itercomplete(nfsnode_t, struct nfsbuflists *, int);
856
857int nfs_bioread(nfsnode_t, uio_t, int, vfs_context_t);
858int nfs_buf_readahead(nfsnode_t, int, daddr64_t *, daddr64_t, thread_t, kauth_cred_t);
859int nfs_buf_readdir(struct nfsbuf *, vfs_context_t);
860int nfs_buf_read(struct nfsbuf *);
861void nfs_buf_read_finish(struct nfsbuf *);
862int nfs_buf_read_rpc(struct nfsbuf *, thread_t, kauth_cred_t);
863void nfs_buf_read_rpc_finish(struct nfsreq *);
864int nfs_buf_write(struct nfsbuf *);
865void nfs_buf_write_finish(struct nfsbuf *, thread_t, kauth_cred_t);
866int nfs_buf_write_rpc(struct nfsbuf *, int, thread_t, kauth_cred_t);
867void nfs_buf_write_rpc_finish(struct nfsreq *);
868int nfs_buf_write_dirty_pages(struct nfsbuf *, thread_t, kauth_cred_t);
869
870int nfs_flushcommits(nfsnode_t, int);
871int nfs_flush(nfsnode_t, int, thread_t, int);
872void nfs_buf_delwri_push(int);
873void nfs_buf_delwri_service(void);
874void nfs_buf_delwri_thread(void *, wait_result_t);;
875
876int nfsiod_start(void);
877void nfsiod_terminate(struct nfsiod *);
878void nfsiod_thread(void);
879int nfsiod_continue(int);
880void nfs_asyncio_finish(struct nfsreq *);
881void nfs_asyncio_resend(struct nfsreq *);
882int nfs_async_write_start(struct nfsmount *);
883void nfs_async_write_done(struct nfsmount *);
884
885#endif /* KERNEL */
886
887#endif /* __APPLE_API_PRIVATE */
888#endif /* _NFS_NFSNODE_H_ */
889