+ * macros for detecting node changes
+ *
+ * These macros help us determine if a file has been changed on the server and
+ * thus whether or not we need to invalidate any cached data.
+ *
+ * For NFSv2/v3, the modification time is used.
+ * For NFSv4, the change attribute is used.
+ */
+#define NFS_CHANGED(VERS, NP, NVAP) \
+ (((VERS) >= NFS_VER4) ? \
+ ((NP)->n_change != (NVAP)->nva_change) : \
+ NFS_COMPARE_MTIME(&(NP)->n_mtime, (NVAP), !=))
+#define NFS_CHANGED_NC(VERS, NP, NVAP) \
+ (((VERS) >= NFS_VER4) ? \
+ ((NP)->n_ncchange != (NVAP)->nva_change) : \
+ NFS_COMPARE_MTIME(&(NP)->n_ncmtime, (NVAP), !=))
+#define NFS_CHANGED_UPDATE(VERS, NP, NVAP) \
+ do { \
+ if ((VERS) >= NFS_VER4) \
+ (NP)->n_change = (NVAP)->nva_change; \
+ else \
+ NFS_COPY_TIME(&(NP)->n_mtime, (NVAP), MODIFY); \
+ } while (0)
+#define NFS_CHANGED_UPDATE_NC(VERS, NP, NVAP) \
+ do { \
+ if ((VERS) >= NFS_VER4) \
+ (NP)->n_ncchange = (NVAP)->nva_change; \
+ else \
+ NFS_COPY_TIME(&(NP)->n_ncmtime, (NVAP), MODIFY); \
+ } while (0)
+
+
+extern lck_grp_t *nfs_open_grp;
+extern uint32_t nfs_open_owner_seqnum, nfs_lock_owner_seqnum;
+
+/*
+ * NFSv4 open owner structure - one per cred per mount
+ */
+struct nfs_open_owner {
+ TAILQ_ENTRY(nfs_open_owner) noo_link; /* List of open owners (on mount) */
+ lck_mtx_t noo_lock; /* owner mutex */
+ struct nfsmount * noo_mount; /* NFS mount */
+ uint32_t noo_refcnt; /* # outstanding references */
+ uint32_t noo_flags; /* see below */
+ kauth_cred_t noo_cred; /* credentials of open owner */
+ uint32_t noo_name; /* unique name used otw */
+ uint32_t noo_seqid; /* client-side sequence ID */
+ TAILQ_HEAD(,nfs_open_file) noo_opens; /* list of open files */
+};
+/* noo_flags */
+#define NFS_OPEN_OWNER_LINK 0x1 /* linked into mount's open owner list */
+#define NFS_OPEN_OWNER_BUSY 0x2 /* open state-modifying operation in progress */
+#define NFS_OPEN_OWNER_WANT 0x4 /* someone else wants to mark busy */
+
+/*
+ * NFS open file structure - one per open owner per nfsnode
+ */
+struct nfs_open_file {
+ lck_mtx_t nof_lock; /* open file mutex */
+ TAILQ_ENTRY(nfs_open_file) nof_link; /* list of open files */
+ TAILQ_ENTRY(nfs_open_file) nof_oolink; /* list of open owner's open files */
+ struct nfs_open_owner * nof_owner; /* open owner */
+ nfsnode_t nof_np; /* nfsnode this open is for */
+ nfs_stateid nof_stateid; /* open stateid */
+ thread_t nof_creator; /* thread that created file */
+ uint32_t nof_opencnt; /* open file count */
+ uint16_t nof_flags; /* see below */
+ uint8_t nof_access:4; /* access mode for this open */
+ uint8_t nof_deny:4; /* deny mode for this open */
+ uint8_t nof_mmap_access:4; /* mmap open access mode */
+ uint8_t nof_mmap_deny:4; /* mmap open deny mode */
+ /* counts of access/deny mode open combinations */
+ uint32_t nof_r; /* read opens (deny none) */
+ uint32_t nof_w; /* write opens (deny none) */
+ uint32_t nof_rw; /* read/write opens (deny none) */
+ uint32_t nof_r_dw; /* read deny-write opens */
+ /* the rest of the counts have a max of 2 (1 for open + 1 for mmap) */
+ uint32_t nof_w_dw:2; /* write deny-write opens (max 2) */
+ uint32_t nof_rw_dw:2; /* read/write deny-write opens (max 2) */
+ uint32_t nof_r_drw:2; /* read deny-read/write opens (max 2) */
+ uint32_t nof_w_drw:2; /* write deny-read/write opens (max 2) */
+ uint32_t nof_rw_drw:2; /* read/write deny-read/write opens (max 2) */
+ /* counts of DELEGATED access/deny mode open combinations */
+ uint32_t nof_d_w_dw:2; /* write deny-write opens (max 2) */
+ uint32_t nof_d_rw_dw:2; /* read/write deny-write opens (max 2) */
+ uint32_t nof_d_r_drw:2; /* read deny-read/write opens (max 2) */
+ uint32_t nof_d_w_drw:2; /* write deny-read/write opens (max 2) */
+ uint32_t nof_d_rw_drw:2; /* read/write deny-read/write opens (max 2) */
+ uint32_t nof_d_r; /* read opens (deny none) */
+ uint32_t nof_d_w; /* write opens (deny none) */
+ uint32_t nof_d_rw; /* read/write opens (deny none) */
+ uint32_t nof_d_r_dw; /* read deny-write opens */
+};
+/* nof_flags */
+#define NFS_OPEN_FILE_BUSY 0x0001 /* open state-modifying operation in progress */
+#define NFS_OPEN_FILE_WANT 0x0002 /* someone else wants to mark busy */
+#define NFS_OPEN_FILE_CREATE 0x0004 /* has an open(RW) from a "CREATE" call */
+#define NFS_OPEN_FILE_NEEDCLOSE 0x0008 /* has an open(R) from an (unopen) VNOP_READ or VNOP_MMAP call */
+#define NFS_OPEN_FILE_SETATTR 0x0020 /* has an open(W) to perform a SETATTR(size) */
+#define NFS_OPEN_FILE_POSIXLOCK 0x0040 /* server supports POSIX locking semantics */
+#define NFS_OPEN_FILE_LOST 0x0080 /* open state has been lost */
+#define NFS_OPEN_FILE_REOPEN 0x0100 /* file needs to be reopened */
+#define NFS_OPEN_FILE_REOPENING 0x0200 /* file is being reopened */
+
+struct nfs_lock_owner;
+/*
+ * NFS file lock
+ *
+ * Each lock request (pending or granted) has an
+ * nfs_file_lock structure representing its state.
+ */
+struct nfs_file_lock {
+ TAILQ_ENTRY(nfs_file_lock) nfl_link; /* List of locks on nfsnode */
+ TAILQ_ENTRY(nfs_file_lock) nfl_lolink; /* List of locks held by locker */
+ struct nfs_lock_owner * nfl_owner; /* lock owner that holds this lock */
+ uint64_t nfl_start; /* starting offset */
+ uint64_t nfl_end; /* ending offset (inclusive) */
+ uint32_t nfl_blockcnt; /* # locks blocked on this lock */
+ uint16_t nfl_flags; /* see below */
+ uint8_t nfl_type; /* lock type: read/write */
+};
+/* nfl_flags */
+#define NFS_FILE_LOCK_ALLOC 0x01 /* lock was allocated */
+#define NFS_FILE_LOCK_STYLE_POSIX 0x02 /* POSIX-style fcntl() lock */
+#define NFS_FILE_LOCK_STYLE_FLOCK 0x04 /* flock(2)-style lock */
+#define NFS_FILE_LOCK_STYLE_MASK 0x06 /* lock style mask */
+#define NFS_FILE_LOCK_WAIT 0x08 /* may block on conflicting locks */
+#define NFS_FILE_LOCK_BLOCKED 0x10 /* request is blocked */
+#define NFS_FILE_LOCK_DEAD 0x20 /* lock (request) no longer exists */
+#define NFS_FILE_LOCK_DELEGATED 0x40 /* lock acquired via delegation */
+
+TAILQ_HEAD(nfs_file_lock_queue, nfs_file_lock);
+
+/*
+ * Calculate length of lock range given the endpoints.
+ * Note that struct flock has "to EOF" reported as 0 but
+ * the NFSv4 protocol has "to EOF" reported as UINT64_MAX.
+ */
+#define NFS_FLOCK_LENGTH(S, E) (((E) == UINT64_MAX) ? 0 : ((E) - (S) + 1))
+#define NFS_LOCK_LENGTH(S, E) (((E) == UINT64_MAX) ? UINT64_MAX : ((E) - (S) + 1))
+
+/*
+ * NFSv4 lock owner structure - per open owner per process per nfsnode
+ *
+ * A lock owner is a process + an nfsnode.
+ *
+ * Note that flock(2) locks technically should have the lock owner be
+ * an fglob pointer instead of a process. However, implementing that
+ * correctly would not be trivial. So, for now, flock(2) locks are
+ * essentially treated like whole-file POSIX locks.
+ */
+struct nfs_lock_owner {
+ lck_mtx_t nlo_lock; /* owner mutex */
+ TAILQ_ENTRY(nfs_lock_owner) nlo_link; /* List of lock owners (on nfsnode) */
+ struct nfs_open_owner * nlo_open_owner; /* corresponding open owner */
+ struct nfs_file_lock_queue nlo_locks; /* list of locks held */
+ struct nfs_file_lock nlo_alock; /* most lockers will only ever have one */
+ struct timeval nlo_pid_start; /* Start time of process id */
+ pid_t nlo_pid; /* lock-owning process ID */
+ uint32_t nlo_refcnt; /* # outstanding references */
+ uint32_t nlo_flags; /* see below */
+ uint32_t nlo_name; /* unique name used otw */
+ uint32_t nlo_seqid; /* client-side sequence ID */
+ uint32_t nlo_stategenid; /* mount state generation ID */
+ nfs_stateid nlo_stateid; /* lock stateid */
+};
+/* nlo_flags */
+#define NFS_LOCK_OWNER_LINK 0x1 /* linked into mount's lock owner list */
+#define NFS_LOCK_OWNER_BUSY 0x2 /* lock state-modifying operation in progress */
+#define NFS_LOCK_OWNER_WANT 0x4 /* someone else wants to mark busy */
+
+/*
+ * The nfsnode is the NFS equivalent of an inode.
+ * There is a unique nfsnode for each NFS vnode.