]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs4_vnops.c
xnu-2782.30.5.tar.gz
[apple/xnu.git] / bsd / nfs / nfs4_vnops.c
CommitLineData
2d21ac55 1/*
6d2010ae 2 * Copyright (c) 2006-2011 Apple Inc. All rights reserved.
2d21ac55
A
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
29/*
30 * vnode op calls for NFS version 4
31 */
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/systm.h>
35#include <sys/resourcevar.h>
36#include <sys/proc_internal.h>
37#include <sys/kauth.h>
38#include <sys/mount_internal.h>
39#include <sys/malloc.h>
40#include <sys/kpi_mbuf.h>
41#include <sys/conf.h>
42#include <sys/vnode_internal.h>
43#include <sys/dirent.h>
44#include <sys/fcntl.h>
45#include <sys/lockf.h>
46#include <sys/ubc_internal.h>
47#include <sys/attr.h>
48#include <sys/signalvar.h>
6d2010ae
A
49#include <sys/uio_internal.h>
50#include <sys/xattr.h>
51#include <sys/paths.h>
2d21ac55
A
52
53#include <vfs/vfs_support.h>
54
55#include <sys/vm.h>
56
57#include <sys/time.h>
58#include <kern/clock.h>
59#include <libkern/OSAtomic.h>
60
61#include <miscfs/fifofs/fifo.h>
62#include <miscfs/specfs/specdev.h>
63
64#include <nfs/rpcv2.h>
65#include <nfs/nfsproto.h>
66#include <nfs/nfs.h>
67#include <nfs/nfsnode.h>
68#include <nfs/nfs_gss.h>
69#include <nfs/nfsmount.h>
70#include <nfs/nfs_lock.h>
71#include <nfs/xdr_subs.h>
72#include <nfs/nfsm_subs.h>
73
74#include <net/if.h>
75#include <netinet/in.h>
76#include <netinet/in_var.h>
77#include <vm/vm_kern.h>
78
79#include <kern/task.h>
80#include <kern/sched_prim.h>
81
2d21ac55 82int
fe8ab488 83nfs4_access_rpc(nfsnode_t np, u_int32_t *access, int rpcflags, vfs_context_t ctx)
2d21ac55 84{
b0d623f7 85 int error = 0, lockerror = ENOENT, status, numops, slot;
2d21ac55
A
86 u_int64_t xid;
87 struct nfsm_chain nmreq, nmrep;
88 struct timeval now;
6d2010ae 89 uint32_t access_result = 0, supported = 0, missing;
2d21ac55
A
90 struct nfsmount *nmp = NFSTONMP(np);
91 int nfsvers = nmp->nm_vers;
92 uid_t uid;
6d2010ae 93 struct nfsreq_secinfo_args si;
2d21ac55 94
6d2010ae
A
95 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
96 return (0);
97
98 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
2d21ac55
A
99 nfsm_chain_null(&nmreq);
100 nfsm_chain_null(&nmrep);
101
b0d623f7
A
102 // PUTFH, ACCESS, GETATTR
103 numops = 3;
2d21ac55
A
104 nfsm_chain_build_alloc_init(error, &nmreq, 17 * NFSX_UNSIGNED);
105 nfsm_chain_add_compound_header(error, &nmreq, "access", numops);
106 numops--;
107 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
108 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
109 numops--;
110 nfsm_chain_add_32(error, &nmreq, NFS_OP_ACCESS);
6d2010ae 111 nfsm_chain_add_32(error, &nmreq, *access);
2d21ac55
A
112 numops--;
113 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 114 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
2d21ac55
A
115 nfsm_chain_build_done(error, &nmreq);
116 nfsm_assert(error, (numops == 0), EPROTO);
117 nfsmout_if(error);
fe8ab488
A
118 error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND,
119 vfs_context_thread(ctx), vfs_context_ucred(ctx),
120 &si, rpcflags, &nmrep, &xid, &status);
2d21ac55 121
b0d623f7
A
122 if ((lockerror = nfs_node_lock(np)))
123 error = lockerror;
2d21ac55
A
124 nfsm_chain_skip_tag(error, &nmrep);
125 nfsm_chain_get_32(error, &nmrep, numops);
126 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
127 nfsm_chain_op_check(error, &nmrep, NFS_OP_ACCESS);
128 nfsm_chain_get_32(error, &nmrep, supported);
6d2010ae 129 nfsm_chain_get_32(error, &nmrep, access_result);
2d21ac55 130 nfsmout_if(error);
6d2010ae 131 if ((missing = (*access & ~supported))) {
2d21ac55
A
132 /* missing support for something(s) we wanted */
133 if (missing & NFS_ACCESS_DELETE) {
134 /*
135 * If the server doesn't report DELETE (possible
136 * on UNIX systems), we'll assume that it is OK
137 * and just let any subsequent delete action fail
138 * if it really isn't deletable.
139 */
6d2010ae 140 access_result |= NFS_ACCESS_DELETE;
2d21ac55
A
141 }
142 }
6d2010ae
A
143 /* ".zfs" subdirectories may erroneously give a denied answer for modify/delete */
144 if (nfs_access_dotzfs) {
145 vnode_t dvp = NULLVP;
146 if (np->n_flag & NISDOTZFSCHILD) /* may be able to create/delete snapshot dirs */
147 access_result |= (NFS_ACCESS_MODIFY|NFS_ACCESS_EXTEND|NFS_ACCESS_DELETE);
148 else if (((dvp = vnode_getparent(NFSTOV(np))) != NULLVP) && (VTONFS(dvp)->n_flag & NISDOTZFSCHILD))
149 access_result |= NFS_ACCESS_DELETE; /* may be able to delete snapshot dirs */
150 if (dvp != NULLVP)
151 vnode_put(dvp);
152 }
b0d623f7 153 /* Some servers report DELETE support but erroneously give a denied answer. */
6d2010ae
A
154 if (nfs_access_delete && (*access & NFS_ACCESS_DELETE) && !(access_result & NFS_ACCESS_DELETE))
155 access_result |= NFS_ACCESS_DELETE;
2d21ac55 156 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 157 nfsm_chain_loadattr(error, &nmrep, np, nfsvers, &xid);
2d21ac55
A
158 nfsmout_if(error);
159
160 uid = kauth_cred_getuid(vfs_context_ucred(ctx));
6d2010ae
A
161 slot = nfs_node_access_slot(np, uid, 1);
162 np->n_accessuid[slot] = uid;
2d21ac55 163 microuptime(&now);
6d2010ae
A
164 np->n_accessstamp[slot] = now.tv_sec;
165 np->n_access[slot] = access_result;
2d21ac55 166
6d2010ae
A
167 /* pass back the access returned with this request */
168 *access = np->n_access[slot];
2d21ac55 169nfsmout:
b0d623f7
A
170 if (!lockerror)
171 nfs_node_unlock(np);
2d21ac55
A
172 nfsm_chain_cleanup(&nmreq);
173 nfsm_chain_cleanup(&nmrep);
174 return (error);
175}
176
177int
178nfs4_getattr_rpc(
179 nfsnode_t np,
180 mount_t mp,
181 u_char *fhp,
182 size_t fhsize,
6d2010ae 183 int flags,
2d21ac55
A
184 vfs_context_t ctx,
185 struct nfs_vattr *nvap,
186 u_int64_t *xidp)
187{
188 struct nfsmount *nmp = mp ? VFSTONFS(mp) : NFSTONMP(np);
6d2010ae
A
189 int error = 0, status, nfsvers, numops, rpcflags = 0, acls;
190 uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
2d21ac55 191 struct nfsm_chain nmreq, nmrep;
6d2010ae 192 struct nfsreq_secinfo_args si;
2d21ac55 193
fe8ab488 194 if (nfs_mount_gone(nmp))
2d21ac55
A
195 return (ENXIO);
196 nfsvers = nmp->nm_vers;
6d2010ae
A
197 acls = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_ACL);
198
199 if (np && (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)) {
200 nfs4_default_attrs_for_referral_trigger(VTONFS(np->n_parent), NULL, 0, nvap, NULL);
201 return (0);
202 }
203
204 if (flags & NGA_MONITOR) /* vnode monitor requests should be soft */
205 rpcflags = R_RECOVER;
2d21ac55 206
fe8ab488
A
207 if (flags & NGA_SOFT) /* Return ETIMEDOUT if server not responding */
208 rpcflags |= R_SOFT;
209
6d2010ae 210 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
2d21ac55
A
211 nfsm_chain_null(&nmreq);
212 nfsm_chain_null(&nmrep);
213
b0d623f7
A
214 // PUTFH, GETATTR
215 numops = 2;
2d21ac55
A
216 nfsm_chain_build_alloc_init(error, &nmreq, 15 * NFSX_UNSIGNED);
217 nfsm_chain_add_compound_header(error, &nmreq, "getattr", numops);
218 numops--;
219 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
220 nfsm_chain_add_fh(error, &nmreq, nfsvers, fhp, fhsize);
221 numops--;
222 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae
A
223 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
224 if ((flags & NGA_ACL) && acls)
225 NFS_BITMAP_SET(bitmap, NFS_FATTR_ACL);
226 nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, np);
2d21ac55
A
227 nfsm_chain_build_done(error, &nmreq);
228 nfsm_assert(error, (numops == 0), EPROTO);
229 nfsmout_if(error);
6d2010ae
A
230 error = nfs_request2(np, mp, &nmreq, NFSPROC4_COMPOUND,
231 vfs_context_thread(ctx), vfs_context_ucred(ctx),
232 NULL, rpcflags, &nmrep, xidp, &status);
2d21ac55
A
233
234 nfsm_chain_skip_tag(error, &nmrep);
235 nfsm_chain_get_32(error, &nmrep, numops);
236 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
237 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
238 nfsmout_if(error);
6d2010ae
A
239 error = nfs4_parsefattr(&nmrep, NULL, nvap, NULL, NULL, NULL);
240 nfsmout_if(error);
241 if ((flags & NGA_ACL) && acls && !NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_ACL)) {
242 /* we asked for the ACL but didn't get one... assume there isn't one */
243 NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_ACL);
244 nvap->nva_acl = NULL;
245 }
2d21ac55
A
246nfsmout:
247 nfsm_chain_cleanup(&nmreq);
248 nfsm_chain_cleanup(&nmrep);
249 return (error);
250}
251
252int
253nfs4_readlink_rpc(nfsnode_t np, char *buf, uint32_t *buflenp, vfs_context_t ctx)
254{
255 struct nfsmount *nmp;
256 int error = 0, lockerror = ENOENT, status, numops;
257 uint32_t len = 0;
258 u_int64_t xid;
259 struct nfsm_chain nmreq, nmrep;
6d2010ae 260 struct nfsreq_secinfo_args si;
2d21ac55
A
261
262 nmp = NFSTONMP(np);
fe8ab488 263 if (nfs_mount_gone(nmp))
2d21ac55 264 return (ENXIO);
6d2010ae
A
265 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
266 return (EINVAL);
267 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
2d21ac55
A
268 nfsm_chain_null(&nmreq);
269 nfsm_chain_null(&nmrep);
270
b0d623f7
A
271 // PUTFH, GETATTR, READLINK
272 numops = 3;
2d21ac55
A
273 nfsm_chain_build_alloc_init(error, &nmreq, 16 * NFSX_UNSIGNED);
274 nfsm_chain_add_compound_header(error, &nmreq, "readlink", numops);
275 numops--;
276 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
277 nfsm_chain_add_fh(error, &nmreq, NFS_VER4, np->n_fhp, np->n_fhsize);
278 numops--;
279 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 280 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
2d21ac55
A
281 numops--;
282 nfsm_chain_add_32(error, &nmreq, NFS_OP_READLINK);
283 nfsm_chain_build_done(error, &nmreq);
284 nfsm_assert(error, (numops == 0), EPROTO);
285 nfsmout_if(error);
6d2010ae 286 error = nfs_request(np, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &si, &nmrep, &xid, &status);
2d21ac55 287
b0d623f7 288 if ((lockerror = nfs_node_lock(np)))
2d21ac55
A
289 error = lockerror;
290 nfsm_chain_skip_tag(error, &nmrep);
291 nfsm_chain_get_32(error, &nmrep, numops);
292 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
293 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 294 nfsm_chain_loadattr(error, &nmrep, np, NFS_VER4, &xid);
2d21ac55
A
295 nfsm_chain_op_check(error, &nmrep, NFS_OP_READLINK);
296 nfsm_chain_get_32(error, &nmrep, len);
297 nfsmout_if(error);
298 if (len >= *buflenp) {
299 if (np->n_size && (np->n_size < *buflenp))
300 len = np->n_size;
301 else
302 len = *buflenp - 1;
303 }
304 nfsm_chain_get_opaque(error, &nmrep, len, buf);
305 if (!error)
306 *buflenp = len;
307nfsmout:
308 if (!lockerror)
b0d623f7 309 nfs_node_unlock(np);
2d21ac55
A
310 nfsm_chain_cleanup(&nmreq);
311 nfsm_chain_cleanup(&nmrep);
312 return (error);
313}
314
315int
316nfs4_read_rpc_async(
317 nfsnode_t np,
318 off_t offset,
319 size_t len,
320 thread_t thd,
321 kauth_cred_t cred,
322 struct nfsreq_cbinfo *cb,
323 struct nfsreq **reqp)
324{
325 struct nfsmount *nmp;
326 int error = 0, nfsvers, numops;
b0d623f7 327 nfs_stateid stateid;
2d21ac55 328 struct nfsm_chain nmreq;
6d2010ae 329 struct nfsreq_secinfo_args si;
2d21ac55
A
330
331 nmp = NFSTONMP(np);
fe8ab488 332 if (nfs_mount_gone(nmp))
2d21ac55
A
333 return (ENXIO);
334 nfsvers = nmp->nm_vers;
6d2010ae
A
335 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
336 return (EINVAL);
2d21ac55 337
6d2010ae 338 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
2d21ac55
A
339 nfsm_chain_null(&nmreq);
340
b0d623f7 341 // PUTFH, READ, GETATTR
2d21ac55
A
342 numops = 3;
343 nfsm_chain_build_alloc_init(error, &nmreq, 22 * NFSX_UNSIGNED);
344 nfsm_chain_add_compound_header(error, &nmreq, "read", numops);
345 numops--;
346 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
347 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
348 numops--;
349 nfsm_chain_add_32(error, &nmreq, NFS_OP_READ);
b0d623f7
A
350 nfs_get_stateid(np, thd, cred, &stateid);
351 nfsm_chain_add_stateid(error, &nmreq, &stateid);
2d21ac55
A
352 nfsm_chain_add_64(error, &nmreq, offset);
353 nfsm_chain_add_32(error, &nmreq, len);
354 numops--;
355 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 356 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
2d21ac55
A
357 nfsm_chain_build_done(error, &nmreq);
358 nfsm_assert(error, (numops == 0), EPROTO);
359 nfsmout_if(error);
6d2010ae 360 error = nfs_request_async(np, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, 0, cb, reqp);
2d21ac55
A
361nfsmout:
362 nfsm_chain_cleanup(&nmreq);
363 return (error);
364}
365
366int
367nfs4_read_rpc_async_finish(
368 nfsnode_t np,
369 struct nfsreq *req,
b0d623f7 370 uio_t uio,
2d21ac55
A
371 size_t *lenp,
372 int *eofp)
373{
374 struct nfsmount *nmp;
375 int error = 0, lockerror, nfsvers, numops, status, eof = 0;
376 size_t retlen = 0;
377 u_int64_t xid;
378 struct nfsm_chain nmrep;
379
380 nmp = NFSTONMP(np);
fe8ab488 381 if (nfs_mount_gone(nmp)) {
2d21ac55
A
382 nfs_request_async_cancel(req);
383 return (ENXIO);
384 }
385 nfsvers = nmp->nm_vers;
386
387 nfsm_chain_null(&nmrep);
388
389 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
390 if (error == EINPROGRESS) /* async request restarted */
391 return (error);
392
b0d623f7 393 if ((lockerror = nfs_node_lock(np)))
2d21ac55
A
394 error = lockerror;
395 nfsm_chain_skip_tag(error, &nmrep);
396 nfsm_chain_get_32(error, &nmrep, numops);
397 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
398 nfsm_chain_op_check(error, &nmrep, NFS_OP_READ);
399 nfsm_chain_get_32(error, &nmrep, eof);
400 nfsm_chain_get_32(error, &nmrep, retlen);
401 if (!error) {
402 *lenp = MIN(retlen, *lenp);
b0d623f7 403 error = nfsm_chain_get_uio(&nmrep, *lenp, uio);
2d21ac55
A
404 }
405 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 406 nfsm_chain_loadattr(error, &nmrep, np, nfsvers, &xid);
2d21ac55 407 if (!lockerror)
b0d623f7 408 nfs_node_unlock(np);
2d21ac55
A
409 if (eofp) {
410 if (!eof && !retlen)
411 eof = 1;
412 *eofp = eof;
413 }
414 nfsm_chain_cleanup(&nmrep);
6d2010ae
A
415 if (np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR)
416 microuptime(&np->n_lastio);
2d21ac55
A
417 return (error);
418}
419
420int
421nfs4_write_rpc_async(
422 nfsnode_t np,
b0d623f7 423 uio_t uio,
2d21ac55
A
424 size_t len,
425 thread_t thd,
426 kauth_cred_t cred,
427 int iomode,
428 struct nfsreq_cbinfo *cb,
429 struct nfsreq **reqp)
430{
431 struct nfsmount *nmp;
6d2010ae 432 mount_t mp;
2d21ac55 433 int error = 0, nfsvers, numops;
b0d623f7 434 nfs_stateid stateid;
2d21ac55 435 struct nfsm_chain nmreq;
6d2010ae 436 struct nfsreq_secinfo_args si;
2d21ac55
A
437
438 nmp = NFSTONMP(np);
fe8ab488 439 if (nfs_mount_gone(nmp))
2d21ac55
A
440 return (ENXIO);
441 nfsvers = nmp->nm_vers;
6d2010ae
A
442 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
443 return (EINVAL);
444
445 /* for async mounts, don't bother sending sync write requests */
446 if ((iomode != NFS_WRITE_UNSTABLE) && nfs_allow_async &&
447 ((mp = NFSTOMP(np))) && (vfs_flags(mp) & MNT_ASYNC))
448 iomode = NFS_WRITE_UNSTABLE;
2d21ac55 449
6d2010ae 450 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
2d21ac55
A
451 nfsm_chain_null(&nmreq);
452
b0d623f7 453 // PUTFH, WRITE, GETATTR
2d21ac55
A
454 numops = 3;
455 nfsm_chain_build_alloc_init(error, &nmreq, 25 * NFSX_UNSIGNED + len);
456 nfsm_chain_add_compound_header(error, &nmreq, "write", numops);
457 numops--;
458 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
459 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
460 numops--;
461 nfsm_chain_add_32(error, &nmreq, NFS_OP_WRITE);
b0d623f7
A
462 nfs_get_stateid(np, thd, cred, &stateid);
463 nfsm_chain_add_stateid(error, &nmreq, &stateid);
464 nfsm_chain_add_64(error, &nmreq, uio_offset(uio));
2d21ac55
A
465 nfsm_chain_add_32(error, &nmreq, iomode);
466 nfsm_chain_add_32(error, &nmreq, len);
467 if (!error)
b0d623f7 468 error = nfsm_chain_add_uio(&nmreq, uio, len);
2d21ac55
A
469 numops--;
470 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 471 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
2d21ac55
A
472 nfsm_chain_build_done(error, &nmreq);
473 nfsm_assert(error, (numops == 0), EPROTO);
474 nfsmout_if(error);
475
6d2010ae 476 error = nfs_request_async(np, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, 0, cb, reqp);
2d21ac55
A
477nfsmout:
478 nfsm_chain_cleanup(&nmreq);
479 return (error);
480}
481
482int
483nfs4_write_rpc_async_finish(
484 nfsnode_t np,
485 struct nfsreq *req,
486 int *iomodep,
487 size_t *rlenp,
488 uint64_t *wverfp)
489{
490 struct nfsmount *nmp;
491 int error = 0, lockerror = ENOENT, nfsvers, numops, status;
492 int committed = NFS_WRITE_FILESYNC;
493 size_t rlen = 0;
494 u_int64_t xid, wverf;
495 mount_t mp;
496 struct nfsm_chain nmrep;
497
498 nmp = NFSTONMP(np);
fe8ab488 499 if (nfs_mount_gone(nmp)) {
2d21ac55
A
500 nfs_request_async_cancel(req);
501 return (ENXIO);
502 }
503 nfsvers = nmp->nm_vers;
504
505 nfsm_chain_null(&nmrep);
506
507 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
508 if (error == EINPROGRESS) /* async request restarted */
509 return (error);
510 nmp = NFSTONMP(np);
fe8ab488 511 if (nfs_mount_gone(nmp))
2d21ac55 512 error = ENXIO;
b0d623f7 513 if (!error && (lockerror = nfs_node_lock(np)))
2d21ac55
A
514 error = lockerror;
515 nfsm_chain_skip_tag(error, &nmrep);
516 nfsm_chain_get_32(error, &nmrep, numops);
517 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
518 nfsm_chain_op_check(error, &nmrep, NFS_OP_WRITE);
519 nfsm_chain_get_32(error, &nmrep, rlen);
520 nfsmout_if(error);
521 *rlenp = rlen;
522 if (rlen <= 0)
523 error = NFSERR_IO;
524 nfsm_chain_get_32(error, &nmrep, committed);
525 nfsm_chain_get_64(error, &nmrep, wverf);
526 nfsmout_if(error);
527 if (wverfp)
528 *wverfp = wverf;
529 lck_mtx_lock(&nmp->nm_lock);
530 if (!(nmp->nm_state & NFSSTA_HASWRITEVERF)) {
531 nmp->nm_verf = wverf;
532 nmp->nm_state |= NFSSTA_HASWRITEVERF;
533 } else if (nmp->nm_verf != wverf) {
534 nmp->nm_verf = wverf;
535 }
536 lck_mtx_unlock(&nmp->nm_lock);
537 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 538 nfsm_chain_loadattr(error, &nmrep, np, nfsvers, &xid);
2d21ac55
A
539nfsmout:
540 if (!lockerror)
b0d623f7 541 nfs_node_unlock(np);
2d21ac55
A
542 nfsm_chain_cleanup(&nmrep);
543 if ((committed != NFS_WRITE_FILESYNC) && nfs_allow_async &&
544 ((mp = NFSTOMP(np))) && (vfs_flags(mp) & MNT_ASYNC))
545 committed = NFS_WRITE_FILESYNC;
546 *iomodep = committed;
6d2010ae
A
547 if (np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR)
548 microuptime(&np->n_lastio);
2d21ac55
A
549 return (error);
550}
551
552int
553nfs4_remove_rpc(
554 nfsnode_t dnp,
555 char *name,
556 int namelen,
557 thread_t thd,
558 kauth_cred_t cred)
559{
b0d623f7 560 int error = 0, lockerror = ENOENT, remove_error = 0, status;
2d21ac55
A
561 struct nfsmount *nmp;
562 int nfsvers, numops;
563 u_int64_t xid;
564 struct nfsm_chain nmreq, nmrep;
6d2010ae 565 struct nfsreq_secinfo_args si;
2d21ac55
A
566
567 nmp = NFSTONMP(dnp);
fe8ab488 568 if (nfs_mount_gone(nmp))
2d21ac55
A
569 return (ENXIO);
570 nfsvers = nmp->nm_vers;
6d2010ae
A
571 if (dnp->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
572 return (EINVAL);
573 NFSREQ_SECINFO_SET(&si, dnp, NULL, 0, NULL, 0);
b0d623f7 574restart:
2d21ac55
A
575 nfsm_chain_null(&nmreq);
576 nfsm_chain_null(&nmrep);
577
578 // PUTFH, REMOVE, GETATTR
579 numops = 3;
580 nfsm_chain_build_alloc_init(error, &nmreq, 17 * NFSX_UNSIGNED + namelen);
581 nfsm_chain_add_compound_header(error, &nmreq, "remove", numops);
582 numops--;
583 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
584 nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
585 numops--;
586 nfsm_chain_add_32(error, &nmreq, NFS_OP_REMOVE);
6d2010ae 587 nfsm_chain_add_name(error, &nmreq, name, namelen, nmp);
2d21ac55
A
588 numops--;
589 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 590 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, dnp);
2d21ac55
A
591 nfsm_chain_build_done(error, &nmreq);
592 nfsm_assert(error, (numops == 0), EPROTO);
593 nfsmout_if(error);
594
6d2010ae 595 error = nfs_request2(dnp, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, 0, &nmrep, &xid, &status);
2d21ac55 596
b0d623f7
A
597 if ((lockerror = nfs_node_lock(dnp)))
598 error = lockerror;
2d21ac55
A
599 nfsm_chain_skip_tag(error, &nmrep);
600 nfsm_chain_get_32(error, &nmrep, numops);
601 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
602 nfsm_chain_op_check(error, &nmrep, NFS_OP_REMOVE);
603 remove_error = error;
604 nfsm_chain_check_change_info(error, &nmrep, dnp);
605 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 606 nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, &xid);
b0d623f7 607 if (error && !lockerror)
2d21ac55
A
608 NATTRINVALIDATE(dnp);
609nfsmout:
610 nfsm_chain_cleanup(&nmreq);
611 nfsm_chain_cleanup(&nmrep);
612
b0d623f7
A
613 if (!lockerror) {
614 dnp->n_flag |= NMODIFIED;
615 nfs_node_unlock(dnp);
616 }
617 if (error == NFSERR_GRACE) {
618 tsleep(&nmp->nm_state, (PZERO-1), "nfsgrace", 2*hz);
619 goto restart;
620 }
2d21ac55
A
621
622 return (remove_error);
623}
624
625int
626nfs4_rename_rpc(
627 nfsnode_t fdnp,
628 char *fnameptr,
629 int fnamelen,
630 nfsnode_t tdnp,
631 char *tnameptr,
632 int tnamelen,
633 vfs_context_t ctx)
634{
b0d623f7 635 int error = 0, lockerror = ENOENT, status, nfsvers, numops;
2d21ac55
A
636 struct nfsmount *nmp;
637 u_int64_t xid, savedxid;
638 struct nfsm_chain nmreq, nmrep;
6d2010ae 639 struct nfsreq_secinfo_args si;
2d21ac55
A
640
641 nmp = NFSTONMP(fdnp);
fe8ab488 642 if (nfs_mount_gone(nmp))
2d21ac55
A
643 return (ENXIO);
644 nfsvers = nmp->nm_vers;
6d2010ae
A
645 if (fdnp->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
646 return (EINVAL);
647 if (tdnp->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
648 return (EINVAL);
2d21ac55 649
6d2010ae 650 NFSREQ_SECINFO_SET(&si, fdnp, NULL, 0, NULL, 0);
2d21ac55
A
651 nfsm_chain_null(&nmreq);
652 nfsm_chain_null(&nmrep);
653
654 // PUTFH(FROM), SAVEFH, PUTFH(TO), RENAME, GETATTR(TO), RESTOREFH, GETATTR(FROM)
655 numops = 7;
656 nfsm_chain_build_alloc_init(error, &nmreq, 30 * NFSX_UNSIGNED + fnamelen + tnamelen);
657 nfsm_chain_add_compound_header(error, &nmreq, "rename", numops);
658 numops--;
659 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
660 nfsm_chain_add_fh(error, &nmreq, nfsvers, fdnp->n_fhp, fdnp->n_fhsize);
661 numops--;
662 nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
663 numops--;
664 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
665 nfsm_chain_add_fh(error, &nmreq, nfsvers, tdnp->n_fhp, tdnp->n_fhsize);
666 numops--;
667 nfsm_chain_add_32(error, &nmreq, NFS_OP_RENAME);
6d2010ae
A
668 nfsm_chain_add_name(error, &nmreq, fnameptr, fnamelen, nmp);
669 nfsm_chain_add_name(error, &nmreq, tnameptr, tnamelen, nmp);
2d21ac55
A
670 numops--;
671 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 672 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, tdnp);
2d21ac55
A
673 numops--;
674 nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
675 numops--;
676 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 677 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, fdnp);
2d21ac55
A
678 nfsm_chain_build_done(error, &nmreq);
679 nfsm_assert(error, (numops == 0), EPROTO);
680 nfsmout_if(error);
681
6d2010ae 682 error = nfs_request(fdnp, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &si, &nmrep, &xid, &status);
2d21ac55 683
b0d623f7
A
684 if ((lockerror = nfs_node_lock2(fdnp, tdnp)))
685 error = lockerror;
2d21ac55
A
686 nfsm_chain_skip_tag(error, &nmrep);
687 nfsm_chain_get_32(error, &nmrep, numops);
688 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
689 nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
690 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
691 nfsm_chain_op_check(error, &nmrep, NFS_OP_RENAME);
692 nfsm_chain_check_change_info(error, &nmrep, fdnp);
693 nfsm_chain_check_change_info(error, &nmrep, tdnp);
694 /* directory attributes: if we don't get them, make sure to invalidate */
695 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
696 savedxid = xid;
6d2010ae 697 nfsm_chain_loadattr(error, &nmrep, tdnp, nfsvers, &xid);
b0d623f7 698 if (error && !lockerror)
2d21ac55
A
699 NATTRINVALIDATE(tdnp);
700 nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
701 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
702 xid = savedxid;
6d2010ae 703 nfsm_chain_loadattr(error, &nmrep, fdnp, nfsvers, &xid);
b0d623f7 704 if (error && !lockerror)
2d21ac55
A
705 NATTRINVALIDATE(fdnp);
706nfsmout:
707 nfsm_chain_cleanup(&nmreq);
708 nfsm_chain_cleanup(&nmrep);
b0d623f7
A
709 if (!lockerror) {
710 fdnp->n_flag |= NMODIFIED;
711 tdnp->n_flag |= NMODIFIED;
712 nfs_node_unlock2(fdnp, tdnp);
713 }
2d21ac55
A
714 return (error);
715}
716
717/*
718 * NFS V4 readdir RPC.
719 */
2d21ac55 720int
b0d623f7
A
721nfs4_readdir_rpc(nfsnode_t dnp, struct nfsbuf *bp, vfs_context_t ctx)
722{
2d21ac55 723 struct nfsmount *nmp;
6d2010ae 724 int error = 0, lockerror, nfsvers, namedattr, rdirplus, bigcookies, numops;
b0d623f7
A
725 int i, status, more_entries = 1, eof, bp_dropped = 0;
726 uint32_t nmreaddirsize, nmrsize;
727 uint32_t namlen, skiplen, fhlen, xlen, attrlen, reclen, space_free, space_needed;
728 uint64_t cookie, lastcookie, xid, savedxid;
729 struct nfsm_chain nmreq, nmrep, nmrepsave;
730 fhandle_t fh;
731 struct nfs_vattr nvattr, *nvattrp;
732 struct nfs_dir_buf_header *ndbhp;
733 struct direntry *dp;
734 char *padstart, padlen;
2d21ac55
A
735 const char *tag;
736 uint32_t entry_attrs[NFS_ATTR_BITMAP_LEN];
b0d623f7 737 struct timeval now;
6d2010ae 738 struct nfsreq_secinfo_args si;
2d21ac55 739
2d21ac55 740 nmp = NFSTONMP(dnp);
fe8ab488 741 if (nfs_mount_gone(nmp))
2d21ac55
A
742 return (ENXIO);
743 nfsvers = nmp->nm_vers;
744 nmreaddirsize = nmp->nm_readdirsize;
745 nmrsize = nmp->nm_rsize;
b0d623f7 746 bigcookies = nmp->nm_state & NFSSTA_BIGCOOKIES;
6d2010ae
A
747 namedattr = (dnp->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR) ? 1 : 0;
748 rdirplus = (NMFLAG(nmp, RDIRPLUS) || namedattr) ? 1 : 0;
749 if (dnp->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
750 return (EINVAL);
751 NFSREQ_SECINFO_SET(&si, dnp, NULL, 0, NULL, 0);
2d21ac55
A
752
753 /*
754 * Set up attribute request for entries.
755 * For READDIRPLUS functionality, get everything.
b0d623f7 756 * Otherwise, just get what we need for struct direntry.
2d21ac55
A
757 */
758 if (rdirplus) {
b0d623f7 759 tag = "readdirplus";
6d2010ae 760 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, entry_attrs);
2d21ac55
A
761 NFS_BITMAP_SET(entry_attrs, NFS_FATTR_FILEHANDLE);
762 } else {
b0d623f7 763 tag = "readdir";
2d21ac55
A
764 NFS_CLEAR_ATTRIBUTES(entry_attrs);
765 NFS_BITMAP_SET(entry_attrs, NFS_FATTR_TYPE);
766 NFS_BITMAP_SET(entry_attrs, NFS_FATTR_FILEID);
6d2010ae 767 NFS_BITMAP_SET(entry_attrs, NFS_FATTR_MOUNTED_ON_FILEID);
2d21ac55 768 }
2d21ac55
A
769 NFS_BITMAP_SET(entry_attrs, NFS_FATTR_RDATTR_ERROR);
770
b0d623f7
A
771 /* lock to protect access to cookie verifier */
772 if ((lockerror = nfs_node_lock(dnp)))
2d21ac55
A
773 return (lockerror);
774
b0d623f7
A
775 /* determine cookie to use, and move dp to the right offset */
776 ndbhp = (struct nfs_dir_buf_header*)bp->nb_data;
777 dp = NFS_DIR_BUF_FIRST_DIRENTRY(bp);
778 if (ndbhp->ndbh_count) {
779 for (i=0; i < ndbhp->ndbh_count-1; i++)
780 dp = NFS_DIRENTRY_NEXT(dp);
781 cookie = dp->d_seekoff;
782 dp = NFS_DIRENTRY_NEXT(dp);
783 } else {
784 cookie = bp->nb_lblkno;
785 /* increment with every buffer read */
316670eb 786 OSAddAtomic64(1, &nfsstats.readdir_bios);
2d21ac55 787 }
b0d623f7 788 lastcookie = cookie;
2d21ac55
A
789
790 /*
b0d623f7
A
791 * The NFS client is responsible for the "." and ".." entries in the
792 * directory. So, we put them at the start of the first buffer.
6d2010ae 793 * Don't bother for attribute directories.
2d21ac55 794 */
6d2010ae
A
795 if (((bp->nb_lblkno == 0) && (ndbhp->ndbh_count == 0)) &&
796 !(dnp->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR)) {
b0d623f7
A
797 fh.fh_len = 0;
798 fhlen = rdirplus ? fh.fh_len + 1 : 0;
799 xlen = rdirplus ? (fhlen + sizeof(time_t)) : 0;
800 /* "." */
801 namlen = 1;
802 reclen = NFS_DIRENTRY_LEN(namlen + xlen);
803 if (xlen)
804 bzero(&dp->d_name[namlen+1], xlen);
805 dp->d_namlen = namlen;
806 strlcpy(dp->d_name, ".", namlen+1);
2d21ac55 807 dp->d_fileno = dnp->n_vattr.nva_fileid;
2d21ac55 808 dp->d_type = DT_DIR;
b0d623f7
A
809 dp->d_reclen = reclen;
810 dp->d_seekoff = 1;
811 padstart = dp->d_name + dp->d_namlen + 1 + xlen;
812 dp = NFS_DIRENTRY_NEXT(dp);
813 padlen = (char*)dp - padstart;
814 if (padlen > 0)
815 bzero(padstart, padlen);
816 if (rdirplus) /* zero out attributes */
817 bzero(NFS_DIR_BUF_NVATTR(bp, 0), sizeof(struct nfs_vattr));
818
819 /* ".." */
820 namlen = 2;
821 reclen = NFS_DIRENTRY_LEN(namlen + xlen);
822 if (xlen)
823 bzero(&dp->d_name[namlen+1], xlen);
824 dp->d_namlen = namlen;
825 strlcpy(dp->d_name, "..", namlen+1);
2d21ac55
A
826 if (dnp->n_parent)
827 dp->d_fileno = VTONFS(dnp->n_parent)->n_vattr.nva_fileid;
828 else
829 dp->d_fileno = dnp->n_vattr.nva_fileid;
2d21ac55 830 dp->d_type = DT_DIR;
b0d623f7
A
831 dp->d_reclen = reclen;
832 dp->d_seekoff = 2;
833 padstart = dp->d_name + dp->d_namlen + 1 + xlen;
834 dp = NFS_DIRENTRY_NEXT(dp);
835 padlen = (char*)dp - padstart;
836 if (padlen > 0)
837 bzero(padstart, padlen);
838 if (rdirplus) /* zero out attributes */
839 bzero(NFS_DIR_BUF_NVATTR(bp, 1), sizeof(struct nfs_vattr));
840
841 ndbhp->ndbh_entry_end = (char*)dp - bp->nb_data;
842 ndbhp->ndbh_count = 2;
2d21ac55
A
843 }
844
845 /*
b0d623f7
A
846 * Loop around doing readdir(plus) RPCs of size nm_readdirsize until
847 * the buffer is full (or we hit EOF). Then put the remainder of the
848 * results in the next buffer(s).
2d21ac55 849 */
b0d623f7
A
850 nfsm_chain_null(&nmreq);
851 nfsm_chain_null(&nmrep);
852 while (nfs_dir_buf_freespace(bp, rdirplus) && !(ndbhp->ndbh_flags & NDB_FULL)) {
2d21ac55 853
b0d623f7
A
854 // PUTFH, GETATTR, READDIR
855 numops = 3;
2d21ac55
A
856 nfsm_chain_build_alloc_init(error, &nmreq, 26 * NFSX_UNSIGNED);
857 nfsm_chain_add_compound_header(error, &nmreq, tag, numops);
858 numops--;
859 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
860 nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
861 numops--;
862 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 863 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, dnp);
2d21ac55
A
864 numops--;
865 nfsm_chain_add_32(error, &nmreq, NFS_OP_READDIR);
b0d623f7
A
866 nfsm_chain_add_64(error, &nmreq, (cookie <= 2) ? 0 : cookie);
867 nfsm_chain_add_64(error, &nmreq, dnp->n_cookieverf);
2d21ac55
A
868 nfsm_chain_add_32(error, &nmreq, nmreaddirsize);
869 nfsm_chain_add_32(error, &nmreq, nmrsize);
6d2010ae 870 nfsm_chain_add_bitmap_supported(error, &nmreq, entry_attrs, nmp, dnp);
2d21ac55
A
871 nfsm_chain_build_done(error, &nmreq);
872 nfsm_assert(error, (numops == 0), EPROTO);
b0d623f7 873 nfs_node_unlock(dnp);
2d21ac55 874 nfsmout_if(error);
6d2010ae 875 error = nfs_request(dnp, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &si, &nmrep, &xid, &status);
2d21ac55 876
b0d623f7 877 if ((lockerror = nfs_node_lock(dnp)))
2d21ac55 878 error = lockerror;
b0d623f7
A
879
880 savedxid = xid;
2d21ac55
A
881 nfsm_chain_skip_tag(error, &nmrep);
882 nfsm_chain_get_32(error, &nmrep, numops);
883 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
884 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 885 nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, &xid);
2d21ac55 886 nfsm_chain_op_check(error, &nmrep, NFS_OP_READDIR);
b0d623f7 887 nfsm_chain_get_64(error, &nmrep, dnp->n_cookieverf);
2d21ac55 888 nfsm_chain_get_32(error, &nmrep, more_entries);
b0d623f7
A
889
890 if (!lockerror) {
891 nfs_node_unlock(dnp);
892 lockerror = ENOENT;
893 }
2d21ac55
A
894 nfsmout_if(error);
895
b0d623f7
A
896 if (rdirplus)
897 microuptime(&now);
898
899 /* loop through the entries packing them into the buffer */
900 while (more_entries) {
2d21ac55 901 /* Entry: COOKIE, NAME, FATTR */
b0d623f7
A
902 nfsm_chain_get_64(error, &nmrep, cookie);
903 nfsm_chain_get_32(error, &nmrep, namlen);
2d21ac55 904 nfsmout_if(error);
b0d623f7
A
905 if (!bigcookies && (cookie >> 32) && (nmp == NFSTONMP(dnp))) {
906 /* we've got a big cookie, make sure flag is set */
907 lck_mtx_lock(&nmp->nm_lock);
908 nmp->nm_state |= NFSSTA_BIGCOOKIES;
909 lck_mtx_unlock(&nmp->nm_lock);
910 bigcookies = 1;
911 }
912 /* just truncate names that don't fit in direntry.d_name */
913 if (namlen <= 0) {
2d21ac55
A
914 error = EBADRPC;
915 goto nfsmout;
916 }
b0d623f7
A
917 if (namlen > (sizeof(dp->d_name)-1)) {
918 skiplen = namlen - sizeof(dp->d_name) + 1;
919 namlen = sizeof(dp->d_name) - 1;
2d21ac55
A
920 } else {
921 skiplen = 0;
922 }
b0d623f7
A
923 /* guess that fh size will be same as parent */
924 fhlen = rdirplus ? (1 + dnp->n_fhsize) : 0;
925 xlen = rdirplus ? (fhlen + sizeof(time_t)) : 0;
926 attrlen = rdirplus ? sizeof(struct nfs_vattr) : 0;
927 reclen = NFS_DIRENTRY_LEN(namlen + xlen);
928 space_needed = reclen + attrlen;
929 space_free = nfs_dir_buf_freespace(bp, rdirplus);
930 if (space_needed > space_free) {
931 /*
932 * We still have entries to pack, but we've
933 * run out of room in the current buffer.
934 * So we need to move to the next buffer.
935 * The block# for the next buffer is the
936 * last cookie in the current buffer.
937 */
938nextbuffer:
939 ndbhp->ndbh_flags |= NDB_FULL;
940 nfs_buf_release(bp, 0);
941 bp_dropped = 1;
942 bp = NULL;
943 error = nfs_buf_get(dnp, lastcookie, NFS_DIRBLKSIZ, vfs_context_thread(ctx), NBLK_READ, &bp);
944 nfsmout_if(error);
945 /* initialize buffer */
946 ndbhp = (struct nfs_dir_buf_header*)bp->nb_data;
947 ndbhp->ndbh_flags = 0;
948 ndbhp->ndbh_count = 0;
949 ndbhp->ndbh_entry_end = sizeof(*ndbhp);
950 ndbhp->ndbh_ncgen = dnp->n_ncgen;
951 space_free = nfs_dir_buf_freespace(bp, rdirplus);
952 dp = NFS_DIR_BUF_FIRST_DIRENTRY(bp);
953 /* increment with every buffer read */
316670eb 954 OSAddAtomic64(1, &nfsstats.readdir_bios);
2d21ac55 955 }
b0d623f7
A
956 nmrepsave = nmrep;
957 dp->d_fileno = cookie; /* placeholder */
958 dp->d_seekoff = cookie;
959 dp->d_namlen = namlen;
960 dp->d_reclen = reclen;
2d21ac55 961 dp->d_type = DT_UNKNOWN;
b0d623f7
A
962 nfsm_chain_get_opaque(error, &nmrep, namlen, dp->d_name);
963 nfsmout_if(error);
964 dp->d_name[namlen] = '\0';
2d21ac55
A
965 if (skiplen)
966 nfsm_chain_adv(error, &nmrep,
b0d623f7 967 nfsm_rndup(namlen + skiplen) - nfsm_rndup(namlen));
2d21ac55 968 nfsmout_if(error);
b0d623f7 969 nvattrp = rdirplus ? NFS_DIR_BUF_NVATTR(bp, ndbhp->ndbh_count) : &nvattr;
6d2010ae
A
970 error = nfs4_parsefattr(&nmrep, NULL, nvattrp, &fh, NULL, NULL);
971 if (!error && NFS_BITMAP_ISSET(nvattrp->nva_bitmap, NFS_FATTR_ACL)) {
972 /* we do NOT want ACLs returned to us here */
973 NFS_BITMAP_CLR(nvattrp->nva_bitmap, NFS_FATTR_ACL);
974 if (nvattrp->nva_acl) {
975 kauth_acl_free(nvattrp->nva_acl);
976 nvattrp->nva_acl = NULL;
977 }
978 }
b0d623f7 979 if (error && NFS_BITMAP_ISSET(nvattrp->nva_bitmap, NFS_FATTR_RDATTR_ERROR)) {
6d2010ae
A
980 /* OK, we may not have gotten all of the attributes but we will use what we can. */
981 if ((error == NFSERR_MOVED) || (error == NFSERR_INVAL)) {
982 /* set this up to look like a referral trigger */
983 nfs4_default_attrs_for_referral_trigger(dnp, dp->d_name, namlen, nvattrp, &fh);
984 }
2d21ac55
A
985 error = 0;
986 }
b0d623f7 987 /* check for more entries after this one */
2d21ac55
A
988 nfsm_chain_get_32(error, &nmrep, more_entries);
989 nfsmout_if(error);
990
b0d623f7 991 /* Skip any "." and ".." entries returned from server. */
6d2010ae
A
992 /* Also skip any bothersome named attribute entries. */
993 if (((dp->d_name[0] == '.') && ((namlen == 1) || ((namlen == 2) && (dp->d_name[1] == '.')))) ||
994 (namedattr && (namlen == 11) && (!strcmp(dp->d_name, "SUNWattr_ro") || !strcmp(dp->d_name, "SUNWattr_rw")))) {
b0d623f7 995 lastcookie = cookie;
2d21ac55
A
996 continue;
997 }
998
b0d623f7
A
999 if (NFS_BITMAP_ISSET(nvattrp->nva_bitmap, NFS_FATTR_TYPE))
1000 dp->d_type = IFTODT(VTTOIF(nvattrp->nva_type));
1001 if (NFS_BITMAP_ISSET(nvattrp->nva_bitmap, NFS_FATTR_FILEID))
1002 dp->d_fileno = nvattrp->nva_fileid;
1003 if (rdirplus) {
1004 /* fileid is already in d_fileno, so stash xid in attrs */
1005 nvattrp->nva_fileid = savedxid;
1006 if (NFS_BITMAP_ISSET(nvattrp->nva_bitmap, NFS_FATTR_FILEHANDLE)) {
1007 fhlen = fh.fh_len + 1;
1008 xlen = fhlen + sizeof(time_t);
1009 reclen = NFS_DIRENTRY_LEN(namlen + xlen);
1010 space_needed = reclen + attrlen;
1011 if (space_needed > space_free) {
1012 /* didn't actually have the room... move on to next buffer */
1013 nmrep = nmrepsave;
1014 goto nextbuffer;
1015 }
1016 /* pack the file handle into the record */
1017 dp->d_name[dp->d_namlen+1] = fh.fh_len;
1018 bcopy(fh.fh_data, &dp->d_name[dp->d_namlen+2], fh.fh_len);
1019 } else {
1020 /* mark the file handle invalid */
1021 fh.fh_len = 0;
1022 fhlen = fh.fh_len + 1;
1023 xlen = fhlen + sizeof(time_t);
1024 reclen = NFS_DIRENTRY_LEN(namlen + xlen);
1025 bzero(&dp->d_name[dp->d_namlen+1], fhlen);
2d21ac55 1026 }
b0d623f7
A
1027 *(time_t*)(&dp->d_name[dp->d_namlen+1+fhlen]) = now.tv_sec;
1028 dp->d_reclen = reclen;
2d21ac55 1029 }
b0d623f7
A
1030 padstart = dp->d_name + dp->d_namlen + 1 + xlen;
1031 ndbhp->ndbh_count++;
1032 lastcookie = cookie;
1033
1034 /* advance to next direntry in buffer */
1035 dp = NFS_DIRENTRY_NEXT(dp);
1036 ndbhp->ndbh_entry_end = (char*)dp - bp->nb_data;
1037 /* zero out the pad bytes */
1038 padlen = (char*)dp - padstart;
1039 if (padlen > 0)
1040 bzero(padstart, padlen);
1041 }
1042 /* Finally, get the eof boolean */
1043 nfsm_chain_get_32(error, &nmrep, eof);
1044 nfsmout_if(error);
1045 if (eof) {
1046 ndbhp->ndbh_flags |= (NDB_FULL|NDB_EOF);
1047 nfs_node_lock_force(dnp);
1048 dnp->n_eofcookie = lastcookie;
1049 nfs_node_unlock(dnp);
1050 } else {
1051 more_entries = 1;
2d21ac55 1052 }
b0d623f7
A
1053 if (bp_dropped) {
1054 nfs_buf_release(bp, 0);
1055 bp = NULL;
1056 break;
2d21ac55 1057 }
b0d623f7 1058 if ((lockerror = nfs_node_lock(dnp)))
2d21ac55
A
1059 error = lockerror;
1060 nfsmout_if(error);
1061 nfsm_chain_cleanup(&nmrep);
b0d623f7 1062 nfsm_chain_null(&nmreq);
2d21ac55 1063 }
2d21ac55 1064nfsmout:
b0d623f7
A
1065 if (bp_dropped && bp)
1066 nfs_buf_release(bp, 0);
1067 if (!lockerror)
1068 nfs_node_unlock(dnp);
2d21ac55
A
1069 nfsm_chain_cleanup(&nmreq);
1070 nfsm_chain_cleanup(&nmrep);
b0d623f7 1071 return (bp_dropped ? NFSERR_DIRBUFDROPPED : error);
2d21ac55
A
1072}
1073
1074int
1075nfs4_lookup_rpc_async(
1076 nfsnode_t dnp,
1077 char *name,
1078 int namelen,
1079 vfs_context_t ctx,
1080 struct nfsreq **reqp)
1081{
6d2010ae 1082 int error = 0, isdotdot = 0, nfsvers, numops;
2d21ac55
A
1083 struct nfsm_chain nmreq;
1084 uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
1085 struct nfsmount *nmp;
6d2010ae 1086 struct nfsreq_secinfo_args si;
2d21ac55
A
1087
1088 nmp = NFSTONMP(dnp);
fe8ab488 1089 if (nfs_mount_gone(nmp))
2d21ac55
A
1090 return (ENXIO);
1091 nfsvers = nmp->nm_vers;
6d2010ae
A
1092 if (dnp->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
1093 return (EINVAL);
2d21ac55 1094
6d2010ae 1095 if ((name[0] == '.') && (name[1] == '.') && (namelen == 2)) {
2d21ac55 1096 isdotdot = 1;
6d2010ae
A
1097 NFSREQ_SECINFO_SET(&si, dnp, NULL, 0, NULL, 0);
1098 } else {
1099 NFSREQ_SECINFO_SET(&si, dnp, dnp->n_fhp, dnp->n_fhsize, name, namelen);
1100 }
2d21ac55
A
1101
1102 nfsm_chain_null(&nmreq);
1103
6d2010ae
A
1104 // PUTFH, GETATTR, LOOKUP(P), GETFH, GETATTR (FH)
1105 numops = 5;
2d21ac55
A
1106 nfsm_chain_build_alloc_init(error, &nmreq, 20 * NFSX_UNSIGNED + namelen);
1107 nfsm_chain_add_compound_header(error, &nmreq, "lookup", numops);
1108 numops--;
1109 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
1110 nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
1111 numops--;
1112 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 1113 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, dnp);
2d21ac55
A
1114 numops--;
1115 if (isdotdot) {
1116 nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUPP);
1117 } else {
1118 nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUP);
6d2010ae 1119 nfsm_chain_add_name(error, &nmreq, name, namelen, nmp);
2d21ac55 1120 }
6d2010ae
A
1121 numops--;
1122 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETFH);
1123 numops--;
1124 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
1125 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
1126 /* some ".zfs" directories can't handle being asked for some attributes */
1127 if ((dnp->n_flag & NISDOTZFS) && !isdotdot)
1128 NFS_BITMAP_CLR(bitmap, NFS_FATTR_NAMED_ATTR);
1129 if ((dnp->n_flag & NISDOTZFSCHILD) && isdotdot)
1130 NFS_BITMAP_CLR(bitmap, NFS_FATTR_NAMED_ATTR);
1131 if (((namelen == 4) && (name[0] == '.') && (name[1] == 'z') && (name[2] == 'f') && (name[3] == 's')))
1132 NFS_BITMAP_CLR(bitmap, NFS_FATTR_NAMED_ATTR);
1133 nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, NULL);
2d21ac55
A
1134 nfsm_chain_build_done(error, &nmreq);
1135 nfsm_assert(error, (numops == 0), EPROTO);
1136 nfsmout_if(error);
1137 error = nfs_request_async(dnp, NULL, &nmreq, NFSPROC4_COMPOUND,
6d2010ae 1138 vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, reqp);
2d21ac55
A
1139nfsmout:
1140 nfsm_chain_cleanup(&nmreq);
1141 return (error);
1142}
1143
6d2010ae 1144
2d21ac55
A
1145int
1146nfs4_lookup_rpc_async_finish(
1147 nfsnode_t dnp,
6d2010ae
A
1148 char *name,
1149 int namelen,
1150 vfs_context_t ctx,
2d21ac55
A
1151 struct nfsreq *req,
1152 u_int64_t *xidp,
1153 fhandle_t *fhp,
1154 struct nfs_vattr *nvap)
1155{
6d2010ae
A
1156 int error = 0, lockerror = ENOENT, status, nfsvers, numops, isdotdot = 0;
1157 uint32_t op = NFS_OP_LOOKUP;
2d21ac55
A
1158 u_int64_t xid;
1159 struct nfsmount *nmp;
1160 struct nfsm_chain nmrep;
1161
1162 nmp = NFSTONMP(dnp);
1163 nfsvers = nmp->nm_vers;
6d2010ae
A
1164 if ((name[0] == '.') && (name[1] == '.') && (namelen == 2))
1165 isdotdot = 1;
2d21ac55
A
1166
1167 nfsm_chain_null(&nmrep);
1168
1169 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
1170
b0d623f7
A
1171 if ((lockerror = nfs_node_lock(dnp)))
1172 error = lockerror;
2d21ac55
A
1173 nfsm_chain_skip_tag(error, &nmrep);
1174 nfsm_chain_get_32(error, &nmrep, numops);
1175 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
1176 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
1177 if (xidp)
1178 *xidp = xid;
6d2010ae 1179 nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, &xid);
2d21ac55 1180
6d2010ae 1181 nfsm_chain_op_check(error, &nmrep, (isdotdot ? NFS_OP_LOOKUPP : NFS_OP_LOOKUP));
2d21ac55 1182 nfsmout_if(error || !fhp || !nvap);
6d2010ae
A
1183 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETFH);
1184 nfsm_chain_get_32(error, &nmrep, fhp->fh_len);
1185 nfsm_chain_get_opaque(error, &nmrep, fhp->fh_len, fhp->fh_data);
2d21ac55 1186 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae
A
1187 if ((error == NFSERR_MOVED) || (error == NFSERR_INVAL)) {
1188 /* set this up to look like a referral trigger */
1189 nfs4_default_attrs_for_referral_trigger(dnp, name, namelen, nvap, fhp);
1190 error = 0;
1191 } else {
1192 nfsmout_if(error);
1193 error = nfs4_parsefattr(&nmrep, NULL, nvap, NULL, NULL, NULL);
2d21ac55
A
1194 }
1195nfsmout:
b0d623f7
A
1196 if (!lockerror)
1197 nfs_node_unlock(dnp);
2d21ac55 1198 nfsm_chain_cleanup(&nmrep);
6d2010ae
A
1199 if (!error && (op == NFS_OP_LOOKUP) && (nmp->nm_state & NFSSTA_NEEDSECINFO)) {
1200 /* We still need to get SECINFO to set default for mount. */
1201 /* Do so for the first LOOKUP that returns successfully. */
1202 struct nfs_sec sec;
1203
1204 sec.count = NX_MAX_SEC_FLAVORS;
1205 error = nfs4_secinfo_rpc(nmp, &req->r_secinfo, vfs_context_ucred(ctx), sec.flavors, &sec.count);
1206 /* [sigh] some implementations return "illegal" error for unsupported ops */
1207 if (error == NFSERR_OP_ILLEGAL)
1208 error = 0;
1209 if (!error) {
1210 /* set our default security flavor to the first in the list */
1211 lck_mtx_lock(&nmp->nm_lock);
1212 if (sec.count)
1213 nmp->nm_auth = sec.flavors[0];
1214 nmp->nm_state &= ~NFSSTA_NEEDSECINFO;
1215 lck_mtx_unlock(&nmp->nm_lock);
1216 }
1217 }
2d21ac55
A
1218 return (error);
1219}
1220
1221int
1222nfs4_commit_rpc(
1223 nfsnode_t np,
6d2010ae
A
1224 uint64_t offset,
1225 uint64_t count,
1226 kauth_cred_t cred,
1227 uint64_t wverf)
2d21ac55
A
1228{
1229 struct nfsmount *nmp;
1230 int error = 0, lockerror, status, nfsvers, numops;
6d2010ae 1231 u_int64_t xid, newwverf;
2d21ac55
A
1232 uint32_t count32;
1233 struct nfsm_chain nmreq, nmrep;
6d2010ae 1234 struct nfsreq_secinfo_args si;
2d21ac55
A
1235
1236 nmp = NFSTONMP(np);
1237 FSDBG(521, np, offset, count, nmp ? nmp->nm_state : 0);
fe8ab488 1238 if (nfs_mount_gone(nmp))
2d21ac55 1239 return (ENXIO);
6d2010ae
A
1240 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
1241 return (EINVAL);
2d21ac55
A
1242 if (!(nmp->nm_state & NFSSTA_HASWRITEVERF))
1243 return (0);
1244 nfsvers = nmp->nm_vers;
1245
1246 if (count > UINT32_MAX)
1247 count32 = 0;
1248 else
1249 count32 = count;
1250
6d2010ae 1251 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
2d21ac55
A
1252 nfsm_chain_null(&nmreq);
1253 nfsm_chain_null(&nmrep);
1254
1255 // PUTFH, COMMIT, GETATTR
1256 numops = 3;
1257 nfsm_chain_build_alloc_init(error, &nmreq, 19 * NFSX_UNSIGNED);
1258 nfsm_chain_add_compound_header(error, &nmreq, "commit", numops);
1259 numops--;
1260 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
1261 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
1262 numops--;
1263 nfsm_chain_add_32(error, &nmreq, NFS_OP_COMMIT);
1264 nfsm_chain_add_64(error, &nmreq, offset);
1265 nfsm_chain_add_32(error, &nmreq, count32);
1266 numops--;
1267 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 1268 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
2d21ac55
A
1269 nfsm_chain_build_done(error, &nmreq);
1270 nfsm_assert(error, (numops == 0), EPROTO);
1271 nfsmout_if(error);
1272 error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND,
6d2010ae 1273 current_thread(), cred, &si, 0, &nmrep, &xid, &status);
2d21ac55 1274
b0d623f7 1275 if ((lockerror = nfs_node_lock(np)))
2d21ac55
A
1276 error = lockerror;
1277 nfsm_chain_skip_tag(error, &nmrep);
1278 nfsm_chain_get_32(error, &nmrep, numops);
1279 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
1280 nfsm_chain_op_check(error, &nmrep, NFS_OP_COMMIT);
6d2010ae 1281 nfsm_chain_get_64(error, &nmrep, newwverf);
2d21ac55 1282 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 1283 nfsm_chain_loadattr(error, &nmrep, np, nfsvers, &xid);
2d21ac55 1284 if (!lockerror)
b0d623f7 1285 nfs_node_unlock(np);
2d21ac55
A
1286 nfsmout_if(error);
1287 lck_mtx_lock(&nmp->nm_lock);
6d2010ae
A
1288 if (nmp->nm_verf != newwverf)
1289 nmp->nm_verf = newwverf;
1290 if (wverf != newwverf)
2d21ac55 1291 error = NFSERR_STALEWRITEVERF;
2d21ac55
A
1292 lck_mtx_unlock(&nmp->nm_lock);
1293nfsmout:
1294 nfsm_chain_cleanup(&nmreq);
1295 nfsm_chain_cleanup(&nmrep);
1296 return (error);
1297}
1298
1299int
1300nfs4_pathconf_rpc(
1301 nfsnode_t np,
1302 struct nfs_fsattr *nfsap,
1303 vfs_context_t ctx)
1304{
1305 u_int64_t xid;
1306 int error = 0, lockerror, status, nfsvers, numops;
1307 struct nfsm_chain nmreq, nmrep;
1308 struct nfsmount *nmp = NFSTONMP(np);
1309 uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
1310 struct nfs_vattr nvattr;
6d2010ae 1311 struct nfsreq_secinfo_args si;
2d21ac55 1312
fe8ab488 1313 if (nfs_mount_gone(nmp))
2d21ac55
A
1314 return (ENXIO);
1315 nfsvers = nmp->nm_vers;
6d2010ae
A
1316 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
1317 return (EINVAL);
2d21ac55 1318
6d2010ae
A
1319 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
1320 NVATTR_INIT(&nvattr);
2d21ac55
A
1321 nfsm_chain_null(&nmreq);
1322 nfsm_chain_null(&nmrep);
1323
1324 /* NFSv4: fetch "pathconf" info for this node */
b0d623f7
A
1325 // PUTFH, GETATTR
1326 numops = 2;
2d21ac55
A
1327 nfsm_chain_build_alloc_init(error, &nmreq, 16 * NFSX_UNSIGNED);
1328 nfsm_chain_add_compound_header(error, &nmreq, "pathconf", numops);
1329 numops--;
1330 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
1331 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
1332 numops--;
1333 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
1334 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
1335 NFS_BITMAP_SET(bitmap, NFS_FATTR_MAXLINK);
1336 NFS_BITMAP_SET(bitmap, NFS_FATTR_MAXNAME);
1337 NFS_BITMAP_SET(bitmap, NFS_FATTR_NO_TRUNC);
1338 NFS_BITMAP_SET(bitmap, NFS_FATTR_CHOWN_RESTRICTED);
1339 NFS_BITMAP_SET(bitmap, NFS_FATTR_CASE_INSENSITIVE);
1340 NFS_BITMAP_SET(bitmap, NFS_FATTR_CASE_PRESERVING);
6d2010ae 1341 nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, np);
2d21ac55
A
1342 nfsm_chain_build_done(error, &nmreq);
1343 nfsm_assert(error, (numops == 0), EPROTO);
1344 nfsmout_if(error);
6d2010ae 1345 error = nfs_request(np, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &si, &nmrep, &xid, &status);
2d21ac55
A
1346
1347 nfsm_chain_skip_tag(error, &nmrep);
1348 nfsm_chain_get_32(error, &nmrep, numops);
1349 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
1350 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
1351 nfsmout_if(error);
6d2010ae 1352 error = nfs4_parsefattr(&nmrep, nfsap, &nvattr, NULL, NULL, NULL);
2d21ac55 1353 nfsmout_if(error);
b0d623f7 1354 if ((lockerror = nfs_node_lock(np)))
2d21ac55 1355 error = lockerror;
b0d623f7
A
1356 if (!error)
1357 nfs_loadattrcache(np, &nvattr, &xid, 0);
2d21ac55 1358 if (!lockerror)
b0d623f7 1359 nfs_node_unlock(np);
2d21ac55 1360nfsmout:
6d2010ae 1361 NVATTR_CLEANUP(&nvattr);
2d21ac55
A
1362 nfsm_chain_cleanup(&nmreq);
1363 nfsm_chain_cleanup(&nmrep);
1364 return (error);
1365}
1366
1367int
1368nfs4_vnop_getattr(
1369 struct vnop_getattr_args /* {
1370 struct vnodeop_desc *a_desc;
1371 vnode_t a_vp;
1372 struct vnode_attr *a_vap;
1373 vfs_context_t a_context;
1374 } */ *ap)
1375{
1376 struct vnode_attr *vap = ap->a_vap;
6d2010ae 1377 struct nfsmount *nmp;
2d21ac55 1378 struct nfs_vattr nva;
6d2010ae
A
1379 int error, acls, ngaflags;
1380
fe8ab488
A
1381 nmp = VTONMP(ap->a_vp);
1382 if (nfs_mount_gone(nmp))
6d2010ae
A
1383 return (ENXIO);
1384 acls = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_ACL);
2d21ac55 1385
6d2010ae
A
1386 ngaflags = NGA_CACHED;
1387 if (VATTR_IS_ACTIVE(vap, va_acl) && acls)
1388 ngaflags |= NGA_ACL;
1389 error = nfs_getattr(VTONFS(ap->a_vp), &nva, ap->a_context, ngaflags);
2d21ac55
A
1390 if (error)
1391 return (error);
1392
1393 /* copy what we have in nva to *a_vap */
6d2010ae 1394 if (VATTR_IS_ACTIVE(vap, va_rdev) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_RAWDEV)) {
2d21ac55
A
1395 dev_t rdev = makedev(nva.nva_rawdev.specdata1, nva.nva_rawdev.specdata2);
1396 VATTR_RETURN(vap, va_rdev, rdev);
1397 }
6d2010ae 1398 if (VATTR_IS_ACTIVE(vap, va_nlink) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_NUMLINKS))
2d21ac55 1399 VATTR_RETURN(vap, va_nlink, nva.nva_nlink);
6d2010ae 1400 if (VATTR_IS_ACTIVE(vap, va_data_size) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_SIZE))
2d21ac55
A
1401 VATTR_RETURN(vap, va_data_size, nva.nva_size);
1402 // VATTR_RETURN(vap, va_data_alloc, ???);
1403 // VATTR_RETURN(vap, va_total_size, ???);
6d2010ae 1404 if (VATTR_IS_ACTIVE(vap, va_total_alloc) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_SPACE_USED))
2d21ac55 1405 VATTR_RETURN(vap, va_total_alloc, nva.nva_bytes);
6d2010ae 1406 if (VATTR_IS_ACTIVE(vap, va_uid) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_OWNER))
2d21ac55 1407 VATTR_RETURN(vap, va_uid, nva.nva_uid);
6d2010ae
A
1408 if (VATTR_IS_ACTIVE(vap, va_uuuid) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_OWNER))
1409 VATTR_RETURN(vap, va_uuuid, nva.nva_uuuid);
1410 if (VATTR_IS_ACTIVE(vap, va_gid) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_OWNER_GROUP))
2d21ac55 1411 VATTR_RETURN(vap, va_gid, nva.nva_gid);
6d2010ae
A
1412 if (VATTR_IS_ACTIVE(vap, va_guuid) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_OWNER_GROUP))
1413 VATTR_RETURN(vap, va_guuid, nva.nva_guuid);
1414 if (VATTR_IS_ACTIVE(vap, va_mode)) {
1415 if (NMFLAG(nmp, ACLONLY) || !NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_MODE))
1416 VATTR_RETURN(vap, va_mode, 0777);
1417 else
1418 VATTR_RETURN(vap, va_mode, nva.nva_mode);
1419 }
1420 if (VATTR_IS_ACTIVE(vap, va_flags) &&
1421 (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_ARCHIVE) ||
1422 NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_HIDDEN) ||
1423 (nva.nva_flags & NFS_FFLAG_TRIGGER))) {
2d21ac55 1424 uint32_t flags = 0;
6d2010ae
A
1425 if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_ARCHIVE) &&
1426 (nva.nva_flags & NFS_FFLAG_ARCHIVED))
2d21ac55 1427 flags |= SF_ARCHIVED;
6d2010ae
A
1428 if (NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_HIDDEN) &&
1429 (nva.nva_flags & NFS_FFLAG_HIDDEN))
2d21ac55
A
1430 flags |= UF_HIDDEN;
1431 VATTR_RETURN(vap, va_flags, flags);
1432 }
6d2010ae 1433 if (VATTR_IS_ACTIVE(vap, va_create_time) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_CREATE)) {
2d21ac55
A
1434 vap->va_create_time.tv_sec = nva.nva_timesec[NFSTIME_CREATE];
1435 vap->va_create_time.tv_nsec = nva.nva_timensec[NFSTIME_CREATE];
1436 VATTR_SET_SUPPORTED(vap, va_create_time);
1437 }
6d2010ae 1438 if (VATTR_IS_ACTIVE(vap, va_access_time) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_ACCESS)) {
2d21ac55
A
1439 vap->va_access_time.tv_sec = nva.nva_timesec[NFSTIME_ACCESS];
1440 vap->va_access_time.tv_nsec = nva.nva_timensec[NFSTIME_ACCESS];
1441 VATTR_SET_SUPPORTED(vap, va_access_time);
1442 }
6d2010ae 1443 if (VATTR_IS_ACTIVE(vap, va_modify_time) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_MODIFY)) {
2d21ac55
A
1444 vap->va_modify_time.tv_sec = nva.nva_timesec[NFSTIME_MODIFY];
1445 vap->va_modify_time.tv_nsec = nva.nva_timensec[NFSTIME_MODIFY];
1446 VATTR_SET_SUPPORTED(vap, va_modify_time);
1447 }
6d2010ae 1448 if (VATTR_IS_ACTIVE(vap, va_change_time) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_METADATA)) {
2d21ac55
A
1449 vap->va_change_time.tv_sec = nva.nva_timesec[NFSTIME_CHANGE];
1450 vap->va_change_time.tv_nsec = nva.nva_timensec[NFSTIME_CHANGE];
1451 VATTR_SET_SUPPORTED(vap, va_change_time);
1452 }
6d2010ae 1453 if (VATTR_IS_ACTIVE(vap, va_backup_time) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TIME_BACKUP)) {
2d21ac55
A
1454 vap->va_backup_time.tv_sec = nva.nva_timesec[NFSTIME_BACKUP];
1455 vap->va_backup_time.tv_nsec = nva.nva_timensec[NFSTIME_BACKUP];
1456 VATTR_SET_SUPPORTED(vap, va_backup_time);
1457 }
6d2010ae 1458 if (VATTR_IS_ACTIVE(vap, va_fileid) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_FILEID))
2d21ac55 1459 VATTR_RETURN(vap, va_fileid, nva.nva_fileid);
6d2010ae 1460 if (VATTR_IS_ACTIVE(vap, va_type) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_TYPE))
2d21ac55 1461 VATTR_RETURN(vap, va_type, nva.nva_type);
6d2010ae 1462 if (VATTR_IS_ACTIVE(vap, va_filerev) && NFS_BITMAP_ISSET(nva.nva_bitmap, NFS_FATTR_CHANGE))
2d21ac55
A
1463 VATTR_RETURN(vap, va_filerev, nva.nva_change);
1464
6d2010ae
A
1465 if (VATTR_IS_ACTIVE(vap, va_acl) && acls) {
1466 VATTR_RETURN(vap, va_acl, nva.nva_acl);
1467 nva.nva_acl = NULL;
1468 }
1469
2d21ac55
A
1470 // other attrs we might support someday:
1471 // VATTR_RETURN(vap, va_encoding, ??? /* potentially unnormalized UTF-8? */);
2d21ac55 1472
6d2010ae 1473 NVATTR_CLEANUP(&nva);
2d21ac55
A
1474 return (error);
1475}
1476
1477int
1478nfs4_setattr_rpc(
1479 nfsnode_t np,
1480 struct vnode_attr *vap,
b0d623f7 1481 vfs_context_t ctx)
2d21ac55
A
1482{
1483 struct nfsmount *nmp = NFSTONMP(np);
6d2010ae 1484 int error = 0, setattr_error = 0, lockerror = ENOENT, status, nfsvers, numops;
b0d623f7 1485 u_int64_t xid, nextxid;
2d21ac55 1486 struct nfsm_chain nmreq, nmrep;
b0d623f7 1487 uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen;
6d2010ae
A
1488 uint32_t getbitmap[NFS_ATTR_BITMAP_LEN];
1489 uint32_t setbitmap[NFS_ATTR_BITMAP_LEN];
b0d623f7 1490 nfs_stateid stateid;
6d2010ae 1491 struct nfsreq_secinfo_args si;
2d21ac55 1492
fe8ab488 1493 if (nfs_mount_gone(nmp))
2d21ac55
A
1494 return (ENXIO);
1495 nfsvers = nmp->nm_vers;
6d2010ae
A
1496 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
1497 return (EINVAL);
2d21ac55
A
1498
1499 if (VATTR_IS_ACTIVE(vap, va_flags) && (vap->va_flags & ~(SF_ARCHIVED|UF_HIDDEN))) {
1500 /* we don't support setting unsupported flags (duh!) */
1501 if (vap->va_active & ~VNODE_ATTR_va_flags)
1502 return (EINVAL); /* return EINVAL if other attributes also set */
1503 else
1504 return (ENOTSUP); /* return ENOTSUP for chflags(2) */
1505 }
1506
6d2010ae
A
1507 /* don't bother requesting some changes if they don't look like they are changing */
1508 if (VATTR_IS_ACTIVE(vap, va_uid) && (vap->va_uid == np->n_vattr.nva_uid))
1509 VATTR_CLEAR_ACTIVE(vap, va_uid);
1510 if (VATTR_IS_ACTIVE(vap, va_gid) && (vap->va_gid == np->n_vattr.nva_gid))
1511 VATTR_CLEAR_ACTIVE(vap, va_gid);
1512 if (VATTR_IS_ACTIVE(vap, va_uuuid) && kauth_guid_equal(&vap->va_uuuid, &np->n_vattr.nva_uuuid))
1513 VATTR_CLEAR_ACTIVE(vap, va_uuuid);
1514 if (VATTR_IS_ACTIVE(vap, va_guuid) && kauth_guid_equal(&vap->va_guuid, &np->n_vattr.nva_guuid))
1515 VATTR_CLEAR_ACTIVE(vap, va_guuid);
1516
1517tryagain:
1518 /* do nothing if no attributes will be sent */
1519 nfs_vattr_set_bitmap(nmp, bitmap, vap);
1520 if (!bitmap[0] && !bitmap[1])
1521 return (0);
1522
1523 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
2d21ac55
A
1524 nfsm_chain_null(&nmreq);
1525 nfsm_chain_null(&nmrep);
1526
6d2010ae
A
1527 /*
1528 * Prepare GETATTR bitmap: if we are setting the ACL or mode, we
1529 * need to invalidate any cached ACL. And if we had an ACL cached,
1530 * we might as well also fetch the new value.
1531 */
1532 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, getbitmap);
1533 if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACL) ||
1534 NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MODE)) {
1535 if (NACLVALID(np))
1536 NFS_BITMAP_SET(getbitmap, NFS_FATTR_ACL);
1537 NACLINVALIDATE(np);
1538 }
1539
2d21ac55
A
1540 // PUTFH, SETATTR, GETATTR
1541 numops = 3;
1542 nfsm_chain_build_alloc_init(error, &nmreq, 40 * NFSX_UNSIGNED);
1543 nfsm_chain_add_compound_header(error, &nmreq, "setattr", numops);
1544 numops--;
1545 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
1546 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
1547 numops--;
1548 nfsm_chain_add_32(error, &nmreq, NFS_OP_SETATTR);
1549 if (VATTR_IS_ACTIVE(vap, va_data_size))
b0d623f7 1550 nfs_get_stateid(np, vfs_context_thread(ctx), vfs_context_ucred(ctx), &stateid);
2d21ac55 1551 else
b0d623f7
A
1552 stateid.seqid = stateid.other[0] = stateid.other[1] = stateid.other[2] = 0;
1553 nfsm_chain_add_stateid(error, &nmreq, &stateid);
2d21ac55
A
1554 nfsm_chain_add_fattr4(error, &nmreq, vap, nmp);
1555 numops--;
1556 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 1557 nfsm_chain_add_bitmap_supported(error, &nmreq, getbitmap, nmp, np);
2d21ac55
A
1558 nfsm_chain_build_done(error, &nmreq);
1559 nfsm_assert(error, (numops == 0), EPROTO);
1560 nfsmout_if(error);
6d2010ae 1561 error = nfs_request(np, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &si, &nmrep, &xid, &status);
2d21ac55 1562
b0d623f7 1563 if ((lockerror = nfs_node_lock(np)))
2d21ac55
A
1564 error = lockerror;
1565 nfsm_chain_skip_tag(error, &nmrep);
1566 nfsm_chain_get_32(error, &nmrep, numops);
1567 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
6d2010ae 1568 nfsmout_if(error);
2d21ac55 1569 nfsm_chain_op_check(error, &nmrep, NFS_OP_SETATTR);
6d2010ae
A
1570 nfsmout_if(error == EBADRPC);
1571 setattr_error = error;
1572 error = 0;
2d21ac55 1573 bmlen = NFS_ATTR_BITMAP_LEN;
6d2010ae
A
1574 nfsm_chain_get_bitmap(error, &nmrep, setbitmap, bmlen);
1575 if (!error) {
1576 if (VATTR_IS_ACTIVE(vap, va_data_size) && (np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR))
1577 microuptime(&np->n_lastio);
1578 nfs_vattr_set_supported(setbitmap, vap);
1579 error = setattr_error;
1580 }
2d21ac55 1581 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 1582 nfsm_chain_loadattr(error, &nmrep, np, nfsvers, &xid);
2d21ac55
A
1583 if (error)
1584 NATTRINVALIDATE(np);
b0d623f7
A
1585 /*
1586 * We just changed the attributes and we want to make sure that we
1587 * see the latest attributes. Get the next XID. If it's not the
1588 * next XID after the SETATTR XID, then it's possible that another
1589 * RPC was in flight at the same time and it might put stale attributes
1590 * in the cache. In that case, we invalidate the attributes and set
1591 * the attribute cache XID to guarantee that newer attributes will
1592 * get loaded next.
1593 */
1594 nextxid = 0;
1595 nfs_get_xid(&nextxid);
1596 if (nextxid != (xid + 1)) {
1597 np->n_xid = nextxid;
1598 NATTRINVALIDATE(np);
1599 }
2d21ac55 1600nfsmout:
b0d623f7
A
1601 if (!lockerror)
1602 nfs_node_unlock(np);
2d21ac55
A
1603 nfsm_chain_cleanup(&nmreq);
1604 nfsm_chain_cleanup(&nmrep);
6d2010ae
A
1605 if ((setattr_error == EINVAL) && VATTR_IS_ACTIVE(vap, va_acl) && VATTR_IS_ACTIVE(vap, va_mode) && !NMFLAG(nmp, ACLONLY)) {
1606 /*
1607 * Some server's may not like ACL/mode combos that get sent.
1608 * If it looks like that's what the server choked on, try setting
1609 * just the ACL and not the mode (unless it looks like everything
1610 * but mode was already successfully set).
1611 */
1612 if (((bitmap[0] & setbitmap[0]) != bitmap[0]) ||
1613 ((bitmap[1] & (setbitmap[1]|NFS_FATTR_MODE)) != bitmap[1])) {
1614 VATTR_CLEAR_ACTIVE(vap, va_mode);
1615 error = 0;
1616 goto tryagain;
1617 }
1618 }
2d21ac55
A
1619 return (error);
1620}
1621
b0d623f7
A
1622/*
1623 * Wait for any pending recovery to complete.
1624 */
2d21ac55 1625int
b0d623f7 1626nfs_mount_state_wait_for_recovery(struct nfsmount *nmp)
2d21ac55 1627{
b0d623f7 1628 struct timespec ts = { 1, 0 };
6d2010ae 1629 int error = 0, slpflag = NMFLAG(nmp, INTR) ? PCATCH : 0;
b0d623f7
A
1630
1631 lck_mtx_lock(&nmp->nm_lock);
1632 while (nmp->nm_state & NFSSTA_RECOVER) {
1633 if ((error = nfs_sigintr(nmp, NULL, current_thread(), 1)))
1634 break;
1635 nfs_mount_sock_thread_wake(nmp);
1636 msleep(&nmp->nm_state, &nmp->nm_lock, slpflag|(PZERO-1), "nfsrecoverwait", &ts);
6d2010ae 1637 slpflag = 0;
b0d623f7
A
1638 }
1639 lck_mtx_unlock(&nmp->nm_lock);
1640
1641 return (error);
2d21ac55
A
1642}
1643
b0d623f7
A
1644/*
1645 * We're about to use/manipulate NFS mount's open/lock state.
1646 * Wait for any pending state recovery to complete, then
1647 * mark the state as being in use (which will hold off
1648 * the recovery thread until we're done).
1649 */
2d21ac55 1650int
6d2010ae 1651nfs_mount_state_in_use_start(struct nfsmount *nmp, thread_t thd)
2d21ac55 1652{
b0d623f7 1653 struct timespec ts = { 1, 0 };
6d2010ae 1654 int error = 0, slpflag = (NMFLAG(nmp, INTR) && thd) ? PCATCH : 0;
b0d623f7 1655
fe8ab488 1656 if (nfs_mount_gone(nmp))
b0d623f7
A
1657 return (ENXIO);
1658 lck_mtx_lock(&nmp->nm_lock);
6d2010ae
A
1659 if (nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD)) {
1660 lck_mtx_unlock(&nmp->nm_lock);
1661 return (ENXIO);
1662 }
b0d623f7 1663 while (nmp->nm_state & NFSSTA_RECOVER) {
6d2010ae 1664 if ((error = nfs_sigintr(nmp, NULL, thd, 1)))
b0d623f7
A
1665 break;
1666 nfs_mount_sock_thread_wake(nmp);
1667 msleep(&nmp->nm_state, &nmp->nm_lock, slpflag|(PZERO-1), "nfsrecoverwait", &ts);
6d2010ae 1668 slpflag = 0;
b0d623f7
A
1669 }
1670 if (!error)
1671 nmp->nm_stateinuse++;
1672 lck_mtx_unlock(&nmp->nm_lock);
1673
1674 return (error);
2d21ac55
A
1675}
1676
b0d623f7
A
1677/*
1678 * We're done using/manipulating the NFS mount's open/lock
1679 * state. If the given error indicates that recovery should
1680 * be performed, we'll initiate recovery.
1681 */
2d21ac55 1682int
b0d623f7 1683nfs_mount_state_in_use_end(struct nfsmount *nmp, int error)
2d21ac55 1684{
b0d623f7
A
1685 int restart = nfs_mount_state_error_should_restart(error);
1686
fe8ab488 1687 if (nfs_mount_gone(nmp))
b0d623f7
A
1688 return (restart);
1689 lck_mtx_lock(&nmp->nm_lock);
1690 if (restart && (error != NFSERR_OLD_STATEID) && (error != NFSERR_GRACE)) {
6d2010ae
A
1691 printf("nfs_mount_state_in_use_end: error %d, initiating recovery for %s, 0x%x\n",
1692 error, vfs_statfs(nmp->nm_mountp)->f_mntfromname, nmp->nm_stategenid);
1693 nfs_need_recover(nmp, error);
b0d623f7
A
1694 }
1695 if (nmp->nm_stateinuse > 0)
1696 nmp->nm_stateinuse--;
1697 else
1698 panic("NFS mount state in use count underrun");
1699 if (!nmp->nm_stateinuse && (nmp->nm_state & NFSSTA_RECOVER))
1700 wakeup(&nmp->nm_stateinuse);
1701 lck_mtx_unlock(&nmp->nm_lock);
1702 if (error == NFSERR_GRACE)
1703 tsleep(&nmp->nm_state, (PZERO-1), "nfsgrace", 2*hz);
1704
1705 return (restart);
2d21ac55
A
1706}
1707
1708/*
b0d623f7 1709 * Does the error mean we should restart/redo a state-related operation?
2d21ac55
A
1710 */
1711int
b0d623f7 1712nfs_mount_state_error_should_restart(int error)
2d21ac55 1713{
b0d623f7
A
1714 switch (error) {
1715 case NFSERR_STALE_STATEID:
1716 case NFSERR_STALE_CLIENTID:
1717 case NFSERR_ADMIN_REVOKED:
1718 case NFSERR_EXPIRED:
1719 case NFSERR_OLD_STATEID:
1720 case NFSERR_BAD_STATEID:
1721 case NFSERR_GRACE:
1722 return (1);
1723 }
1724 return (0);
1725}
2d21ac55 1726
b0d623f7
A
1727/*
1728 * In some cases we may want to limit how many times we restart a
1729 * state-related operation - e.g. we're repeatedly getting NFSERR_GRACE.
1730 * Base the limit on the lease (as long as it's not too short).
1731 */
1732uint
1733nfs_mount_state_max_restarts(struct nfsmount *nmp)
1734{
1735 return (MAX(nmp->nm_fsattr.nfsa_lease, 60));
1736}
2d21ac55 1737
6d2010ae
A
1738/*
1739 * Does the error mean we probably lost a delegation?
1740 */
1741int
1742nfs_mount_state_error_delegation_lost(int error)
1743{
1744 switch (error) {
1745 case NFSERR_STALE_STATEID:
1746 case NFSERR_ADMIN_REVOKED:
1747 case NFSERR_EXPIRED:
1748 case NFSERR_OLD_STATEID:
1749 case NFSERR_BAD_STATEID:
1750 case NFSERR_GRACE: /* ugh! (stupid) RFC 3530 specifically disallows CLAIM_DELEGATE_CUR during grace period? */
1751 return (1);
1752 }
1753 return (0);
1754}
1755
b0d623f7
A
1756
1757/*
1758 * Mark an NFS node's open state as busy.
1759 */
1760int
6d2010ae 1761nfs_open_state_set_busy(nfsnode_t np, thread_t thd)
b0d623f7
A
1762{
1763 struct nfsmount *nmp;
b0d623f7
A
1764 struct timespec ts = {2, 0};
1765 int error = 0, slpflag;
1766
1767 nmp = NFSTONMP(np);
fe8ab488 1768 if (nfs_mount_gone(nmp))
2d21ac55 1769 return (ENXIO);
6d2010ae 1770 slpflag = (NMFLAG(nmp, INTR) && thd) ? PCATCH : 0;
2d21ac55 1771
b0d623f7
A
1772 lck_mtx_lock(&np->n_openlock);
1773 while (np->n_openflags & N_OPENBUSY) {
1774 if ((error = nfs_sigintr(nmp, NULL, thd, 0)))
1775 break;
1776 np->n_openflags |= N_OPENWANT;
1777 msleep(&np->n_openflags, &np->n_openlock, slpflag, "nfs_open_state_set_busy", &ts);
6d2010ae 1778 slpflag = 0;
b0d623f7
A
1779 }
1780 if (!error)
1781 np->n_openflags |= N_OPENBUSY;
1782 lck_mtx_unlock(&np->n_openlock);
2d21ac55 1783
b0d623f7
A
1784 return (error);
1785}
2d21ac55 1786
b0d623f7
A
1787/*
1788 * Clear an NFS node's open state busy flag and wake up
1789 * anyone wanting it.
1790 */
1791void
1792nfs_open_state_clear_busy(nfsnode_t np)
1793{
1794 int wanted;
1795
1796 lck_mtx_lock(&np->n_openlock);
1797 if (!(np->n_openflags & N_OPENBUSY))
1798 panic("nfs_open_state_clear_busy");
1799 wanted = (np->n_openflags & N_OPENWANT);
1800 np->n_openflags &= ~(N_OPENBUSY|N_OPENWANT);
1801 lck_mtx_unlock(&np->n_openlock);
1802 if (wanted)
1803 wakeup(&np->n_openflags);
1804}
2d21ac55 1805
b0d623f7
A
1806/*
1807 * Search a mount's open owner list for the owner for this credential.
1808 * If not found and "alloc" is set, then allocate a new one.
1809 */
1810struct nfs_open_owner *
1811nfs_open_owner_find(struct nfsmount *nmp, kauth_cred_t cred, int alloc)
1812{
1813 uid_t uid = kauth_cred_getuid(cred);
1814 struct nfs_open_owner *noop, *newnoop = NULL;
2d21ac55 1815
b0d623f7
A
1816tryagain:
1817 lck_mtx_lock(&nmp->nm_lock);
1818 TAILQ_FOREACH(noop, &nmp->nm_open_owners, noo_link) {
1819 if (kauth_cred_getuid(noop->noo_cred) == uid)
1820 break;
2d21ac55 1821 }
2d21ac55 1822
b0d623f7
A
1823 if (!noop && !newnoop && alloc) {
1824 lck_mtx_unlock(&nmp->nm_lock);
1825 MALLOC(newnoop, struct nfs_open_owner *, sizeof(struct nfs_open_owner), M_TEMP, M_WAITOK);
1826 if (!newnoop)
1827 return (NULL);
1828 bzero(newnoop, sizeof(*newnoop));
1829 lck_mtx_init(&newnoop->noo_lock, nfs_open_grp, LCK_ATTR_NULL);
1830 newnoop->noo_mount = nmp;
1831 kauth_cred_ref(cred);
1832 newnoop->noo_cred = cred;
1833 newnoop->noo_name = OSAddAtomic(1, &nfs_open_owner_seqnum);
1834 TAILQ_INIT(&newnoop->noo_opens);
1835 goto tryagain;
1836 }
1837 if (!noop && newnoop) {
1838 newnoop->noo_flags |= NFS_OPEN_OWNER_LINK;
1839 TAILQ_INSERT_HEAD(&nmp->nm_open_owners, newnoop, noo_link);
1840 noop = newnoop;
1841 }
1842 lck_mtx_unlock(&nmp->nm_lock);
1843
1844 if (newnoop && (noop != newnoop))
1845 nfs_open_owner_destroy(newnoop);
1846
1847 if (noop)
1848 nfs_open_owner_ref(noop);
1849
1850 return (noop);
1851}
1852
1853/*
1854 * destroy an open owner that's no longer needed
1855 */
1856void
1857nfs_open_owner_destroy(struct nfs_open_owner *noop)
1858{
1859 if (noop->noo_cred)
1860 kauth_cred_unref(&noop->noo_cred);
1861 lck_mtx_destroy(&noop->noo_lock, nfs_open_grp);
1862 FREE(noop, M_TEMP);
1863}
1864
1865/*
1866 * acquire a reference count on an open owner
1867 */
1868void
1869nfs_open_owner_ref(struct nfs_open_owner *noop)
1870{
1871 lck_mtx_lock(&noop->noo_lock);
1872 noop->noo_refcnt++;
1873 lck_mtx_unlock(&noop->noo_lock);
1874}
1875
1876/*
1877 * drop a reference count on an open owner and destroy it if
1878 * it is no longer referenced and no longer on the mount's list.
1879 */
1880void
1881nfs_open_owner_rele(struct nfs_open_owner *noop)
1882{
1883 lck_mtx_lock(&noop->noo_lock);
1884 if (noop->noo_refcnt < 1)
1885 panic("nfs_open_owner_rele: no refcnt");
1886 noop->noo_refcnt--;
1887 if (!noop->noo_refcnt && (noop->noo_flags & NFS_OPEN_OWNER_BUSY))
1888 panic("nfs_open_owner_rele: busy");
1889 /* XXX we may potentially want to clean up idle/unused open owner structures */
1890 if (noop->noo_refcnt || (noop->noo_flags & NFS_OPEN_OWNER_LINK)) {
1891 lck_mtx_unlock(&noop->noo_lock);
1892 return;
1893 }
1894 /* owner is no longer referenced or linked to mount, so destroy it */
1895 lck_mtx_unlock(&noop->noo_lock);
1896 nfs_open_owner_destroy(noop);
1897}
1898
1899/*
1900 * Mark an open owner as busy because we are about to
1901 * start an operation that uses and updates open owner state.
1902 */
1903int
1904nfs_open_owner_set_busy(struct nfs_open_owner *noop, thread_t thd)
1905{
1906 struct nfsmount *nmp;
1907 struct timespec ts = {2, 0};
1908 int error = 0, slpflag;
1909
1910 nmp = noop->noo_mount;
fe8ab488 1911 if (nfs_mount_gone(nmp))
b0d623f7 1912 return (ENXIO);
6d2010ae 1913 slpflag = (NMFLAG(nmp, INTR) && thd) ? PCATCH : 0;
b0d623f7
A
1914
1915 lck_mtx_lock(&noop->noo_lock);
1916 while (noop->noo_flags & NFS_OPEN_OWNER_BUSY) {
1917 if ((error = nfs_sigintr(nmp, NULL, thd, 0)))
1918 break;
1919 noop->noo_flags |= NFS_OPEN_OWNER_WANT;
1920 msleep(noop, &noop->noo_lock, slpflag, "nfs_open_owner_set_busy", &ts);
6d2010ae 1921 slpflag = 0;
b0d623f7
A
1922 }
1923 if (!error)
1924 noop->noo_flags |= NFS_OPEN_OWNER_BUSY;
1925 lck_mtx_unlock(&noop->noo_lock);
1926
1927 return (error);
1928}
1929
1930/*
1931 * Clear the busy flag on an open owner and wake up anyone waiting
1932 * to mark it busy.
1933 */
1934void
1935nfs_open_owner_clear_busy(struct nfs_open_owner *noop)
1936{
1937 int wanted;
1938
1939 lck_mtx_lock(&noop->noo_lock);
1940 if (!(noop->noo_flags & NFS_OPEN_OWNER_BUSY))
1941 panic("nfs_open_owner_clear_busy");
1942 wanted = (noop->noo_flags & NFS_OPEN_OWNER_WANT);
1943 noop->noo_flags &= ~(NFS_OPEN_OWNER_BUSY|NFS_OPEN_OWNER_WANT);
1944 lck_mtx_unlock(&noop->noo_lock);
1945 if (wanted)
1946 wakeup(noop);
1947}
1948
1949/*
1950 * Given an open/lock owner and an error code, increment the
1951 * sequence ID if appropriate.
1952 */
1953void
1954nfs_owner_seqid_increment(struct nfs_open_owner *noop, struct nfs_lock_owner *nlop, int error)
1955{
1956 switch (error) {
1957 case NFSERR_STALE_CLIENTID:
1958 case NFSERR_STALE_STATEID:
1959 case NFSERR_OLD_STATEID:
1960 case NFSERR_BAD_STATEID:
1961 case NFSERR_BAD_SEQID:
1962 case NFSERR_BADXDR:
1963 case NFSERR_RESOURCE:
1964 case NFSERR_NOFILEHANDLE:
1965 /* do not increment the open seqid on these errors */
1966 return;
1967 }
1968 if (noop)
1969 noop->noo_seqid++;
1970 if (nlop)
1971 nlop->nlo_seqid++;
1972}
1973
1974/*
1975 * Search a node's open file list for any conflicts with this request.
1976 * Also find this open owner's open file structure.
1977 * If not found and "alloc" is set, then allocate one.
1978 */
1979int
1980nfs_open_file_find(
1981 nfsnode_t np,
1982 struct nfs_open_owner *noop,
1983 struct nfs_open_file **nofpp,
1984 uint32_t accessMode,
1985 uint32_t denyMode,
1986 int alloc)
6d2010ae
A
1987{
1988 *nofpp = NULL;
1989 return nfs_open_file_find_internal(np, noop, nofpp, accessMode, denyMode, alloc);
1990}
1991
1992/*
1993 * Internally, allow using a provisional nodeless nofp (passed in via *nofpp)
1994 * if an existing one is not found. This is used in "create" scenarios to
1995 * officially add the provisional nofp to the node once the node is created.
1996 */
1997int
1998nfs_open_file_find_internal(
1999 nfsnode_t np,
2000 struct nfs_open_owner *noop,
2001 struct nfs_open_file **nofpp,
2002 uint32_t accessMode,
2003 uint32_t denyMode,
2004 int alloc)
b0d623f7
A
2005{
2006 struct nfs_open_file *nofp = NULL, *nofp2, *newnofp = NULL;
2007
2008 if (!np)
2009 goto alloc;
2010tryagain:
2011 lck_mtx_lock(&np->n_openlock);
2012 TAILQ_FOREACH(nofp2, &np->n_opens, nof_link) {
2013 if (nofp2->nof_owner == noop) {
2014 nofp = nofp2;
2015 if (!accessMode)
2016 break;
2017 }
2018 if ((accessMode & nofp2->nof_deny) || (denyMode & nofp2->nof_access)) {
2019 /* This request conflicts with an existing open on this client. */
2020 lck_mtx_unlock(&np->n_openlock);
b0d623f7
A
2021 return (EACCES);
2022 }
2023 }
2024
2025 /*
2026 * If this open owner doesn't have an open
2027 * file structure yet, we create one for it.
2028 */
6d2010ae 2029 if (!nofp && !*nofpp && !newnofp && alloc) {
b0d623f7
A
2030 lck_mtx_unlock(&np->n_openlock);
2031alloc:
2032 MALLOC(newnofp, struct nfs_open_file *, sizeof(struct nfs_open_file), M_TEMP, M_WAITOK);
6d2010ae 2033 if (!newnofp)
b0d623f7 2034 return (ENOMEM);
b0d623f7
A
2035 bzero(newnofp, sizeof(*newnofp));
2036 lck_mtx_init(&newnofp->nof_lock, nfs_open_grp, LCK_ATTR_NULL);
2037 newnofp->nof_owner = noop;
2038 nfs_open_owner_ref(noop);
2039 newnofp->nof_np = np;
2040 lck_mtx_lock(&noop->noo_lock);
2041 TAILQ_INSERT_HEAD(&noop->noo_opens, newnofp, nof_oolink);
2042 lck_mtx_unlock(&noop->noo_lock);
2043 if (np)
2044 goto tryagain;
2045 }
6d2010ae
A
2046 if (!nofp) {
2047 if (*nofpp) {
2048 (*nofpp)->nof_np = np;
2049 nofp = *nofpp;
2050 } else {
2051 nofp = newnofp;
2052 }
2053 if (nofp && np)
2054 TAILQ_INSERT_HEAD(&np->n_opens, nofp, nof_link);
b0d623f7
A
2055 }
2056 if (np)
2057 lck_mtx_unlock(&np->n_openlock);
2058
6d2010ae 2059 if (alloc && newnofp && (nofp != newnofp))
b0d623f7
A
2060 nfs_open_file_destroy(newnofp);
2061
2062 *nofpp = nofp;
2063 return (nofp ? 0 : ESRCH);
2064}
2065
2066/*
2067 * Destroy an open file structure.
2068 */
2069void
2070nfs_open_file_destroy(struct nfs_open_file *nofp)
2071{
2072 lck_mtx_lock(&nofp->nof_owner->noo_lock);
2073 TAILQ_REMOVE(&nofp->nof_owner->noo_opens, nofp, nof_oolink);
2074 lck_mtx_unlock(&nofp->nof_owner->noo_lock);
2075 nfs_open_owner_rele(nofp->nof_owner);
2076 lck_mtx_destroy(&nofp->nof_lock, nfs_open_grp);
2077 FREE(nofp, M_TEMP);
2078}
2079
2080/*
2081 * Mark an open file as busy because we are about to
2082 * start an operation that uses and updates open file state.
2083 */
2084int
2085nfs_open_file_set_busy(struct nfs_open_file *nofp, thread_t thd)
2086{
2087 struct nfsmount *nmp;
2088 struct timespec ts = {2, 0};
2089 int error = 0, slpflag;
2090
2091 nmp = nofp->nof_owner->noo_mount;
fe8ab488 2092 if (nfs_mount_gone(nmp))
b0d623f7 2093 return (ENXIO);
6d2010ae 2094 slpflag = (NMFLAG(nmp, INTR) && thd) ? PCATCH : 0;
b0d623f7
A
2095
2096 lck_mtx_lock(&nofp->nof_lock);
2097 while (nofp->nof_flags & NFS_OPEN_FILE_BUSY) {
2098 if ((error = nfs_sigintr(nmp, NULL, thd, 0)))
2099 break;
2100 nofp->nof_flags |= NFS_OPEN_FILE_WANT;
2101 msleep(nofp, &nofp->nof_lock, slpflag, "nfs_open_file_set_busy", &ts);
6d2010ae 2102 slpflag = 0;
b0d623f7
A
2103 }
2104 if (!error)
2105 nofp->nof_flags |= NFS_OPEN_FILE_BUSY;
2106 lck_mtx_unlock(&nofp->nof_lock);
2107
2108 return (error);
2109}
2110
2111/*
2112 * Clear the busy flag on an open file and wake up anyone waiting
2113 * to mark it busy.
2114 */
2115void
2116nfs_open_file_clear_busy(struct nfs_open_file *nofp)
2117{
2118 int wanted;
2119
2120 lck_mtx_lock(&nofp->nof_lock);
2121 if (!(nofp->nof_flags & NFS_OPEN_FILE_BUSY))
2122 panic("nfs_open_file_clear_busy");
2123 wanted = (nofp->nof_flags & NFS_OPEN_FILE_WANT);
2124 nofp->nof_flags &= ~(NFS_OPEN_FILE_BUSY|NFS_OPEN_FILE_WANT);
2125 lck_mtx_unlock(&nofp->nof_lock);
2126 if (wanted)
2127 wakeup(nofp);
2128}
2129
2130/*
6d2010ae 2131 * Add the open state for the given access/deny modes to this open file.
b0d623f7
A
2132 */
2133void
6d2010ae 2134nfs_open_file_add_open(struct nfs_open_file *nofp, uint32_t accessMode, uint32_t denyMode, int delegated)
b0d623f7 2135{
6d2010ae
A
2136 lck_mtx_lock(&nofp->nof_lock);
2137 nofp->nof_access |= accessMode;
2138 nofp->nof_deny |= denyMode;
b0d623f7 2139
6d2010ae
A
2140 if (delegated) {
2141 if (denyMode == NFS_OPEN_SHARE_DENY_NONE) {
2142 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ)
2143 nofp->nof_d_r++;
2144 else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE)
2145 nofp->nof_d_w++;
2146 else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH)
2147 nofp->nof_d_rw++;
2148 } else if (denyMode == NFS_OPEN_SHARE_DENY_WRITE) {
2149 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ)
2150 nofp->nof_d_r_dw++;
2151 else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE)
2152 nofp->nof_d_w_dw++;
2153 else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH)
2154 nofp->nof_d_rw_dw++;
2155 } else { /* NFS_OPEN_SHARE_DENY_BOTH */
2156 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ)
2157 nofp->nof_d_r_drw++;
2158 else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE)
2159 nofp->nof_d_w_drw++;
2160 else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH)
2161 nofp->nof_d_rw_drw++;
2162 }
b0d623f7 2163 } else {
6d2010ae
A
2164 if (denyMode == NFS_OPEN_SHARE_DENY_NONE) {
2165 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ)
2166 nofp->nof_r++;
2167 else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE)
2168 nofp->nof_w++;
2169 else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH)
2170 nofp->nof_rw++;
2171 } else if (denyMode == NFS_OPEN_SHARE_DENY_WRITE) {
2172 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ)
2173 nofp->nof_r_dw++;
2174 else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE)
2175 nofp->nof_w_dw++;
2176 else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH)
2177 nofp->nof_rw_dw++;
2178 } else { /* NFS_OPEN_SHARE_DENY_BOTH */
2179 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ)
2180 nofp->nof_r_drw++;
2181 else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE)
2182 nofp->nof_w_drw++;
2183 else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH)
2184 nofp->nof_rw_drw++;
2185 }
b0d623f7 2186 }
6d2010ae
A
2187
2188 nofp->nof_opencnt++;
2189 lck_mtx_unlock(&nofp->nof_lock);
b0d623f7
A
2190}
2191
2192/*
6d2010ae
A
2193 * Find which particular open combo will be closed and report what
2194 * the new modes will be and whether the open was delegated.
b0d623f7 2195 */
6d2010ae
A
2196void
2197nfs_open_file_remove_open_find(
b0d623f7
A
2198 struct nfs_open_file *nofp,
2199 uint32_t accessMode,
2200 uint32_t denyMode,
6d2010ae
A
2201 uint32_t *newAccessMode,
2202 uint32_t *newDenyMode,
2203 int *delegated)
b0d623f7 2204{
6d2010ae
A
2205 /*
2206 * Calculate new modes: a mode bit gets removed when there's only
2207 * one count in all the corresponding counts
2208 */
2209 *newAccessMode = nofp->nof_access;
2210 *newDenyMode = nofp->nof_deny;
b0d623f7 2211
6d2010ae
A
2212 if ((accessMode & NFS_OPEN_SHARE_ACCESS_READ) &&
2213 (nofp->nof_access & NFS_OPEN_SHARE_ACCESS_READ) &&
2214 ((nofp->nof_r + nofp->nof_d_r +
2215 nofp->nof_rw + nofp->nof_d_rw +
2216 nofp->nof_r_dw + nofp->nof_d_r_dw +
2217 nofp->nof_rw_dw + nofp->nof_d_rw_dw +
2218 nofp->nof_r_drw + nofp->nof_d_r_drw +
2219 nofp->nof_rw_dw + nofp->nof_d_rw_dw) == 1))
2220 *newAccessMode &= ~NFS_OPEN_SHARE_ACCESS_READ;
2221 if ((accessMode & NFS_OPEN_SHARE_ACCESS_WRITE) &&
2222 (nofp->nof_access & NFS_OPEN_SHARE_ACCESS_WRITE) &&
2223 ((nofp->nof_w + nofp->nof_d_w +
2224 nofp->nof_rw + nofp->nof_d_rw +
2225 nofp->nof_w_dw + nofp->nof_d_w_dw +
2226 nofp->nof_rw_dw + nofp->nof_d_rw_dw +
2227 nofp->nof_w_drw + nofp->nof_d_w_drw +
2228 nofp->nof_rw_dw + nofp->nof_d_rw_dw) == 1))
2229 *newAccessMode &= ~NFS_OPEN_SHARE_ACCESS_WRITE;
2230 if ((denyMode & NFS_OPEN_SHARE_DENY_READ) &&
2231 (nofp->nof_deny & NFS_OPEN_SHARE_DENY_READ) &&
2232 ((nofp->nof_r_drw + nofp->nof_d_r_drw +
2233 nofp->nof_w_drw + nofp->nof_d_w_drw +
2234 nofp->nof_rw_drw + nofp->nof_d_rw_drw) == 1))
2235 *newDenyMode &= ~NFS_OPEN_SHARE_DENY_READ;
2236 if ((denyMode & NFS_OPEN_SHARE_DENY_WRITE) &&
2237 (nofp->nof_deny & NFS_OPEN_SHARE_DENY_WRITE) &&
2238 ((nofp->nof_r_drw + nofp->nof_d_r_drw +
2239 nofp->nof_w_drw + nofp->nof_d_w_drw +
2240 nofp->nof_rw_drw + nofp->nof_d_rw_drw +
2241 nofp->nof_r_dw + nofp->nof_d_r_dw +
2242 nofp->nof_w_dw + nofp->nof_d_w_dw +
2243 nofp->nof_rw_dw + nofp->nof_d_rw_dw) == 1))
2244 *newDenyMode &= ~NFS_OPEN_SHARE_DENY_WRITE;
2245
2246 /* Find the corresponding open access/deny mode counter. */
b0d623f7
A
2247 if (denyMode == NFS_OPEN_SHARE_DENY_NONE) {
2248 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ)
6d2010ae 2249 *delegated = (nofp->nof_d_r != 0);
b0d623f7 2250 else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE)
6d2010ae 2251 *delegated = (nofp->nof_d_w != 0);
b0d623f7 2252 else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH)
6d2010ae
A
2253 *delegated = (nofp->nof_d_rw != 0);
2254 else
2255 *delegated = 0;
b0d623f7
A
2256 } else if (denyMode == NFS_OPEN_SHARE_DENY_WRITE) {
2257 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ)
6d2010ae 2258 *delegated = (nofp->nof_d_r_dw != 0);
b0d623f7 2259 else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE)
6d2010ae 2260 *delegated = (nofp->nof_d_w_dw != 0);
b0d623f7 2261 else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH)
6d2010ae
A
2262 *delegated = (nofp->nof_d_rw_dw != 0);
2263 else
2264 *delegated = 0;
b0d623f7
A
2265 } else { /* NFS_OPEN_SHARE_DENY_BOTH */
2266 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ)
6d2010ae 2267 *delegated = (nofp->nof_d_r_drw != 0);
b0d623f7 2268 else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE)
6d2010ae 2269 *delegated = (nofp->nof_d_w_drw != 0);
b0d623f7 2270 else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH)
6d2010ae
A
2271 *delegated = (nofp->nof_d_rw_drw != 0);
2272 else
2273 *delegated = 0;
b0d623f7 2274 }
6d2010ae
A
2275}
2276
2277/*
2278 * Remove the open state for the given access/deny modes to this open file.
2279 */
2280void
2281nfs_open_file_remove_open(struct nfs_open_file *nofp, uint32_t accessMode, uint32_t denyMode)
2282{
2283 uint32_t newAccessMode, newDenyMode;
2284 int delegated = 0;
2285
2286 lck_mtx_lock(&nofp->nof_lock);
2287 nfs_open_file_remove_open_find(nofp, accessMode, denyMode, &newAccessMode, &newDenyMode, &delegated);
2288
2289 /* Decrement the corresponding open access/deny mode counter. */
2290 if (denyMode == NFS_OPEN_SHARE_DENY_NONE) {
2291 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ) {
2292 if (delegated) {
2293 if (nofp->nof_d_r == 0)
2294 NP(nofp->nof_np, "nfs: open(R) delegated count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2295 else
2296 nofp->nof_d_r--;
2297 } else {
2298 if (nofp->nof_r == 0)
2299 NP(nofp->nof_np, "nfs: open(R) count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2300 else
2301 nofp->nof_r--;
2302 }
2303 } else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE) {
2304 if (delegated) {
2305 if (nofp->nof_d_w == 0)
2306 NP(nofp->nof_np, "nfs: open(W) delegated count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2307 else
2308 nofp->nof_d_w--;
2309 } else {
2310 if (nofp->nof_w == 0)
2311 NP(nofp->nof_np, "nfs: open(W) count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2312 else
2313 nofp->nof_w--;
2314 }
2315 } else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH) {
2316 if (delegated) {
2317 if (nofp->nof_d_rw == 0)
2318 NP(nofp->nof_np, "nfs: open(RW) delegated count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2319 else
2320 nofp->nof_d_rw--;
2321 } else {
2322 if (nofp->nof_rw == 0)
2323 NP(nofp->nof_np, "nfs: open(RW) count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2324 else
2325 nofp->nof_rw--;
2326 }
2327 }
2328 } else if (denyMode == NFS_OPEN_SHARE_DENY_WRITE) {
2329 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ) {
2330 if (delegated) {
2331 if (nofp->nof_d_r_dw == 0)
2332 NP(nofp->nof_np, "nfs: open(R,DW) delegated count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2333 else
2334 nofp->nof_d_r_dw--;
2335 } else {
2336 if (nofp->nof_r_dw == 0)
2337 NP(nofp->nof_np, "nfs: open(R,DW) count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2338 else
2339 nofp->nof_r_dw--;
2340 }
2341 } else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE) {
2342 if (delegated) {
2343 if (nofp->nof_d_w_dw == 0)
2344 NP(nofp->nof_np, "nfs: open(W,DW) delegated count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2345 else
2346 nofp->nof_d_w_dw--;
2347 } else {
2348 if (nofp->nof_w_dw == 0)
2349 NP(nofp->nof_np, "nfs: open(W,DW) count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2350 else
2351 nofp->nof_w_dw--;
2352 }
2353 } else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH) {
2354 if (delegated) {
2355 if (nofp->nof_d_rw_dw == 0)
2356 NP(nofp->nof_np, "nfs: open(RW,DW) delegated count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2357 else
2358 nofp->nof_d_rw_dw--;
2359 } else {
2360 if (nofp->nof_rw_dw == 0)
2361 NP(nofp->nof_np, "nfs: open(RW,DW) count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2362 else
2363 nofp->nof_rw_dw--;
2364 }
2365 }
2366 } else { /* NFS_OPEN_SHARE_DENY_BOTH */
2367 if (accessMode == NFS_OPEN_SHARE_ACCESS_READ) {
2368 if (delegated) {
2369 if (nofp->nof_d_r_drw == 0)
2370 NP(nofp->nof_np, "nfs: open(R,DRW) delegated count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2371 else
2372 nofp->nof_d_r_drw--;
2373 } else {
2374 if (nofp->nof_r_drw == 0)
2375 NP(nofp->nof_np, "nfs: open(R,DRW) count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2376 else
2377 nofp->nof_r_drw--;
2378 }
2379 } else if (accessMode == NFS_OPEN_SHARE_ACCESS_WRITE) {
2380 if (delegated) {
2381 if (nofp->nof_d_w_drw == 0)
2382 NP(nofp->nof_np, "nfs: open(W,DRW) delegated count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2383 else
2384 nofp->nof_d_w_drw--;
2385 } else {
2386 if (nofp->nof_w_drw == 0)
2387 NP(nofp->nof_np, "nfs: open(W,DRW) count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2388 else
2389 nofp->nof_w_drw--;
2390 }
2391 } else if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH) {
2392 if (delegated) {
2393 if (nofp->nof_d_rw_drw == 0)
2394 NP(nofp->nof_np, "nfs: open(RW,DRW) delegated count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2395 else
2396 nofp->nof_d_rw_drw--;
2397 } else {
2398 if (nofp->nof_rw_drw == 0)
2399 NP(nofp->nof_np, "nfs: open(RW,DRW) count underrun, %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
2400 else
2401 nofp->nof_rw_drw--;
2402 }
2403 }
2404 }
2405
2406 /* update the modes */
2407 nofp->nof_access = newAccessMode;
2408 nofp->nof_deny = newDenyMode;
2409 nofp->nof_opencnt--;
2410 lck_mtx_unlock(&nofp->nof_lock);
2411}
2412
2413
2414/*
2415 * Get the current (delegation, lock, open, default) stateid for this node.
2416 * If node has a delegation, use that stateid.
2417 * If pid has a lock, use the lockowner's stateid.
2418 * Or use the open file's stateid.
2419 * If no open file, use a default stateid of all ones.
2420 */
2421void
2422nfs_get_stateid(nfsnode_t np, thread_t thd, kauth_cred_t cred, nfs_stateid *sid)
2423{
2424 struct nfsmount *nmp = NFSTONMP(np);
2425 proc_t p = thd ? get_bsdthreadtask_info(thd) : current_proc(); // XXX async I/O requests don't have a thread
2426 struct nfs_open_owner *noop = NULL;
2427 struct nfs_open_file *nofp = NULL;
2428 struct nfs_lock_owner *nlop = NULL;
2429 nfs_stateid *s = NULL;
2430
2431 if (np->n_openflags & N_DELEG_MASK) {
2432 s = &np->n_dstateid;
2433 } else {
2434 if (p)
2435 nlop = nfs_lock_owner_find(np, p, 0);
2436 if (nlop && !TAILQ_EMPTY(&nlop->nlo_locks)) {
2437 /* we hold locks, use lock stateid */
2438 s = &nlop->nlo_stateid;
2439 } else if (((noop = nfs_open_owner_find(nmp, cred, 0))) &&
2440 (nfs_open_file_find(np, noop, &nofp, 0, 0, 0) == 0) &&
2441 !(nofp->nof_flags & NFS_OPEN_FILE_LOST) &&
2442 nofp->nof_access) {
2443 /* we (should) have the file open, use open stateid */
2444 if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN)
2445 nfs4_reopen(nofp, thd);
2446 if (!(nofp->nof_flags & NFS_OPEN_FILE_LOST))
2447 s = &nofp->nof_stateid;
2448 }
2449 }
2450
2451 if (s) {
2452 sid->seqid = s->seqid;
2453 sid->other[0] = s->other[0];
2454 sid->other[1] = s->other[1];
2455 sid->other[2] = s->other[2];
2456 } else {
2457 /* named attributes may not have a stateid for reads, so don't complain for them */
2458 if (!(np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR))
2459 NP(np, "nfs_get_stateid: no stateid");
2460 sid->seqid = sid->other[0] = sid->other[1] = sid->other[2] = 0xffffffff;
2461 }
2462 if (nlop)
2463 nfs_lock_owner_rele(nlop);
2464 if (noop)
2465 nfs_open_owner_rele(noop);
2466}
2467
2468
2469/*
2470 * When we have a delegation, we may be able to perform the OPEN locally.
2471 * Perform the OPEN by checking the delegation ACE and/or checking via ACCESS.
2472 */
2473int
2474nfs4_open_delegated(
2475 nfsnode_t np,
2476 struct nfs_open_file *nofp,
2477 uint32_t accessMode,
2478 uint32_t denyMode,
2479 vfs_context_t ctx)
2480{
2481 int error = 0, ismember, readtoo = 0, authorized = 0;
2482 uint32_t action;
2483 struct kauth_acl_eval eval;
2484 kauth_cred_t cred = vfs_context_ucred(ctx);
2485
2486 if (!(accessMode & NFS_OPEN_SHARE_ACCESS_READ)) {
2487 /*
2488 * Try to open it for read access too,
2489 * so the buffer cache can read data.
2490 */
2491 readtoo = 1;
2492 accessMode |= NFS_OPEN_SHARE_ACCESS_READ;
2493 }
2494
2495tryagain:
2496 action = 0;
2497 if (accessMode & NFS_OPEN_SHARE_ACCESS_READ)
2498 action |= KAUTH_VNODE_READ_DATA;
2499 if (accessMode & NFS_OPEN_SHARE_ACCESS_WRITE)
2500 action |= KAUTH_VNODE_WRITE_DATA;
2501
2502 /* evaluate ACE (if we have one) */
2503 if (np->n_dace.ace_flags) {
2504 eval.ae_requested = action;
2505 eval.ae_acl = &np->n_dace;
2506 eval.ae_count = 1;
2507 eval.ae_options = 0;
2508 if (np->n_vattr.nva_uid == kauth_cred_getuid(cred))
2509 eval.ae_options |= KAUTH_AEVAL_IS_OWNER;
2510 error = kauth_cred_ismember_gid(cred, np->n_vattr.nva_gid, &ismember);
2511 if (!error && ismember)
2512 eval.ae_options |= KAUTH_AEVAL_IN_GROUP;
2513
2514 eval.ae_exp_gall = KAUTH_VNODE_GENERIC_ALL_BITS;
2515 eval.ae_exp_gread = KAUTH_VNODE_GENERIC_READ_BITS;
2516 eval.ae_exp_gwrite = KAUTH_VNODE_GENERIC_WRITE_BITS;
2517 eval.ae_exp_gexec = KAUTH_VNODE_GENERIC_EXECUTE_BITS;
2518
2519 error = kauth_acl_evaluate(cred, &eval);
2520
2521 if (!error && (eval.ae_result == KAUTH_RESULT_ALLOW))
2522 authorized = 1;
2523 }
2524
2525 if (!authorized) {
2526 /* need to ask the server via ACCESS */
2527 struct vnop_access_args naa;
2528 naa.a_desc = &vnop_access_desc;
2529 naa.a_vp = NFSTOV(np);
2530 naa.a_action = action;
2531 naa.a_context = ctx;
2532 if (!(error = nfs_vnop_access(&naa)))
2533 authorized = 1;
2534 }
2535
2536 if (!authorized) {
2537 if (readtoo) {
2538 /* try again without the extra read access */
2539 accessMode &= ~NFS_OPEN_SHARE_ACCESS_READ;
2540 readtoo = 0;
2541 goto tryagain;
2542 }
2543 return (error ? error : EACCES);
2544 }
2545
2546 nfs_open_file_add_open(nofp, accessMode, denyMode, 1);
2547
2548 return (0);
2549}
2550
2551
2552/*
2553 * Open a file with the given access/deny modes.
2554 *
2555 * If we have a delegation, we may be able to handle the open locally.
2556 * Otherwise, we will always send the open RPC even if this open's mode is
2557 * a subset of all the existing opens. This makes sure that we will always
2558 * be able to do a downgrade to any of the open modes.
2559 *
2560 * Note: local conflicts should have already been checked in nfs_open_file_find().
2561 */
2562int
2563nfs4_open(
2564 nfsnode_t np,
2565 struct nfs_open_file *nofp,
2566 uint32_t accessMode,
2567 uint32_t denyMode,
2568 vfs_context_t ctx)
2569{
2570 vnode_t vp = NFSTOV(np);
2571 vnode_t dvp = NULL;
2572 struct componentname cn;
2573 const char *vname = NULL;
2574 size_t namelen;
2575 char smallname[128];
2576 char *filename = NULL;
2577 int error = 0, readtoo = 0;
2578
2579 /*
2580 * We can handle the OPEN ourselves if we have a delegation,
2581 * unless it's a read delegation and the open is asking for
2582 * either write access or deny read. We also don't bother to
2583 * use the delegation if it's being returned.
2584 */
2585 if (np->n_openflags & N_DELEG_MASK) {
2586 if ((error = nfs_open_state_set_busy(np, vfs_context_thread(ctx))))
2587 return (error);
2588 if ((np->n_openflags & N_DELEG_MASK) && !(np->n_openflags & N_DELEG_RETURN) &&
2589 (((np->n_openflags & N_DELEG_MASK) == N_DELEG_WRITE) ||
2590 (!(accessMode & NFS_OPEN_SHARE_ACCESS_WRITE) && !(denyMode & NFS_OPEN_SHARE_DENY_READ)))) {
2591 error = nfs4_open_delegated(np, nofp, accessMode, denyMode, ctx);
2592 nfs_open_state_clear_busy(np);
2593 return (error);
2594 }
2595 nfs_open_state_clear_busy(np);
2596 }
2597
2598 /*
2599 * [sigh] We can't trust VFS to get the parent right for named
2600 * attribute nodes. (It likes to reparent the nodes after we've
2601 * created them.) Luckily we can probably get the right parent
2602 * from the n_parent we have stashed away.
2603 */
2604 if ((np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR) &&
2605 (((dvp = np->n_parent)) && (error = vnode_get(dvp))))
2606 dvp = NULL;
2607 if (!dvp)
2608 dvp = vnode_getparent(vp);
2609 vname = vnode_getname(vp);
2610 if (!dvp || !vname) {
2611 if (!error)
2612 error = EIO;
2613 goto out;
2614 }
2615 filename = &smallname[0];
2616 namelen = snprintf(filename, sizeof(smallname), "%s", vname);
2617 if (namelen >= sizeof(smallname)) {
2618 MALLOC(filename, char *, namelen+1, M_TEMP, M_WAITOK);
2619 if (!filename) {
2620 error = ENOMEM;
2621 goto out;
2622 }
2623 snprintf(filename, namelen+1, "%s", vname);
2624 }
2625 bzero(&cn, sizeof(cn));
2626 cn.cn_nameptr = filename;
2627 cn.cn_namelen = namelen;
2628
2629 if (!(accessMode & NFS_OPEN_SHARE_ACCESS_READ)) {
2630 /*
2631 * Try to open it for read access too,
2632 * so the buffer cache can read data.
2633 */
2634 readtoo = 1;
2635 accessMode |= NFS_OPEN_SHARE_ACCESS_READ;
2636 }
2637tryagain:
2638 error = nfs4_open_rpc(nofp, ctx, &cn, NULL, dvp, &vp, NFS_OPEN_NOCREATE, accessMode, denyMode);
2639 if (error) {
2640 if (!nfs_mount_state_error_should_restart(error) &&
2641 (error != EINTR) && (error != ERESTART) && readtoo) {
2642 /* try again without the extra read access */
2643 accessMode &= ~NFS_OPEN_SHARE_ACCESS_READ;
2644 readtoo = 0;
2645 goto tryagain;
2646 }
2647 goto out;
2648 }
2649 nfs_open_file_add_open(nofp, accessMode, denyMode, 0);
b0d623f7
A
2650out:
2651 if (filename && (filename != &smallname[0]))
2652 FREE(filename, M_TEMP);
2653 if (vname)
2654 vnode_putname(vname);
2655 if (dvp != NULLVP)
2656 vnode_put(dvp);
2657 return (error);
2658}
2659
b0d623f7 2660int
6d2010ae
A
2661nfs_vnop_mmap(
2662 struct vnop_mmap_args /* {
b0d623f7
A
2663 struct vnodeop_desc *a_desc;
2664 vnode_t a_vp;
6d2010ae 2665 int a_fflags;
b0d623f7
A
2666 vfs_context_t a_context;
2667 } */ *ap)
2668{
2669 vfs_context_t ctx = ap->a_context;
2670 vnode_t vp = ap->a_vp;
2671 nfsnode_t np = VTONFS(vp);
6d2010ae 2672 int error = 0, accessMode, denyMode, delegated;
b0d623f7 2673 struct nfsmount *nmp;
b0d623f7
A
2674 struct nfs_open_owner *noop = NULL;
2675 struct nfs_open_file *nofp = NULL;
2676
b0d623f7 2677 nmp = VTONMP(vp);
fe8ab488 2678 if (nfs_mount_gone(nmp))
b0d623f7
A
2679 return (ENXIO);
2680
6d2010ae
A
2681 if (!vnode_isreg(vp) || !(ap->a_fflags & (PROT_READ|PROT_WRITE)))
2682 return (EINVAL);
2683 if (np->n_flag & NREVOKE)
2684 return (EIO);
b0d623f7 2685
6d2010ae
A
2686 /*
2687 * fflags contains some combination of: PROT_READ, PROT_WRITE
2688 * Since it's not possible to mmap() without having the file open for reading,
2689 * read access is always there (regardless if PROT_READ is not set).
2690 */
2691 accessMode = NFS_OPEN_SHARE_ACCESS_READ;
2692 if (ap->a_fflags & PROT_WRITE)
b0d623f7 2693 accessMode |= NFS_OPEN_SHARE_ACCESS_WRITE;
6d2010ae 2694 denyMode = NFS_OPEN_SHARE_DENY_NONE;
b0d623f7
A
2695
2696 noop = nfs_open_owner_find(nmp, vfs_context_ucred(ctx), 1);
2697 if (!noop)
2698 return (ENOMEM);
2699
2700restart:
6d2010ae 2701 error = nfs_mount_state_in_use_start(nmp, NULL);
b0d623f7
A
2702 if (error) {
2703 nfs_open_owner_rele(noop);
2704 return (error);
2705 }
6d2010ae 2706 if (np->n_flag & NREVOKE) {
b0d623f7 2707 error = EIO;
6d2010ae
A
2708 nfs_mount_state_in_use_end(nmp, 0);
2709 nfs_open_owner_rele(noop);
2710 return (error);
2711 }
2712
2713 error = nfs_open_file_find(np, noop, &nofp, 0, 0, 1);
2714 if (error || (!error && (nofp->nof_flags & NFS_OPEN_FILE_LOST))) {
2715 NP(np, "nfs_vnop_mmap: no open file for owner, error %d, %d", error, kauth_cred_getuid(noop->noo_cred));
2716 error = EPERM;
b0d623f7
A
2717 }
2718 if (!error && (nofp->nof_flags & NFS_OPEN_FILE_REOPEN)) {
2719 nfs_mount_state_in_use_end(nmp, 0);
6d2010ae 2720 error = nfs4_reopen(nofp, NULL);
b0d623f7 2721 nofp = NULL;
6d2010ae
A
2722 if (!error)
2723 goto restart;
b0d623f7
A
2724 }
2725 if (!error)
6d2010ae 2726 error = nfs_open_file_set_busy(nofp, NULL);
b0d623f7
A
2727 if (error) {
2728 nofp = NULL;
2729 goto out;
2730 }
2731
2732 /*
6d2010ae
A
2733 * The open reference for mmap must mirror an existing open because
2734 * we may need to reclaim it after the file is closed.
2735 * So grab another open count matching the accessMode passed in.
2736 * If we already had an mmap open, prefer read/write without deny mode.
2737 * This means we may have to drop the current mmap open first.
b0d623f7 2738 */
6d2010ae
A
2739
2740 if (!nofp->nof_access) {
2741 if (accessMode != NFS_OPEN_SHARE_ACCESS_READ) {
2742 /* not asking for just read access -> fail */
2743 error = EPERM;
2744 goto out;
2745 }
2746 /* we don't have the file open, so open it for read access */
2747 if (nmp->nm_vers < NFS_VER4) {
2748 /* NFS v2/v3 opens are always allowed - so just add it. */
2749 nfs_open_file_add_open(nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, 0);
b0d623f7 2750 error = 0;
6d2010ae
A
2751 } else {
2752 error = nfs4_open(np, nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, ctx);
b0d623f7 2753 }
6d2010ae
A
2754 if (!error)
2755 nofp->nof_flags |= NFS_OPEN_FILE_NEEDCLOSE;
b0d623f7
A
2756 if (error)
2757 goto out;
6d2010ae
A
2758 }
2759
2760 /* determine deny mode for open */
2761 if (accessMode == NFS_OPEN_SHARE_ACCESS_BOTH) {
2762 if (nofp->nof_d_rw || nofp->nof_d_rw_dw || nofp->nof_d_rw_drw) {
2763 delegated = 1;
2764 if (nofp->nof_d_rw)
2765 denyMode = NFS_OPEN_SHARE_DENY_NONE;
2766 else if (nofp->nof_d_rw_dw)
2767 denyMode = NFS_OPEN_SHARE_DENY_WRITE;
2768 else if (nofp->nof_d_rw_drw)
2769 denyMode = NFS_OPEN_SHARE_DENY_BOTH;
2770 } else if (nofp->nof_rw || nofp->nof_rw_dw || nofp->nof_rw_drw) {
2771 delegated = 0;
2772 if (nofp->nof_rw)
2773 denyMode = NFS_OPEN_SHARE_DENY_NONE;
2774 else if (nofp->nof_rw_dw)
2775 denyMode = NFS_OPEN_SHARE_DENY_WRITE;
2776 else if (nofp->nof_rw_drw)
2777 denyMode = NFS_OPEN_SHARE_DENY_BOTH;
2778 } else {
2779 error = EPERM;
2780 }
2781 } else { /* NFS_OPEN_SHARE_ACCESS_READ */
2782 if (nofp->nof_d_r || nofp->nof_d_r_dw || nofp->nof_d_r_drw) {
2783 delegated = 1;
2784 if (nofp->nof_d_r)
2785 denyMode = NFS_OPEN_SHARE_DENY_NONE;
2786 else if (nofp->nof_d_r_dw)
2787 denyMode = NFS_OPEN_SHARE_DENY_WRITE;
2788 else if (nofp->nof_d_r_drw)
2789 denyMode = NFS_OPEN_SHARE_DENY_BOTH;
2790 } else if (nofp->nof_r || nofp->nof_r_dw || nofp->nof_r_drw) {
2791 delegated = 0;
2792 if (nofp->nof_r)
2793 denyMode = NFS_OPEN_SHARE_DENY_NONE;
2794 else if (nofp->nof_r_dw)
2795 denyMode = NFS_OPEN_SHARE_DENY_WRITE;
2796 else if (nofp->nof_r_drw)
2797 denyMode = NFS_OPEN_SHARE_DENY_BOTH;
2798 } else {
2799 error = EPERM;
2800 }
2801 }
2802 if (error) /* mmap mode without proper open mode */
2803 goto out;
2804
2805 /*
2806 * If the existing mmap access is more than the new access OR the
2807 * existing access is the same and the existing deny mode is less,
2808 * then we'll stick with the existing mmap open mode.
2809 */
2810 if ((nofp->nof_mmap_access > accessMode) ||
2811 ((nofp->nof_mmap_access == accessMode) && (nofp->nof_mmap_deny <= denyMode)))
2812 goto out;
2813
2814 /* update mmap open mode */
2815 if (nofp->nof_mmap_access) {
2816 error = nfs_close(np, nofp, nofp->nof_mmap_access, nofp->nof_mmap_deny, ctx);
2817 if (error) {
2818 if (!nfs_mount_state_error_should_restart(error))
2819 NP(np, "nfs_vnop_mmap: close of previous mmap mode failed: %d, %d", error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
2820 NP(np, "nfs_vnop_mmap: update, close error %d, %d", error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
2821 goto out;
b0d623f7 2822 }
6d2010ae 2823 nofp->nof_mmap_access = nofp->nof_mmap_deny = 0;
b0d623f7
A
2824 }
2825
6d2010ae
A
2826 nfs_open_file_add_open(nofp, accessMode, denyMode, delegated);
2827 nofp->nof_mmap_access = accessMode;
2828 nofp->nof_mmap_deny = denyMode;
2829
b0d623f7
A
2830out:
2831 if (nofp)
2832 nfs_open_file_clear_busy(nofp);
2833 if (nfs_mount_state_in_use_end(nmp, error)) {
2834 nofp = NULL;
2835 goto restart;
2836 }
2837 if (noop)
2838 nfs_open_owner_rele(noop);
316670eb
A
2839
2840 if (!error) {
2841 int ismapped = 0;
2842 nfs_node_lock_force(np);
2843 if ((np->n_flag & NISMAPPED) == 0) {
2844 np->n_flag |= NISMAPPED;
2845 ismapped = 1;
2846 }
2847 nfs_node_unlock(np);
2848 if (ismapped) {
2849 lck_mtx_lock(&nmp->nm_lock);
2850 nmp->nm_state &= ~NFSSTA_SQUISHY;
2851 nmp->nm_curdeadtimeout = nmp->nm_deadtimeout;
2852 if (nmp->nm_curdeadtimeout <= 0)
2853 nmp->nm_deadto_start = 0;
2854 nmp->nm_mappers++;
2855 lck_mtx_unlock(&nmp->nm_lock);
2856 }
2857 }
2858
b0d623f7
A
2859 return (error);
2860}
2861
b0d623f7
A
2862
2863int
6d2010ae
A
2864nfs_vnop_mnomap(
2865 struct vnop_mnomap_args /* {
b0d623f7
A
2866 struct vnodeop_desc *a_desc;
2867 vnode_t a_vp;
b0d623f7
A
2868 vfs_context_t a_context;
2869 } */ *ap)
2870{
2871 vfs_context_t ctx = ap->a_context;
2872 vnode_t vp = ap->a_vp;
b0d623f7
A
2873 nfsnode_t np = VTONFS(vp);
2874 struct nfsmount *nmp;
b0d623f7 2875 struct nfs_open_file *nofp = NULL;
6d2010ae
A
2876 off_t size;
2877 int error;
316670eb
A
2878 int is_mapped_flag = 0;
2879
b0d623f7 2880 nmp = VTONMP(vp);
fe8ab488 2881 if (nfs_mount_gone(nmp))
b0d623f7
A
2882 return (ENXIO);
2883
316670eb
A
2884 nfs_node_lock_force(np);
2885 if (np->n_flag & NISMAPPED) {
2886 is_mapped_flag = 1;
2887 np->n_flag &= ~NISMAPPED;
2888 }
2889 nfs_node_unlock(np);
2890 if (is_mapped_flag) {
2891 lck_mtx_lock(&nmp->nm_lock);
2892 if (nmp->nm_mappers)
2893 nmp->nm_mappers--;
2894 else
2895 NP(np, "nfs_vnop_mnomap: removing mmap reference from mount, but mount has no files mmapped");
2896 lck_mtx_unlock(&nmp->nm_lock);
2897 }
2898
6d2010ae
A
2899 /* flush buffers/ubc before we drop the open (in case it's our last open) */
2900 nfs_flush(np, MNT_WAIT, vfs_context_thread(ctx), V_IGNORE_WRITEERR);
2901 if (UBCINFOEXISTS(vp) && (size = ubc_getsize(vp)))
2902 ubc_msync(vp, 0, size, NULL, UBC_PUSHALL | UBC_SYNC);
b0d623f7 2903
6d2010ae
A
2904 /* walk all open files and close all mmap opens */
2905loop:
2906 error = nfs_mount_state_in_use_start(nmp, NULL);
2907 if (error)
2908 return (error);
2909 lck_mtx_lock(&np->n_openlock);
2910 TAILQ_FOREACH(nofp, &np->n_opens, nof_link) {
2911 if (!nofp->nof_mmap_access)
2912 continue;
b0d623f7 2913 lck_mtx_unlock(&np->n_openlock);
6d2010ae
A
2914 if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN) {
2915 nfs_mount_state_in_use_end(nmp, 0);
2916 error = nfs4_reopen(nofp, NULL);
2917 if (!error)
2918 goto loop;
2919 }
2920 if (!error)
2921 error = nfs_open_file_set_busy(nofp, NULL);
2922 if (error) {
2923 lck_mtx_lock(&np->n_openlock);
2924 break;
2925 }
2926 if (nofp->nof_mmap_access) {
2927 error = nfs_close(np, nofp, nofp->nof_mmap_access, nofp->nof_mmap_deny, ctx);
2928 if (!nfs_mount_state_error_should_restart(error)) {
2929 if (error) /* not a state-operation-restarting error, so just clear the access */
2930 NP(np, "nfs_vnop_mnomap: close of mmap mode failed: %d, %d", error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
2931 nofp->nof_mmap_access = nofp->nof_mmap_deny = 0;
2932 }
2933 if (error)
2934 NP(np, "nfs_vnop_mnomap: error %d, %d", error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
2935 }
2936 nfs_open_file_clear_busy(nofp);
2937 nfs_mount_state_in_use_end(nmp, error);
2938 goto loop;
b0d623f7 2939 }
6d2010ae
A
2940 lck_mtx_unlock(&np->n_openlock);
2941 nfs_mount_state_in_use_end(nmp, error);
2942 return (error);
2943}
b0d623f7 2944
6d2010ae
A
2945/*
2946 * Search a node's lock owner list for the owner for this process.
2947 * If not found and "alloc" is set, then allocate a new one.
2948 */
2949struct nfs_lock_owner *
2950nfs_lock_owner_find(nfsnode_t np, proc_t p, int alloc)
2951{
2952 pid_t pid = proc_pid(p);
2953 struct nfs_lock_owner *nlop, *newnlop = NULL;
b0d623f7 2954
6d2010ae
A
2955tryagain:
2956 lck_mtx_lock(&np->n_openlock);
2957 TAILQ_FOREACH(nlop, &np->n_lock_owners, nlo_link) {
2958 if (nlop->nlo_pid != pid)
2959 continue;
2960 if (timevalcmp(&nlop->nlo_pid_start, &p->p_start, ==))
2961 break;
2962 /* stale lock owner... reuse it if we can */
2963 if (nlop->nlo_refcnt) {
2964 TAILQ_REMOVE(&np->n_lock_owners, nlop, nlo_link);
2965 nlop->nlo_flags &= ~NFS_LOCK_OWNER_LINK;
2966 lck_mtx_unlock(&np->n_openlock);
2967 goto tryagain;
2968 }
2969 nlop->nlo_pid_start = p->p_start;
2970 nlop->nlo_seqid = 0;
2971 nlop->nlo_stategenid = 0;
2972 break;
b0d623f7
A
2973 }
2974
6d2010ae
A
2975 if (!nlop && !newnlop && alloc) {
2976 lck_mtx_unlock(&np->n_openlock);
2977 MALLOC(newnlop, struct nfs_lock_owner *, sizeof(struct nfs_lock_owner), M_TEMP, M_WAITOK);
2978 if (!newnlop)
2979 return (NULL);
2980 bzero(newnlop, sizeof(*newnlop));
2981 lck_mtx_init(&newnlop->nlo_lock, nfs_open_grp, LCK_ATTR_NULL);
2982 newnlop->nlo_pid = pid;
2983 newnlop->nlo_pid_start = p->p_start;
2984 newnlop->nlo_name = OSAddAtomic(1, &nfs_lock_owner_seqnum);
2985 TAILQ_INIT(&newnlop->nlo_locks);
2986 goto tryagain;
b0d623f7 2987 }
6d2010ae
A
2988 if (!nlop && newnlop) {
2989 newnlop->nlo_flags |= NFS_LOCK_OWNER_LINK;
2990 TAILQ_INSERT_HEAD(&np->n_lock_owners, newnlop, nlo_link);
2991 nlop = newnlop;
b0d623f7 2992 }
6d2010ae 2993 lck_mtx_unlock(&np->n_openlock);
b0d623f7 2994
6d2010ae
A
2995 if (newnlop && (nlop != newnlop))
2996 nfs_lock_owner_destroy(newnlop);
b0d623f7 2997
6d2010ae
A
2998 if (nlop)
2999 nfs_lock_owner_ref(nlop);
b0d623f7 3000
6d2010ae
A
3001 return (nlop);
3002}
b0d623f7
A
3003
3004/*
3005 * destroy a lock owner that's no longer needed
3006 */
3007void
3008nfs_lock_owner_destroy(struct nfs_lock_owner *nlop)
3009{
3010 if (nlop->nlo_open_owner) {
3011 nfs_open_owner_rele(nlop->nlo_open_owner);
3012 nlop->nlo_open_owner = NULL;
3013 }
3014 lck_mtx_destroy(&nlop->nlo_lock, nfs_open_grp);
3015 FREE(nlop, M_TEMP);
3016}
3017
3018/*
3019 * acquire a reference count on a lock owner
3020 */
3021void
3022nfs_lock_owner_ref(struct nfs_lock_owner *nlop)
3023{
3024 lck_mtx_lock(&nlop->nlo_lock);
3025 nlop->nlo_refcnt++;
3026 lck_mtx_unlock(&nlop->nlo_lock);
3027}
3028
3029/*
3030 * drop a reference count on a lock owner and destroy it if
3031 * it is no longer referenced and no longer on the mount's list.
3032 */
3033void
3034nfs_lock_owner_rele(struct nfs_lock_owner *nlop)
3035{
3036 lck_mtx_lock(&nlop->nlo_lock);
3037 if (nlop->nlo_refcnt < 1)
3038 panic("nfs_lock_owner_rele: no refcnt");
3039 nlop->nlo_refcnt--;
3040 if (!nlop->nlo_refcnt && (nlop->nlo_flags & NFS_LOCK_OWNER_BUSY))
3041 panic("nfs_lock_owner_rele: busy");
3042 /* XXX we may potentially want to clean up idle/unused lock owner structures */
3043 if (nlop->nlo_refcnt || (nlop->nlo_flags & NFS_LOCK_OWNER_LINK)) {
3044 lck_mtx_unlock(&nlop->nlo_lock);
3045 return;
3046 }
3047 /* owner is no longer referenced or linked to mount, so destroy it */
3048 lck_mtx_unlock(&nlop->nlo_lock);
3049 nfs_lock_owner_destroy(nlop);
3050}
3051
3052/*
3053 * Mark a lock owner as busy because we are about to
3054 * start an operation that uses and updates lock owner state.
3055 */
3056int
3057nfs_lock_owner_set_busy(struct nfs_lock_owner *nlop, thread_t thd)
3058{
3059 struct nfsmount *nmp;
3060 struct timespec ts = {2, 0};
3061 int error = 0, slpflag;
3062
3063 nmp = nlop->nlo_open_owner->noo_mount;
fe8ab488 3064 if (nfs_mount_gone(nmp))
b0d623f7 3065 return (ENXIO);
6d2010ae 3066 slpflag = (NMFLAG(nmp, INTR) && thd) ? PCATCH : 0;
b0d623f7
A
3067
3068 lck_mtx_lock(&nlop->nlo_lock);
3069 while (nlop->nlo_flags & NFS_LOCK_OWNER_BUSY) {
3070 if ((error = nfs_sigintr(nmp, NULL, thd, 0)))
3071 break;
3072 nlop->nlo_flags |= NFS_LOCK_OWNER_WANT;
3073 msleep(nlop, &nlop->nlo_lock, slpflag, "nfs_lock_owner_set_busy", &ts);
6d2010ae 3074 slpflag = 0;
b0d623f7
A
3075 }
3076 if (!error)
3077 nlop->nlo_flags |= NFS_LOCK_OWNER_BUSY;
3078 lck_mtx_unlock(&nlop->nlo_lock);
3079
3080 return (error);
3081}
3082
3083/*
3084 * Clear the busy flag on a lock owner and wake up anyone waiting
3085 * to mark it busy.
3086 */
3087void
3088nfs_lock_owner_clear_busy(struct nfs_lock_owner *nlop)
3089{
3090 int wanted;
3091
3092 lck_mtx_lock(&nlop->nlo_lock);
3093 if (!(nlop->nlo_flags & NFS_LOCK_OWNER_BUSY))
3094 panic("nfs_lock_owner_clear_busy");
3095 wanted = (nlop->nlo_flags & NFS_LOCK_OWNER_WANT);
3096 nlop->nlo_flags &= ~(NFS_LOCK_OWNER_BUSY|NFS_LOCK_OWNER_WANT);
3097 lck_mtx_unlock(&nlop->nlo_lock);
3098 if (wanted)
3099 wakeup(nlop);
3100}
3101
3102/*
3103 * Insert a held lock into a lock owner's sorted list.
3104 * (flock locks are always inserted at the head the list)
3105 */
3106void
3107nfs_lock_owner_insert_held_lock(struct nfs_lock_owner *nlop, struct nfs_file_lock *newnflp)
3108{
3109 struct nfs_file_lock *nflp;
3110
3111 /* insert new lock in lock owner's held lock list */
3112 lck_mtx_lock(&nlop->nlo_lock);
3113 if ((newnflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) == NFS_FILE_LOCK_STYLE_FLOCK) {
3114 TAILQ_INSERT_HEAD(&nlop->nlo_locks, newnflp, nfl_lolink);
3115 } else {
3116 TAILQ_FOREACH(nflp, &nlop->nlo_locks, nfl_lolink) {
3117 if (newnflp->nfl_start < nflp->nfl_start)
3118 break;
3119 }
3120 if (nflp)
3121 TAILQ_INSERT_BEFORE(nflp, newnflp, nfl_lolink);
3122 else
3123 TAILQ_INSERT_TAIL(&nlop->nlo_locks, newnflp, nfl_lolink);
3124 }
3125 lck_mtx_unlock(&nlop->nlo_lock);
3126}
3127
3128/*
3129 * Get a file lock structure for this lock owner.
3130 */
3131struct nfs_file_lock *
3132nfs_file_lock_alloc(struct nfs_lock_owner *nlop)
3133{
3134 struct nfs_file_lock *nflp = NULL;
3135
3136 lck_mtx_lock(&nlop->nlo_lock);
3137 if (!nlop->nlo_alock.nfl_owner) {
3138 nflp = &nlop->nlo_alock;
3139 nflp->nfl_owner = nlop;
3140 }
3141 lck_mtx_unlock(&nlop->nlo_lock);
3142 if (!nflp) {
3143 MALLOC(nflp, struct nfs_file_lock *, sizeof(struct nfs_file_lock), M_TEMP, M_WAITOK);
3144 if (!nflp)
3145 return (NULL);
3146 bzero(nflp, sizeof(*nflp));
3147 nflp->nfl_flags |= NFS_FILE_LOCK_ALLOC;
3148 nflp->nfl_owner = nlop;
3149 }
3150 nfs_lock_owner_ref(nlop);
3151 return (nflp);
3152}
3153
3154/*
3155 * destroy the given NFS file lock structure
3156 */
3157void
3158nfs_file_lock_destroy(struct nfs_file_lock *nflp)
3159{
3160 struct nfs_lock_owner *nlop = nflp->nfl_owner;
3161
3162 if (nflp->nfl_flags & NFS_FILE_LOCK_ALLOC) {
3163 nflp->nfl_owner = NULL;
3164 FREE(nflp, M_TEMP);
3165 } else {
3166 lck_mtx_lock(&nlop->nlo_lock);
3167 bzero(nflp, sizeof(nflp));
3168 lck_mtx_unlock(&nlop->nlo_lock);
3169 }
3170 nfs_lock_owner_rele(nlop);
3171}
3172
3173/*
3174 * Check if one file lock conflicts with another.
3175 * (nflp1 is the new lock. nflp2 is the existing lock.)
3176 */
3177int
3178nfs_file_lock_conflict(struct nfs_file_lock *nflp1, struct nfs_file_lock *nflp2, int *willsplit)
3179{
3180 /* no conflict if lock is dead */
3181 if ((nflp1->nfl_flags & NFS_FILE_LOCK_DEAD) || (nflp2->nfl_flags & NFS_FILE_LOCK_DEAD))
3182 return (0);
3183 /* no conflict if it's ours - unless the lock style doesn't match */
3184 if ((nflp1->nfl_owner == nflp2->nfl_owner) &&
3185 ((nflp1->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) == (nflp2->nfl_flags & NFS_FILE_LOCK_STYLE_MASK))) {
3186 if (willsplit && (nflp1->nfl_type != nflp2->nfl_type) &&
3187 (nflp1->nfl_start > nflp2->nfl_start) &&
3188 (nflp1->nfl_end < nflp2->nfl_end))
3189 *willsplit = 1;
3190 return (0);
3191 }
3192 /* no conflict if ranges don't overlap */
3193 if ((nflp1->nfl_start > nflp2->nfl_end) || (nflp1->nfl_end < nflp2->nfl_start))
3194 return (0);
3195 /* no conflict if neither lock is exclusive */
3196 if ((nflp1->nfl_type != F_WRLCK) && (nflp2->nfl_type != F_WRLCK))
3197 return (0);
3198 /* conflict */
3199 return (1);
3200}
3201
3202/*
3203 * Send an NFSv4 LOCK RPC to the server.
3204 */
3205int
6d2010ae 3206nfs4_setlock_rpc(
b0d623f7
A
3207 nfsnode_t np,
3208 struct nfs_open_file *nofp,
3209 struct nfs_file_lock *nflp,
3210 int reclaim,
6d2010ae 3211 int flags,
b0d623f7
A
3212 thread_t thd,
3213 kauth_cred_t cred)
3214{
3215 struct nfs_lock_owner *nlop = nflp->nfl_owner;
3216 struct nfsmount *nmp;
3217 struct nfsm_chain nmreq, nmrep;
3218 uint64_t xid;
3219 uint32_t locktype;
3220 int error = 0, lockerror = ENOENT, newlocker, numops, status;
6d2010ae 3221 struct nfsreq_secinfo_args si;
b0d623f7
A
3222
3223 nmp = NFSTONMP(np);
fe8ab488 3224 if (nfs_mount_gone(nmp))
b0d623f7 3225 return (ENXIO);
6d2010ae
A
3226 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
3227 return (EINVAL);
b0d623f7
A
3228
3229 newlocker = (nlop->nlo_stategenid != nmp->nm_stategenid);
3230 locktype = (nflp->nfl_flags & NFS_FILE_LOCK_WAIT) ?
3231 ((nflp->nfl_type == F_WRLCK) ?
3232 NFS_LOCK_TYPE_WRITEW :
3233 NFS_LOCK_TYPE_READW) :
3234 ((nflp->nfl_type == F_WRLCK) ?
3235 NFS_LOCK_TYPE_WRITE :
3236 NFS_LOCK_TYPE_READ);
3237 if (newlocker) {
3238 error = nfs_open_file_set_busy(nofp, thd);
3239 if (error)
3240 return (error);
3241 error = nfs_open_owner_set_busy(nofp->nof_owner, thd);
3242 if (error) {
3243 nfs_open_file_clear_busy(nofp);
3244 return (error);
3245 }
3246 if (!nlop->nlo_open_owner) {
3247 nfs_open_owner_ref(nofp->nof_owner);
3248 nlop->nlo_open_owner = nofp->nof_owner;
3249 }
3250 }
3251 error = nfs_lock_owner_set_busy(nlop, thd);
3252 if (error) {
3253 if (newlocker) {
3254 nfs_open_owner_clear_busy(nofp->nof_owner);
3255 nfs_open_file_clear_busy(nofp);
3256 }
3257 return (error);
3258 }
3259
6d2010ae 3260 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
b0d623f7
A
3261 nfsm_chain_null(&nmreq);
3262 nfsm_chain_null(&nmrep);
3263
3264 // PUTFH, GETATTR, LOCK
3265 numops = 3;
3266 nfsm_chain_build_alloc_init(error, &nmreq, 33 * NFSX_UNSIGNED);
3267 nfsm_chain_add_compound_header(error, &nmreq, "lock", numops);
3268 numops--;
3269 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
3270 nfsm_chain_add_fh(error, &nmreq, NFS_VER4, np->n_fhp, np->n_fhsize);
3271 numops--;
3272 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 3273 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
b0d623f7
A
3274 numops--;
3275 nfsm_chain_add_32(error, &nmreq, NFS_OP_LOCK);
3276 nfsm_chain_add_32(error, &nmreq, locktype);
3277 nfsm_chain_add_32(error, &nmreq, reclaim);
3278 nfsm_chain_add_64(error, &nmreq, nflp->nfl_start);
3279 nfsm_chain_add_64(error, &nmreq, NFS_LOCK_LENGTH(nflp->nfl_start, nflp->nfl_end));
3280 nfsm_chain_add_32(error, &nmreq, newlocker);
3281 if (newlocker) {
3282 nfsm_chain_add_32(error, &nmreq, nofp->nof_owner->noo_seqid);
3283 nfsm_chain_add_stateid(error, &nmreq, &nofp->nof_stateid);
3284 nfsm_chain_add_32(error, &nmreq, nlop->nlo_seqid);
3285 nfsm_chain_add_lock_owner4(error, &nmreq, nmp, nlop);
3286 } else {
3287 nfsm_chain_add_stateid(error, &nmreq, &nlop->nlo_stateid);
3288 nfsm_chain_add_32(error, &nmreq, nlop->nlo_seqid);
3289 }
3290 nfsm_chain_build_done(error, &nmreq);
3291 nfsm_assert(error, (numops == 0), EPROTO);
3292 nfsmout_if(error);
3293
6d2010ae 3294 error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, flags|R_NOINTR, &nmrep, &xid, &status);
b0d623f7
A
3295
3296 if ((lockerror = nfs_node_lock(np)))
3297 error = lockerror;
3298 nfsm_chain_skip_tag(error, &nmrep);
3299 nfsm_chain_get_32(error, &nmrep, numops);
3300 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
3301 nfsmout_if(error);
3302 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 3303 nfsm_chain_loadattr(error, &nmrep, np, NFS_VER4, &xid);
b0d623f7
A
3304 nfsmout_if(error);
3305 nfsm_chain_op_check(error, &nmrep, NFS_OP_LOCK);
3306 nfs_owner_seqid_increment(newlocker ? nofp->nof_owner : NULL, nlop, error);
3307 nfsm_chain_get_stateid(error, &nmrep, &nlop->nlo_stateid);
3308
3309 /* Update the lock owner's stategenid once it appears the server has state for it. */
3310 /* We determine this by noting the request was successful (we got a stateid). */
3311 if (newlocker && !error)
3312 nlop->nlo_stategenid = nmp->nm_stategenid;
3313nfsmout:
3314 if (!lockerror)
3315 nfs_node_unlock(np);
3316 nfs_lock_owner_clear_busy(nlop);
3317 if (newlocker) {
3318 nfs_open_owner_clear_busy(nofp->nof_owner);
3319 nfs_open_file_clear_busy(nofp);
3320 }
3321 nfsm_chain_cleanup(&nmreq);
3322 nfsm_chain_cleanup(&nmrep);
3323 return (error);
3324}
3325
3326/*
3327 * Send an NFSv4 LOCKU RPC to the server.
3328 */
3329int
3330nfs4_unlock_rpc(
3331 nfsnode_t np,
3332 struct nfs_lock_owner *nlop,
3333 int type,
3334 uint64_t start,
3335 uint64_t end,
6d2010ae
A
3336 int flags,
3337 thread_t thd,
3338 kauth_cred_t cred)
b0d623f7
A
3339{
3340 struct nfsmount *nmp;
3341 struct nfsm_chain nmreq, nmrep;
3342 uint64_t xid;
3343 int error = 0, lockerror = ENOENT, numops, status;
6d2010ae 3344 struct nfsreq_secinfo_args si;
b0d623f7
A
3345
3346 nmp = NFSTONMP(np);
fe8ab488 3347 if (nfs_mount_gone(nmp))
b0d623f7 3348 return (ENXIO);
6d2010ae
A
3349 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
3350 return (EINVAL);
b0d623f7 3351
6d2010ae 3352 error = nfs_lock_owner_set_busy(nlop, NULL);
b0d623f7
A
3353 if (error)
3354 return (error);
3355
6d2010ae 3356 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
b0d623f7
A
3357 nfsm_chain_null(&nmreq);
3358 nfsm_chain_null(&nmrep);
3359
3360 // PUTFH, GETATTR, LOCKU
3361 numops = 3;
3362 nfsm_chain_build_alloc_init(error, &nmreq, 26 * NFSX_UNSIGNED);
3363 nfsm_chain_add_compound_header(error, &nmreq, "unlock", numops);
3364 numops--;
3365 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
3366 nfsm_chain_add_fh(error, &nmreq, NFS_VER4, np->n_fhp, np->n_fhsize);
3367 numops--;
3368 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 3369 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
b0d623f7
A
3370 numops--;
3371 nfsm_chain_add_32(error, &nmreq, NFS_OP_LOCKU);
3372 nfsm_chain_add_32(error, &nmreq, (type == F_WRLCK) ? NFS_LOCK_TYPE_WRITE : NFS_LOCK_TYPE_READ);
3373 nfsm_chain_add_32(error, &nmreq, nlop->nlo_seqid);
3374 nfsm_chain_add_stateid(error, &nmreq, &nlop->nlo_stateid);
3375 nfsm_chain_add_64(error, &nmreq, start);
3376 nfsm_chain_add_64(error, &nmreq, NFS_LOCK_LENGTH(start, end));
3377 nfsm_chain_build_done(error, &nmreq);
3378 nfsm_assert(error, (numops == 0), EPROTO);
3379 nfsmout_if(error);
3380
6d2010ae 3381 error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, flags|R_NOINTR, &nmrep, &xid, &status);
b0d623f7
A
3382
3383 if ((lockerror = nfs_node_lock(np)))
3384 error = lockerror;
3385 nfsm_chain_skip_tag(error, &nmrep);
3386 nfsm_chain_get_32(error, &nmrep, numops);
3387 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
3388 nfsmout_if(error);
3389 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 3390 nfsm_chain_loadattr(error, &nmrep, np, NFS_VER4, &xid);
b0d623f7
A
3391 nfsmout_if(error);
3392 nfsm_chain_op_check(error, &nmrep, NFS_OP_LOCKU);
3393 nfs_owner_seqid_increment(NULL, nlop, error);
3394 nfsm_chain_get_stateid(error, &nmrep, &nlop->nlo_stateid);
3395nfsmout:
3396 if (!lockerror)
3397 nfs_node_unlock(np);
3398 nfs_lock_owner_clear_busy(nlop);
3399 nfsm_chain_cleanup(&nmreq);
3400 nfsm_chain_cleanup(&nmrep);
3401 return (error);
3402}
3403
3404/*
6d2010ae 3405 * Send an NFSv4 LOCKT RPC to the server.
b0d623f7
A
3406 */
3407int
6d2010ae 3408nfs4_getlock_rpc(
b0d623f7
A
3409 nfsnode_t np,
3410 struct nfs_lock_owner *nlop,
3411 struct flock *fl,
3412 uint64_t start,
3413 uint64_t end,
3414 vfs_context_t ctx)
3415{
3416 struct nfsmount *nmp;
b0d623f7
A
3417 struct nfsm_chain nmreq, nmrep;
3418 uint64_t xid, val64 = 0;
3419 uint32_t val = 0;
6d2010ae
A
3420 int error = 0, lockerror, numops, status;
3421 struct nfsreq_secinfo_args si;
b0d623f7
A
3422
3423 nmp = NFSTONMP(np);
fe8ab488 3424 if (nfs_mount_gone(nmp))
b0d623f7 3425 return (ENXIO);
6d2010ae
A
3426 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
3427 return (EINVAL);
b0d623f7 3428
6d2010ae
A
3429 lockerror = ENOENT;
3430 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
b0d623f7
A
3431 nfsm_chain_null(&nmreq);
3432 nfsm_chain_null(&nmrep);
3433
3434 // PUTFH, GETATTR, LOCKT
3435 numops = 3;
3436 nfsm_chain_build_alloc_init(error, &nmreq, 26 * NFSX_UNSIGNED);
3437 nfsm_chain_add_compound_header(error, &nmreq, "locktest", numops);
3438 numops--;
3439 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
3440 nfsm_chain_add_fh(error, &nmreq, NFS_VER4, np->n_fhp, np->n_fhsize);
3441 numops--;
3442 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 3443 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
b0d623f7
A
3444 numops--;
3445 nfsm_chain_add_32(error, &nmreq, NFS_OP_LOCKT);
3446 nfsm_chain_add_32(error, &nmreq, (fl->l_type == F_WRLCK) ? NFS_LOCK_TYPE_WRITE : NFS_LOCK_TYPE_READ);
3447 nfsm_chain_add_64(error, &nmreq, start);
3448 nfsm_chain_add_64(error, &nmreq, NFS_LOCK_LENGTH(start, end));
3449 nfsm_chain_add_lock_owner4(error, &nmreq, nmp, nlop);
3450 nfsm_chain_build_done(error, &nmreq);
3451 nfsm_assert(error, (numops == 0), EPROTO);
3452 nfsmout_if(error);
3453
6d2010ae 3454 error = nfs_request(np, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &si, &nmrep, &xid, &status);
b0d623f7
A
3455
3456 if ((lockerror = nfs_node_lock(np)))
3457 error = lockerror;
3458 nfsm_chain_skip_tag(error, &nmrep);
3459 nfsm_chain_get_32(error, &nmrep, numops);
3460 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
3461 nfsmout_if(error);
3462 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 3463 nfsm_chain_loadattr(error, &nmrep, np, NFS_VER4, &xid);
b0d623f7
A
3464 nfsmout_if(error);
3465 nfsm_chain_op_check(error, &nmrep, NFS_OP_LOCKT);
3466 if (error == NFSERR_DENIED) {
3467 error = 0;
3468 nfsm_chain_get_64(error, &nmrep, fl->l_start);
3469 nfsm_chain_get_64(error, &nmrep, val64);
3470 fl->l_len = (val64 == UINT64_MAX) ? 0 : val64;
3471 nfsm_chain_get_32(error, &nmrep, val);
3472 fl->l_type = (val == NFS_LOCK_TYPE_WRITE) ? F_WRLCK : F_RDLCK;
3473 fl->l_pid = 0;
3474 fl->l_whence = SEEK_SET;
3475 } else if (!error) {
3476 fl->l_type = F_UNLCK;
3477 }
3478nfsmout:
3479 if (!lockerror)
3480 nfs_node_unlock(np);
3481 nfsm_chain_cleanup(&nmreq);
3482 nfsm_chain_cleanup(&nmrep);
3483 return (error);
3484}
3485
6d2010ae
A
3486
3487/*
3488 * Check for any conflicts with the given lock.
3489 *
3490 * Checking for a lock doesn't require the file to be opened.
3491 * So we skip all the open owner, open file, lock owner work
3492 * and just check for a conflicting lock.
3493 */
3494int
3495nfs_advlock_getlock(
3496 nfsnode_t np,
3497 struct nfs_lock_owner *nlop,
3498 struct flock *fl,
3499 uint64_t start,
3500 uint64_t end,
3501 vfs_context_t ctx)
3502{
3503 struct nfsmount *nmp;
3504 struct nfs_file_lock *nflp;
3505 int error = 0, answered = 0;
3506
3507 nmp = NFSTONMP(np);
fe8ab488 3508 if (nfs_mount_gone(nmp))
6d2010ae
A
3509 return (ENXIO);
3510
3511restart:
3512 if ((error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx))))
3513 return (error);
3514
3515 lck_mtx_lock(&np->n_openlock);
3516 /* scan currently held locks for conflict */
3517 TAILQ_FOREACH(nflp, &np->n_locks, nfl_link) {
3518 if (nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED|NFS_FILE_LOCK_DEAD))
3519 continue;
3520 if ((start <= nflp->nfl_end) && (end >= nflp->nfl_start) &&
3521 ((fl->l_type == F_WRLCK) || (nflp->nfl_type == F_WRLCK)))
3522 break;
3523 }
3524 if (nflp) {
3525 /* found a conflicting lock */
3526 fl->l_type = nflp->nfl_type;
3527 fl->l_pid = (nflp->nfl_flags & NFS_FILE_LOCK_STYLE_FLOCK) ? -1 : nflp->nfl_owner->nlo_pid;
3528 fl->l_start = nflp->nfl_start;
3529 fl->l_len = NFS_FLOCK_LENGTH(nflp->nfl_start, nflp->nfl_end);
3530 fl->l_whence = SEEK_SET;
3531 answered = 1;
3532 } else if ((np->n_openflags & N_DELEG_WRITE) && !(np->n_openflags & N_DELEG_RETURN)) {
3533 /*
3534 * If we have a write delegation, we know there can't be other
3535 * locks on the server. So the answer is no conflicting lock found.
3536 */
3537 fl->l_type = F_UNLCK;
3538 answered = 1;
3539 }
3540 lck_mtx_unlock(&np->n_openlock);
3541 if (answered) {
3542 nfs_mount_state_in_use_end(nmp, 0);
3543 return (0);
3544 }
3545
3546 /* no conflict found locally, so ask the server */
3547 error = nmp->nm_funcs->nf_getlock_rpc(np, nlop, fl, start, end, ctx);
3548
3549 if (nfs_mount_state_in_use_end(nmp, error))
3550 goto restart;
3551 return (error);
3552}
3553
b0d623f7
A
3554/*
3555 * Acquire a file lock for the given range.
3556 *
3557 * Add the lock (request) to the lock queue.
3558 * Scan the lock queue for any conflicting locks.
3559 * If a conflict is found, block or return an error.
3560 * Once end of queue is reached, send request to the server.
3561 * If the server grants the lock, scan the lock queue and
3562 * update any existing locks. Then (optionally) scan the
3563 * queue again to coalesce any locks adjacent to the new one.
3564 */
3565int
6d2010ae 3566nfs_advlock_setlock(
b0d623f7
A
3567 nfsnode_t np,
3568 struct nfs_open_file *nofp,
3569 struct nfs_lock_owner *nlop,
3570 int op,
3571 uint64_t start,
3572 uint64_t end,
3573 int style,
3574 short type,
3575 vfs_context_t ctx)
3576{
3577 struct nfsmount *nmp;
3578 struct nfs_file_lock *newnflp, *nflp, *nflp2 = NULL, *nextnflp, *flocknflp = NULL;
3579 struct nfs_file_lock *coalnflp;
3580 int error = 0, error2, willsplit = 0, delay, slpflag, busy = 0, inuse = 0, restart, inqueue = 0;
3581 struct timespec ts = {1, 0};
3582
3583 nmp = NFSTONMP(np);
fe8ab488 3584 if (nfs_mount_gone(nmp))
b0d623f7 3585 return (ENXIO);
6d2010ae
A
3586 slpflag = NMFLAG(nmp, INTR) ? PCATCH : 0;
3587
3588 if ((type != F_RDLCK) && (type != F_WRLCK))
3589 return (EINVAL);
b0d623f7
A
3590
3591 /* allocate a new lock */
3592 newnflp = nfs_file_lock_alloc(nlop);
3593 if (!newnflp)
3594 return (ENOLCK);
3595 newnflp->nfl_start = start;
3596 newnflp->nfl_end = end;
3597 newnflp->nfl_type = type;
3598 if (op == F_SETLKW)
3599 newnflp->nfl_flags |= NFS_FILE_LOCK_WAIT;
3600 newnflp->nfl_flags |= style;
3601 newnflp->nfl_flags |= NFS_FILE_LOCK_BLOCKED;
3602
3603 if ((style == NFS_FILE_LOCK_STYLE_FLOCK) && (type == F_WRLCK)) {
3604 /*
3605 * For exclusive flock-style locks, if we block waiting for the
3606 * lock, we need to first release any currently held shared
3607 * flock-style lock. So, the first thing we do is check if we
3608 * have a shared flock-style lock.
3609 */
3610 nflp = TAILQ_FIRST(&nlop->nlo_locks);
3611 if (nflp && ((nflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) != NFS_FILE_LOCK_STYLE_FLOCK))
3612 nflp = NULL;
3613 if (nflp && (nflp->nfl_type != F_RDLCK))
3614 nflp = NULL;
3615 flocknflp = nflp;
3616 }
3617
3618restart:
3619 restart = 0;
6d2010ae 3620 error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx));
b0d623f7
A
3621 if (error)
3622 goto error_out;
3623 inuse = 1;
6d2010ae
A
3624 if (np->n_flag & NREVOKE) {
3625 error = EIO;
3626 nfs_mount_state_in_use_end(nmp, 0);
3627 inuse = 0;
3628 goto error_out;
3629 }
b0d623f7
A
3630 if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN) {
3631 nfs_mount_state_in_use_end(nmp, 0);
3632 inuse = 0;
6d2010ae
A
3633 error = nfs4_reopen(nofp, vfs_context_thread(ctx));
3634 if (error)
3635 goto error_out;
b0d623f7
A
3636 goto restart;
3637 }
3638
3639 lck_mtx_lock(&np->n_openlock);
3640 if (!inqueue) {
3641 /* insert new lock at beginning of list */
3642 TAILQ_INSERT_HEAD(&np->n_locks, newnflp, nfl_link);
3643 inqueue = 1;
3644 }
3645
3646 /* scan current list of locks (held and pending) for conflicts */
6d2010ae
A
3647 for (nflp = TAILQ_NEXT(newnflp, nfl_link); nflp; nflp = nextnflp) {
3648 nextnflp = TAILQ_NEXT(nflp, nfl_link);
b0d623f7
A
3649 if (!nfs_file_lock_conflict(newnflp, nflp, &willsplit))
3650 continue;
3651 /* Conflict */
3652 if (!(newnflp->nfl_flags & NFS_FILE_LOCK_WAIT)) {
3653 error = EAGAIN;
3654 break;
3655 }
3656 /* Block until this lock is no longer held. */
3657 if (nflp->nfl_blockcnt == UINT_MAX) {
3658 error = ENOLCK;
3659 break;
3660 }
3661 nflp->nfl_blockcnt++;
3662 do {
3663 if (flocknflp) {
3664 /* release any currently held shared lock before sleeping */
3665 lck_mtx_unlock(&np->n_openlock);
3666 nfs_mount_state_in_use_end(nmp, 0);
3667 inuse = 0;
6d2010ae 3668 error = nfs_advlock_unlock(np, nofp, nlop, 0, UINT64_MAX, NFS_FILE_LOCK_STYLE_FLOCK, ctx);
b0d623f7
A
3669 flocknflp = NULL;
3670 if (!error)
6d2010ae 3671 error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx));
b0d623f7
A
3672 if (error) {
3673 lck_mtx_lock(&np->n_openlock);
3674 break;
3675 }
3676 inuse = 1;
3677 lck_mtx_lock(&np->n_openlock);
3678 /* no need to block/sleep if the conflict is gone */
3679 if (!nfs_file_lock_conflict(newnflp, nflp, NULL))
3680 break;
3681 }
6d2010ae
A
3682 msleep(nflp, &np->n_openlock, slpflag, "nfs_advlock_setlock_blocked", &ts);
3683 slpflag = 0;
b0d623f7
A
3684 error = nfs_sigintr(NFSTONMP(np), NULL, vfs_context_thread(ctx), 0);
3685 if (!error && (nmp->nm_state & NFSSTA_RECOVER)) {
3686 /* looks like we have a recover pending... restart */
3687 restart = 1;
3688 lck_mtx_unlock(&np->n_openlock);
3689 nfs_mount_state_in_use_end(nmp, 0);
3690 inuse = 0;
3691 lck_mtx_lock(&np->n_openlock);
3692 break;
3693 }
6d2010ae
A
3694 if (!error && (np->n_flag & NREVOKE))
3695 error = EIO;
b0d623f7
A
3696 } while (!error && nfs_file_lock_conflict(newnflp, nflp, NULL));
3697 nflp->nfl_blockcnt--;
3698 if ((nflp->nfl_flags & NFS_FILE_LOCK_DEAD) && !nflp->nfl_blockcnt) {
3699 TAILQ_REMOVE(&np->n_locks, nflp, nfl_link);
3700 nfs_file_lock_destroy(nflp);
3701 }
3702 if (error || restart)
3703 break;
6d2010ae
A
3704 /* We have released n_openlock and we can't trust that nextnflp is still valid. */
3705 /* So, start this lock-scanning loop over from where it started. */
3706 nextnflp = TAILQ_NEXT(newnflp, nfl_link);
b0d623f7
A
3707 }
3708 lck_mtx_unlock(&np->n_openlock);
3709 if (restart)
3710 goto restart;
3711 if (error)
3712 goto error_out;
3713
3714 if (willsplit) {
3715 /*
3716 * It looks like this operation is splitting a lock.
3717 * We allocate a new lock now so we don't have to worry
3718 * about the allocation failing after we've updated some state.
3719 */
3720 nflp2 = nfs_file_lock_alloc(nlop);
3721 if (!nflp2) {
3722 error = ENOLCK;
3723 goto error_out;
3724 }
3725 }
3726
3727 /* once scan for local conflicts is clear, send request to server */
6d2010ae 3728 if ((error = nfs_open_state_set_busy(np, vfs_context_thread(ctx))))
b0d623f7
A
3729 goto error_out;
3730 busy = 1;
3731 delay = 0;
3732 do {
6d2010ae
A
3733 /* do we have a delegation? (that we're not returning?) */
3734 if ((np->n_openflags & N_DELEG_MASK) && !(np->n_openflags & N_DELEG_RETURN)) {
3735 if (np->n_openflags & N_DELEG_WRITE) {
3736 /* with a write delegation, just take the lock delegated */
3737 newnflp->nfl_flags |= NFS_FILE_LOCK_DELEGATED;
3738 error = 0;
3739 /* make sure the lock owner knows its open owner */
3740 if (!nlop->nlo_open_owner) {
3741 nfs_open_owner_ref(nofp->nof_owner);
3742 nlop->nlo_open_owner = nofp->nof_owner;
3743 }
3744 break;
3745 } else {
3746 /*
3747 * If we don't have any non-delegated opens but we do have
3748 * delegated opens, then we need to first claim the delegated
3749 * opens so that the lock request on the server can be associated
3750 * with an open it knows about.
3751 */
3752 if ((!nofp->nof_rw_drw && !nofp->nof_w_drw && !nofp->nof_r_drw &&
3753 !nofp->nof_rw_dw && !nofp->nof_w_dw && !nofp->nof_r_dw &&
3754 !nofp->nof_rw && !nofp->nof_w && !nofp->nof_r) &&
3755 (nofp->nof_d_rw_drw || nofp->nof_d_w_drw || nofp->nof_d_r_drw ||
3756 nofp->nof_d_rw_dw || nofp->nof_d_w_dw || nofp->nof_d_r_dw ||
3757 nofp->nof_d_rw || nofp->nof_d_w || nofp->nof_d_r)) {
3758 error = nfs4_claim_delegated_state_for_open_file(nofp, 0);
3759 if (error)
3760 break;
3761 }
3762 }
3763 }
3764 if (np->n_flag & NREVOKE)
3765 error = EIO;
3766 if (!error)
3767 error = nmp->nm_funcs->nf_setlock_rpc(np, nofp, newnflp, 0, 0, vfs_context_thread(ctx), vfs_context_ucred(ctx));
b0d623f7
A
3768 if (!error || ((error != NFSERR_DENIED) && (error != NFSERR_GRACE)))
3769 break;
3770 /* request was denied due to either conflict or grace period */
6d2010ae 3771 if ((error == NFSERR_DENIED) && !(newnflp->nfl_flags & NFS_FILE_LOCK_WAIT)) {
b0d623f7
A
3772 error = EAGAIN;
3773 break;
3774 }
3775 if (flocknflp) {
3776 /* release any currently held shared lock before sleeping */
3777 nfs_open_state_clear_busy(np);
3778 busy = 0;
3779 nfs_mount_state_in_use_end(nmp, 0);
3780 inuse = 0;
6d2010ae 3781 error2 = nfs_advlock_unlock(np, nofp, nlop, 0, UINT64_MAX, NFS_FILE_LOCK_STYLE_FLOCK, ctx);
b0d623f7
A
3782 flocknflp = NULL;
3783 if (!error2)
6d2010ae 3784 error2 = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx));
b0d623f7
A
3785 if (!error2) {
3786 inuse = 1;
6d2010ae 3787 error2 = nfs_open_state_set_busy(np, vfs_context_thread(ctx));
b0d623f7
A
3788 }
3789 if (error2) {
3790 error = error2;
3791 break;
3792 }
3793 busy = 1;
3794 }
6d2010ae
A
3795 /*
3796 * Wait a little bit and send the request again.
3797 * Except for retries of blocked v2/v3 request where we've already waited a bit.
3798 */
3799 if ((nmp->nm_vers >= NFS_VER4) || (error == NFSERR_GRACE)) {
3800 if (error == NFSERR_GRACE)
3801 delay = 4;
3802 if (delay < 4)
3803 delay++;
3804 tsleep(newnflp, slpflag, "nfs_advlock_setlock_delay", delay * (hz/2));
3805 slpflag = 0;
3806 }
b0d623f7
A
3807 error = nfs_sigintr(NFSTONMP(np), NULL, vfs_context_thread(ctx), 0);
3808 if (!error && (nmp->nm_state & NFSSTA_RECOVER)) {
3809 /* looks like we have a recover pending... restart */
3810 nfs_open_state_clear_busy(np);
3811 busy = 0;
3812 nfs_mount_state_in_use_end(nmp, 0);
3813 inuse = 0;
3814 goto restart;
3815 }
6d2010ae
A
3816 if (!error && (np->n_flag & NREVOKE))
3817 error = EIO;
b0d623f7
A
3818 } while (!error);
3819
3820error_out:
3821 if (nfs_mount_state_error_should_restart(error)) {
3822 /* looks like we need to restart this operation */
3823 if (busy) {
3824 nfs_open_state_clear_busy(np);
3825 busy = 0;
3826 }
3827 if (inuse) {
3828 nfs_mount_state_in_use_end(nmp, error);
3829 inuse = 0;
3830 }
3831 goto restart;
3832 }
3833 lck_mtx_lock(&np->n_openlock);
3834 newnflp->nfl_flags &= ~NFS_FILE_LOCK_BLOCKED;
3835 if (error) {
3836 newnflp->nfl_flags |= NFS_FILE_LOCK_DEAD;
3837 if (newnflp->nfl_blockcnt) {
3838 /* wake up anyone blocked on this lock */
3839 wakeup(newnflp);
3840 } else {
3841 /* remove newnflp from lock list and destroy */
316670eb
A
3842 if (inqueue)
3843 TAILQ_REMOVE(&np->n_locks, newnflp, nfl_link);
b0d623f7
A
3844 nfs_file_lock_destroy(newnflp);
3845 }
3846 lck_mtx_unlock(&np->n_openlock);
3847 if (busy)
3848 nfs_open_state_clear_busy(np);
3849 if (inuse)
3850 nfs_mount_state_in_use_end(nmp, error);
3851 if (nflp2)
3852 nfs_file_lock_destroy(nflp2);
3853 return (error);
3854 }
3855
3856 /* server granted the lock */
3857
3858 /*
3859 * Scan for locks to update.
3860 *
3861 * Locks completely covered are killed.
3862 * At most two locks may need to be clipped.
3863 * It's possible that a single lock may need to be split.
3864 */
3865 TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) {
3866 if (nflp == newnflp)
3867 continue;
3868 if (nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED|NFS_FILE_LOCK_DEAD))
3869 continue;
3870 if (nflp->nfl_owner != nlop)
3871 continue;
3872 if ((newnflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) != (nflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK))
3873 continue;
3874 if ((newnflp->nfl_start > nflp->nfl_end) || (newnflp->nfl_end < nflp->nfl_start))
3875 continue;
3876 /* here's one to update */
3877 if ((newnflp->nfl_start <= nflp->nfl_start) && (newnflp->nfl_end >= nflp->nfl_end)) {
3878 /* The entire lock is being replaced. */
3879 nflp->nfl_flags |= NFS_FILE_LOCK_DEAD;
3880 lck_mtx_lock(&nlop->nlo_lock);
3881 TAILQ_REMOVE(&nlop->nlo_locks, nflp, nfl_lolink);
3882 lck_mtx_unlock(&nlop->nlo_lock);
3883 /* lock will be destroyed below, if no waiters */
3884 } else if ((newnflp->nfl_start > nflp->nfl_start) && (newnflp->nfl_end < nflp->nfl_end)) {
3885 /* We're replacing a range in the middle of a lock. */
3886 /* The current lock will be split into two locks. */
3887 /* Update locks and insert new lock after current lock. */
6d2010ae 3888 nflp2->nfl_flags |= (nflp->nfl_flags & (NFS_FILE_LOCK_STYLE_MASK|NFS_FILE_LOCK_DELEGATED));
b0d623f7
A
3889 nflp2->nfl_type = nflp->nfl_type;
3890 nflp2->nfl_start = newnflp->nfl_end + 1;
3891 nflp2->nfl_end = nflp->nfl_end;
3892 nflp->nfl_end = newnflp->nfl_start - 1;
3893 TAILQ_INSERT_AFTER(&np->n_locks, nflp, nflp2, nfl_link);
3894 nfs_lock_owner_insert_held_lock(nlop, nflp2);
3895 nextnflp = nflp2;
3896 nflp2 = NULL;
3897 } else if (newnflp->nfl_start > nflp->nfl_start) {
3898 /* We're replacing the end of a lock. */
3899 nflp->nfl_end = newnflp->nfl_start - 1;
3900 } else if (newnflp->nfl_end < nflp->nfl_end) {
3901 /* We're replacing the start of a lock. */
3902 nflp->nfl_start = newnflp->nfl_end + 1;
3903 }
3904 if (nflp->nfl_blockcnt) {
3905 /* wake up anyone blocked on this lock */
3906 wakeup(nflp);
3907 } else if (nflp->nfl_flags & NFS_FILE_LOCK_DEAD) {
3908 /* remove nflp from lock list and destroy */
3909 TAILQ_REMOVE(&np->n_locks, nflp, nfl_link);
3910 nfs_file_lock_destroy(nflp);
3911 }
3912 }
3913
3914 nfs_lock_owner_insert_held_lock(nlop, newnflp);
3915
3916 /*
3917 * POSIX locks should be coalesced when possible.
3918 */
3919 if ((style == NFS_FILE_LOCK_STYLE_POSIX) && (nofp->nof_flags & NFS_OPEN_FILE_POSIXLOCK)) {
3920 /*
3921 * Walk through the lock queue and check each of our held locks with
3922 * the previous and next locks in the lock owner's "held lock list".
3923 * If the two locks can be coalesced, we merge the current lock into
3924 * the other (previous or next) lock. Merging this way makes sure that
3925 * lock ranges are always merged forward in the lock queue. This is
3926 * important because anyone blocked on the lock being "merged away"
3927 * will still need to block on that range and it will simply continue
3928 * checking locks that are further down the list.
3929 */
3930 TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) {
3931 if (nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED|NFS_FILE_LOCK_DEAD))
3932 continue;
3933 if (nflp->nfl_owner != nlop)
3934 continue;
3935 if ((nflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) != NFS_FILE_LOCK_STYLE_POSIX)
3936 continue;
3937 if (((coalnflp = TAILQ_PREV(nflp, nfs_file_lock_queue, nfl_lolink))) &&
3938 ((coalnflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) == NFS_FILE_LOCK_STYLE_POSIX) &&
3939 (coalnflp->nfl_type == nflp->nfl_type) &&
3940 (coalnflp->nfl_end == (nflp->nfl_start - 1))) {
3941 coalnflp->nfl_end = nflp->nfl_end;
3942 nflp->nfl_flags |= NFS_FILE_LOCK_DEAD;
3943 lck_mtx_lock(&nlop->nlo_lock);
3944 TAILQ_REMOVE(&nlop->nlo_locks, nflp, nfl_lolink);
3945 lck_mtx_unlock(&nlop->nlo_lock);
3946 } else if (((coalnflp = TAILQ_NEXT(nflp, nfl_lolink))) &&
3947 ((coalnflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) == NFS_FILE_LOCK_STYLE_POSIX) &&
3948 (coalnflp->nfl_type == nflp->nfl_type) &&
3949 (coalnflp->nfl_start == (nflp->nfl_end + 1))) {
3950 coalnflp->nfl_start = nflp->nfl_start;
3951 nflp->nfl_flags |= NFS_FILE_LOCK_DEAD;
3952 lck_mtx_lock(&nlop->nlo_lock);
3953 TAILQ_REMOVE(&nlop->nlo_locks, nflp, nfl_lolink);
3954 lck_mtx_unlock(&nlop->nlo_lock);
3955 }
3956 if (!(nflp->nfl_flags & NFS_FILE_LOCK_DEAD))
3957 continue;
3958 if (nflp->nfl_blockcnt) {
3959 /* wake up anyone blocked on this lock */
3960 wakeup(nflp);
3961 } else {
3962 /* remove nflp from lock list and destroy */
3963 TAILQ_REMOVE(&np->n_locks, nflp, nfl_link);
3964 nfs_file_lock_destroy(nflp);
3965 }
3966 }
3967 }
3968
3969 lck_mtx_unlock(&np->n_openlock);
3970 nfs_open_state_clear_busy(np);
3971 nfs_mount_state_in_use_end(nmp, error);
3972
3973 if (nflp2)
3974 nfs_file_lock_destroy(nflp2);
3975 return (error);
3976}
3977
6d2010ae
A
3978/*
3979 * Release all (same style) locks within the given range.
3980 */
b0d623f7 3981int
6d2010ae 3982nfs_advlock_unlock(
b0d623f7
A
3983 nfsnode_t np,
3984 struct nfs_open_file *nofp,
3985 struct nfs_lock_owner *nlop,
3986 uint64_t start,
3987 uint64_t end,
3988 int style,
3989 vfs_context_t ctx)
3990{
3991 struct nfsmount *nmp;
3992 struct nfs_file_lock *nflp, *nextnflp, *newnflp = NULL;
3993 int error = 0, willsplit = 0, send_unlock_rpcs = 1;
3994
3995 nmp = NFSTONMP(np);
fe8ab488 3996 if (nfs_mount_gone(nmp))
b0d623f7
A
3997 return (ENXIO);
3998
3999restart:
6d2010ae 4000 if ((error = nfs_mount_state_in_use_start(nmp, NULL)))
b0d623f7
A
4001 return (error);
4002 if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN) {
4003 nfs_mount_state_in_use_end(nmp, 0);
6d2010ae
A
4004 error = nfs4_reopen(nofp, NULL);
4005 if (error)
4006 return (error);
b0d623f7
A
4007 goto restart;
4008 }
6d2010ae 4009 if ((error = nfs_open_state_set_busy(np, NULL))) {
b0d623f7
A
4010 nfs_mount_state_in_use_end(nmp, error);
4011 return (error);
4012 }
4013
4014 lck_mtx_lock(&np->n_openlock);
4015 if ((start > 0) && (end < UINT64_MAX) && !willsplit) {
4016 /*
4017 * We may need to allocate a new lock if an existing lock gets split.
4018 * So, we first scan the list to check for a split, and if there's
4019 * going to be one, we'll allocate one now.
4020 */
4021 TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) {
4022 if (nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED|NFS_FILE_LOCK_DEAD))
4023 continue;
4024 if (nflp->nfl_owner != nlop)
4025 continue;
4026 if ((nflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) != style)
4027 continue;
4028 if ((start > nflp->nfl_end) || (end < nflp->nfl_start))
4029 continue;
4030 if ((start > nflp->nfl_start) && (end < nflp->nfl_end)) {
4031 willsplit = 1;
4032 break;
4033 }
4034 }
4035 if (willsplit) {
4036 lck_mtx_unlock(&np->n_openlock);
4037 nfs_open_state_clear_busy(np);
4038 nfs_mount_state_in_use_end(nmp, 0);
4039 newnflp = nfs_file_lock_alloc(nlop);
4040 if (!newnflp)
4041 return (ENOMEM);
4042 goto restart;
4043 }
4044 }
4045
4046 /*
4047 * Free all of our locks in the given range.
4048 *
4049 * Note that this process requires sending requests to the server.
4050 * Because of this, we will release the n_openlock while performing
4051 * the unlock RPCs. The N_OPENBUSY state keeps the state of *held*
4052 * locks from changing underneath us. However, other entries in the
4053 * list may be removed. So we need to be careful walking the list.
4054 */
4055
4056 /*
4057 * Don't unlock ranges that are held by other-style locks.
4058 * If style is posix, don't send any unlock rpcs if flock is held.
4059 * If we unlock an flock, don't send unlock rpcs for any posix-style
4060 * ranges held - instead send unlocks for the ranges not held.
4061 */
4062 if ((style == NFS_FILE_LOCK_STYLE_POSIX) &&
4063 ((nflp = TAILQ_FIRST(&nlop->nlo_locks))) &&
4064 ((nflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) == NFS_FILE_LOCK_STYLE_FLOCK))
4065 send_unlock_rpcs = 0;
4066 if ((style == NFS_FILE_LOCK_STYLE_FLOCK) &&
4067 ((nflp = TAILQ_FIRST(&nlop->nlo_locks))) &&
4068 ((nflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) == NFS_FILE_LOCK_STYLE_FLOCK) &&
4069 ((nflp = TAILQ_NEXT(nflp, nfl_lolink))) &&
4070 ((nflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) == NFS_FILE_LOCK_STYLE_POSIX)) {
4071 uint64_t s = 0;
4072 int type = TAILQ_FIRST(&nlop->nlo_locks)->nfl_type;
6d2010ae
A
4073 int delegated = (TAILQ_FIRST(&nlop->nlo_locks)->nfl_flags & NFS_FILE_LOCK_DELEGATED);
4074 while (!delegated && nflp) {
b0d623f7
A
4075 if ((nflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) == NFS_FILE_LOCK_STYLE_POSIX) {
4076 /* unlock the range preceding this lock */
4077 lck_mtx_unlock(&np->n_openlock);
6d2010ae
A
4078 error = nmp->nm_funcs->nf_unlock_rpc(np, nlop, type, s, nflp->nfl_start-1, 0,
4079 vfs_context_thread(ctx), vfs_context_ucred(ctx));
b0d623f7
A
4080 if (nfs_mount_state_error_should_restart(error)) {
4081 nfs_open_state_clear_busy(np);
4082 nfs_mount_state_in_use_end(nmp, error);
4083 goto restart;
4084 }
4085 lck_mtx_lock(&np->n_openlock);
4086 if (error)
4087 goto out;
4088 s = nflp->nfl_end+1;
4089 }
4090 nflp = TAILQ_NEXT(nflp, nfl_lolink);
4091 }
6d2010ae
A
4092 if (!delegated) {
4093 lck_mtx_unlock(&np->n_openlock);
4094 error = nmp->nm_funcs->nf_unlock_rpc(np, nlop, type, s, end, 0,
4095 vfs_context_thread(ctx), vfs_context_ucred(ctx));
4096 if (nfs_mount_state_error_should_restart(error)) {
4097 nfs_open_state_clear_busy(np);
4098 nfs_mount_state_in_use_end(nmp, error);
4099 goto restart;
4100 }
4101 lck_mtx_lock(&np->n_openlock);
4102 if (error)
4103 goto out;
b0d623f7 4104 }
b0d623f7
A
4105 send_unlock_rpcs = 0;
4106 }
4107
4108 TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) {
4109 if (nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED|NFS_FILE_LOCK_DEAD))
4110 continue;
4111 if (nflp->nfl_owner != nlop)
4112 continue;
4113 if ((nflp->nfl_flags & NFS_FILE_LOCK_STYLE_MASK) != style)
4114 continue;
4115 if ((start > nflp->nfl_end) || (end < nflp->nfl_start))
4116 continue;
4117 /* here's one to unlock */
4118 if ((start <= nflp->nfl_start) && (end >= nflp->nfl_end)) {
4119 /* The entire lock is being unlocked. */
6d2010ae 4120 if (send_unlock_rpcs && !(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED)) {
b0d623f7 4121 lck_mtx_unlock(&np->n_openlock);
6d2010ae
A
4122 error = nmp->nm_funcs->nf_unlock_rpc(np, nlop, nflp->nfl_type, nflp->nfl_start, nflp->nfl_end, 0,
4123 vfs_context_thread(ctx), vfs_context_ucred(ctx));
b0d623f7
A
4124 if (nfs_mount_state_error_should_restart(error)) {
4125 nfs_open_state_clear_busy(np);
4126 nfs_mount_state_in_use_end(nmp, error);
4127 goto restart;
4128 }
4129 lck_mtx_lock(&np->n_openlock);
4130 }
4131 nextnflp = TAILQ_NEXT(nflp, nfl_link);
4132 if (error)
4133 break;
4134 nflp->nfl_flags |= NFS_FILE_LOCK_DEAD;
4135 lck_mtx_lock(&nlop->nlo_lock);
4136 TAILQ_REMOVE(&nlop->nlo_locks, nflp, nfl_lolink);
4137 lck_mtx_unlock(&nlop->nlo_lock);
4138 /* lock will be destroyed below, if no waiters */
4139 } else if ((start > nflp->nfl_start) && (end < nflp->nfl_end)) {
4140 /* We're unlocking a range in the middle of a lock. */
4141 /* The current lock will be split into two locks. */
6d2010ae 4142 if (send_unlock_rpcs && !(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED)) {
b0d623f7 4143 lck_mtx_unlock(&np->n_openlock);
6d2010ae
A
4144 error = nmp->nm_funcs->nf_unlock_rpc(np, nlop, nflp->nfl_type, start, end, 0,
4145 vfs_context_thread(ctx), vfs_context_ucred(ctx));
b0d623f7
A
4146 if (nfs_mount_state_error_should_restart(error)) {
4147 nfs_open_state_clear_busy(np);
4148 nfs_mount_state_in_use_end(nmp, error);
4149 goto restart;
4150 }
4151 lck_mtx_lock(&np->n_openlock);
4152 }
4153 if (error)
4154 break;
4155 /* update locks and insert new lock after current lock */
6d2010ae 4156 newnflp->nfl_flags |= (nflp->nfl_flags & (NFS_FILE_LOCK_STYLE_MASK|NFS_FILE_LOCK_DELEGATED));
b0d623f7
A
4157 newnflp->nfl_type = nflp->nfl_type;
4158 newnflp->nfl_start = end + 1;
4159 newnflp->nfl_end = nflp->nfl_end;
4160 nflp->nfl_end = start - 1;
4161 TAILQ_INSERT_AFTER(&np->n_locks, nflp, newnflp, nfl_link);
4162 nfs_lock_owner_insert_held_lock(nlop, newnflp);
4163 nextnflp = newnflp;
4164 newnflp = NULL;
4165 } else if (start > nflp->nfl_start) {
4166 /* We're unlocking the end of a lock. */
6d2010ae 4167 if (send_unlock_rpcs && !(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED)) {
b0d623f7 4168 lck_mtx_unlock(&np->n_openlock);
6d2010ae
A
4169 error = nmp->nm_funcs->nf_unlock_rpc(np, nlop, nflp->nfl_type, start, nflp->nfl_end, 0,
4170 vfs_context_thread(ctx), vfs_context_ucred(ctx));
b0d623f7
A
4171 if (nfs_mount_state_error_should_restart(error)) {
4172 nfs_open_state_clear_busy(np);
4173 nfs_mount_state_in_use_end(nmp, error);
4174 goto restart;
4175 }
4176 lck_mtx_lock(&np->n_openlock);
4177 }
4178 nextnflp = TAILQ_NEXT(nflp, nfl_link);
4179 if (error)
4180 break;
4181 nflp->nfl_end = start - 1;
4182 } else if (end < nflp->nfl_end) {
4183 /* We're unlocking the start of a lock. */
6d2010ae 4184 if (send_unlock_rpcs && !(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED)) {
b0d623f7 4185 lck_mtx_unlock(&np->n_openlock);
6d2010ae
A
4186 error = nmp->nm_funcs->nf_unlock_rpc(np, nlop, nflp->nfl_type, nflp->nfl_start, end, 0,
4187 vfs_context_thread(ctx), vfs_context_ucred(ctx));
b0d623f7
A
4188 if (nfs_mount_state_error_should_restart(error)) {
4189 nfs_open_state_clear_busy(np);
4190 nfs_mount_state_in_use_end(nmp, error);
4191 goto restart;
4192 }
4193 lck_mtx_lock(&np->n_openlock);
4194 }
4195 nextnflp = TAILQ_NEXT(nflp, nfl_link);
4196 if (error)
4197 break;
4198 nflp->nfl_start = end + 1;
4199 }
4200 if (nflp->nfl_blockcnt) {
4201 /* wake up anyone blocked on this lock */
4202 wakeup(nflp);
4203 } else if (nflp->nfl_flags & NFS_FILE_LOCK_DEAD) {
4204 /* remove nflp from lock list and destroy */
4205 TAILQ_REMOVE(&np->n_locks, nflp, nfl_link);
4206 nfs_file_lock_destroy(nflp);
4207 }
4208 }
4209out:
4210 lck_mtx_unlock(&np->n_openlock);
4211 nfs_open_state_clear_busy(np);
4212 nfs_mount_state_in_use_end(nmp, 0);
4213
4214 if (newnflp)
4215 nfs_file_lock_destroy(newnflp);
4216 return (error);
4217}
4218
4219/*
4220 * NFSv4 advisory file locking
4221 */
4222int
6d2010ae 4223nfs_vnop_advlock(
b0d623f7
A
4224 struct vnop_advlock_args /* {
4225 struct vnodeop_desc *a_desc;
4226 vnode_t a_vp;
4227 caddr_t a_id;
4228 int a_op;
4229 struct flock *a_fl;
4230 int a_flags;
4231 vfs_context_t a_context;
4232 } */ *ap)
4233{
4234 vnode_t vp = ap->a_vp;
4235 nfsnode_t np = VTONFS(ap->a_vp);
4236 struct flock *fl = ap->a_fl;
4237 int op = ap->a_op;
4238 int flags = ap->a_flags;
4239 vfs_context_t ctx = ap->a_context;
4240 struct nfsmount *nmp;
b0d623f7
A
4241 struct nfs_open_owner *noop = NULL;
4242 struct nfs_open_file *nofp = NULL;
4243 struct nfs_lock_owner *nlop = NULL;
4244 off_t lstart;
4245 uint64_t start, end;
4246 int error = 0, modified, style;
6d2010ae 4247 enum vtype vtype;
b0d623f7
A
4248#define OFF_MAX QUAD_MAX
4249
4250 nmp = VTONMP(ap->a_vp);
fe8ab488 4251 if (nfs_mount_gone(nmp))
b0d623f7 4252 return (ENXIO);
6d2010ae
A
4253 lck_mtx_lock(&nmp->nm_lock);
4254 if ((nmp->nm_vers <= NFS_VER3) && (nmp->nm_lockmode == NFS_LOCK_MODE_DISABLED)) {
4255 lck_mtx_unlock(&nmp->nm_lock);
4256 return (ENOTSUP);
4257 }
4258 lck_mtx_unlock(&nmp->nm_lock);
b0d623f7 4259
6d2010ae
A
4260 if (np->n_flag & NREVOKE)
4261 return (EIO);
4262 vtype = vnode_vtype(ap->a_vp);
4263 if (vtype == VDIR) /* ignore lock requests on directories */
4264 return (0);
4265 if (vtype != VREG) /* anything other than regular files is invalid */
4266 return (EINVAL);
4267
4268 /* Convert the flock structure into a start and end. */
b0d623f7
A
4269 switch (fl->l_whence) {
4270 case SEEK_SET:
4271 case SEEK_CUR:
4272 /*
4273 * Caller is responsible for adding any necessary offset
4274 * to fl->l_start when SEEK_CUR is used.
4275 */
4276 lstart = fl->l_start;
4277 break;
4278 case SEEK_END:
4279 /* need to flush, and refetch attributes to make */
4280 /* sure we have the correct end of file offset */
4281 if ((error = nfs_node_lock(np)))
4282 return (error);
4283 modified = (np->n_flag & NMODIFIED);
4284 nfs_node_unlock(np);
4285 if (modified && ((error = nfs_vinvalbuf(vp, V_SAVE, ctx, 1))))
4286 return (error);
6d2010ae 4287 if ((error = nfs_getattr(np, NULL, ctx, NGA_UNCACHED)))
b0d623f7
A
4288 return (error);
4289 nfs_data_lock(np, NFS_DATA_LOCK_SHARED);
4290 if ((np->n_size > OFF_MAX) ||
4291 ((fl->l_start > 0) && (np->n_size > (u_quad_t)(OFF_MAX - fl->l_start))))
4292 error = EOVERFLOW;
4293 lstart = np->n_size + fl->l_start;
4294 nfs_data_unlock(np);
4295 if (error)
4296 return (error);
4297 break;
4298 default:
4299 return (EINVAL);
4300 }
4301 if (lstart < 0)
4302 return (EINVAL);
4303 start = lstart;
4304 if (fl->l_len == 0) {
4305 end = UINT64_MAX;
4306 } else if (fl->l_len > 0) {
4307 if ((fl->l_len - 1) > (OFF_MAX - lstart))
4308 return (EOVERFLOW);
4309 end = start - 1 + fl->l_len;
4310 } else { /* l_len is negative */
4311 if ((lstart + fl->l_len) < 0)
4312 return (EINVAL);
4313 end = start - 1;
4314 start += fl->l_len;
4315 }
6d2010ae
A
4316 if ((nmp->nm_vers == NFS_VER2) && ((start > INT32_MAX) || (fl->l_len && (end > INT32_MAX))))
4317 return (EINVAL);
b0d623f7
A
4318
4319 style = (flags & F_FLOCK) ? NFS_FILE_LOCK_STYLE_FLOCK : NFS_FILE_LOCK_STYLE_POSIX;
4320 if ((style == NFS_FILE_LOCK_STYLE_FLOCK) && ((start != 0) || (end != UINT64_MAX)))
4321 return (EINVAL);
4322
4323 /* find the lock owner, alloc if not unlock */
4324 nlop = nfs_lock_owner_find(np, vfs_context_proc(ctx), (op != F_UNLCK));
4325 if (!nlop) {
4326 error = (op == F_UNLCK) ? 0 : ENOMEM;
4327 if (error)
6d2010ae 4328 NP(np, "nfs_vnop_advlock: no lock owner, error %d", error);
b0d623f7
A
4329 goto out;
4330 }
4331
4332 if (op == F_GETLK) {
6d2010ae 4333 error = nfs_advlock_getlock(np, nlop, fl, start, end, ctx);
b0d623f7
A
4334 } else {
4335 /* find the open owner */
4336 noop = nfs_open_owner_find(nmp, vfs_context_ucred(ctx), 0);
4337 if (!noop) {
6d2010ae 4338 NP(np, "nfs_vnop_advlock: no open owner %d", kauth_cred_getuid(vfs_context_ucred(ctx)));
b0d623f7
A
4339 error = EPERM;
4340 goto out;
4341 }
4342 /* find the open file */
4343restart:
4344 error = nfs_open_file_find(np, noop, &nofp, 0, 0, 0);
4345 if (error)
4346 error = EBADF;
4347 if (!error && (nofp->nof_flags & NFS_OPEN_FILE_LOST)) {
6d2010ae 4348 NP(np, "nfs_vnop_advlock: LOST %d", kauth_cred_getuid(nofp->nof_owner->noo_cred));
b0d623f7
A
4349 error = EIO;
4350 }
4351 if (!error && (nofp->nof_flags & NFS_OPEN_FILE_REOPEN)) {
6d2010ae 4352 error = nfs4_reopen(nofp, ((op == F_UNLCK) ? NULL : vfs_context_thread(ctx)));
b0d623f7 4353 nofp = NULL;
6d2010ae
A
4354 if (!error)
4355 goto restart;
b0d623f7
A
4356 }
4357 if (error) {
6d2010ae 4358 NP(np, "nfs_vnop_advlock: no open file %d, %d", error, kauth_cred_getuid(noop->noo_cred));
b0d623f7
A
4359 goto out;
4360 }
4361 if (op == F_UNLCK) {
6d2010ae 4362 error = nfs_advlock_unlock(np, nofp, nlop, start, end, style, ctx);
b0d623f7
A
4363 } else if ((op == F_SETLK) || (op == F_SETLKW)) {
4364 if ((op == F_SETLK) && (flags & F_WAIT))
4365 op = F_SETLKW;
6d2010ae 4366 error = nfs_advlock_setlock(np, nofp, nlop, op, start, end, style, fl->l_type, ctx);
b0d623f7
A
4367 } else {
4368 /* not getlk, unlock or lock? */
4369 error = EINVAL;
4370 }
4371 }
4372
4373out:
4374 if (nlop)
4375 nfs_lock_owner_rele(nlop);
4376 if (noop)
4377 nfs_open_owner_rele(noop);
4378 return (error);
4379}
4380
4381/*
4382 * Check if an open owner holds any locks on a file.
4383 */
4384int
6d2010ae 4385nfs_check_for_locks(struct nfs_open_owner *noop, struct nfs_open_file *nofp)
b0d623f7
A
4386{
4387 struct nfs_lock_owner *nlop;
4388
4389 TAILQ_FOREACH(nlop, &nofp->nof_np->n_lock_owners, nlo_link) {
4390 if (nlop->nlo_open_owner != noop)
4391 continue;
4392 if (!TAILQ_EMPTY(&nlop->nlo_locks))
4393 break;
4394 }
4395 return (nlop ? 1 : 0);
4396}
4397
4398/*
4399 * Reopen simple (no deny, no locks) open state that was lost.
4400 */
6d2010ae 4401int
b0d623f7
A
4402nfs4_reopen(struct nfs_open_file *nofp, thread_t thd)
4403{
4404 struct nfs_open_owner *noop = nofp->nof_owner;
4405 struct nfsmount *nmp = NFSTONMP(nofp->nof_np);
6d2010ae
A
4406 nfsnode_t np = nofp->nof_np;
4407 vnode_t vp = NFSTOV(np);
b0d623f7
A
4408 vnode_t dvp = NULL;
4409 struct componentname cn;
4410 const char *vname = NULL;
6d2010ae 4411 const char *name = NULL;
b0d623f7
A
4412 size_t namelen;
4413 char smallname[128];
4414 char *filename = NULL;
6d2010ae 4415 int error = 0, done = 0, slpflag = NMFLAG(nmp, INTR) ? PCATCH : 0;
b0d623f7
A
4416 struct timespec ts = { 1, 0 };
4417
4418 lck_mtx_lock(&nofp->nof_lock);
4419 while (nofp->nof_flags & NFS_OPEN_FILE_REOPENING) {
4420 if ((error = nfs_sigintr(nmp, NULL, thd, 0)))
4421 break;
4422 msleep(&nofp->nof_flags, &nofp->nof_lock, slpflag|(PZERO-1), "nfsreopenwait", &ts);
6d2010ae 4423 slpflag = 0;
b0d623f7 4424 }
6d2010ae 4425 if (error || !(nofp->nof_flags & NFS_OPEN_FILE_REOPEN)) {
b0d623f7 4426 lck_mtx_unlock(&nofp->nof_lock);
6d2010ae 4427 return (error);
b0d623f7
A
4428 }
4429 nofp->nof_flags |= NFS_OPEN_FILE_REOPENING;
4430 lck_mtx_unlock(&nofp->nof_lock);
4431
6d2010ae
A
4432 nfs_node_lock_force(np);
4433 if ((vnode_vtype(vp) != VDIR) && np->n_sillyrename) {
4434 /*
4435 * The node's been sillyrenamed, so we need to use
4436 * the sillyrename directory/name to do the open.
4437 */
4438 struct nfs_sillyrename *nsp = np->n_sillyrename;
4439 dvp = NFSTOV(nsp->nsr_dnp);
4440 if ((error = vnode_get(dvp))) {
4441 nfs_node_unlock(np);
4442 goto out;
4443 }
4444 name = nsp->nsr_name;
4445 } else {
4446 /*
4447 * [sigh] We can't trust VFS to get the parent right for named
4448 * attribute nodes. (It likes to reparent the nodes after we've
4449 * created them.) Luckily we can probably get the right parent
4450 * from the n_parent we have stashed away.
4451 */
4452 if ((np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR) &&
4453 (((dvp = np->n_parent)) && (error = vnode_get(dvp))))
4454 dvp = NULL;
4455 if (!dvp)
4456 dvp = vnode_getparent(vp);
4457 vname = vnode_getname(vp);
4458 if (!dvp || !vname) {
4459 if (!error)
4460 error = EIO;
4461 nfs_node_unlock(np);
4462 goto out;
4463 }
4464 name = vname;
b0d623f7
A
4465 }
4466 filename = &smallname[0];
6d2010ae 4467 namelen = snprintf(filename, sizeof(smallname), "%s", name);
b0d623f7 4468 if (namelen >= sizeof(smallname)) {
6d2010ae 4469 MALLOC(filename, char *, namelen+1, M_TEMP, M_WAITOK);
b0d623f7
A
4470 if (!filename) {
4471 error = ENOMEM;
4472 goto out;
4473 }
6d2010ae 4474 snprintf(filename, namelen+1, "%s", name);
b0d623f7 4475 }
6d2010ae 4476 nfs_node_unlock(np);
b0d623f7
A
4477 bzero(&cn, sizeof(cn));
4478 cn.cn_nameptr = filename;
4479 cn.cn_namelen = namelen;
4480
4481restart:
4482 done = 0;
6d2010ae 4483 if ((error = nfs_mount_state_in_use_start(nmp, thd)))
b0d623f7
A
4484 goto out;
4485
4486 if (nofp->nof_rw)
4487 error = nfs4_open_reopen_rpc(nofp, thd, noop->noo_cred, &cn, dvp, &vp, NFS_OPEN_SHARE_ACCESS_BOTH, NFS_OPEN_SHARE_DENY_NONE);
4488 if (!error && nofp->nof_w)
4489 error = nfs4_open_reopen_rpc(nofp, thd, noop->noo_cred, &cn, dvp, &vp, NFS_OPEN_SHARE_ACCESS_WRITE, NFS_OPEN_SHARE_DENY_NONE);
4490 if (!error && nofp->nof_r)
4491 error = nfs4_open_reopen_rpc(nofp, thd, noop->noo_cred, &cn, dvp, &vp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE);
4492
4493 if (nfs_mount_state_in_use_end(nmp, error)) {
4494 if (error == NFSERR_GRACE)
4495 goto restart;
6d2010ae
A
4496 printf("nfs4_reopen: RPC failed, error %d, lost %d, %s\n", error,
4497 (nofp->nof_flags & NFS_OPEN_FILE_LOST) ? 1 : 0, name ? name : "???");
b0d623f7
A
4498 error = 0;
4499 goto out;
4500 }
4501 done = 1;
4502out:
6d2010ae
A
4503 if (error && (error != EINTR) && (error != ERESTART))
4504 nfs_revoke_open_state_for_node(np);
b0d623f7
A
4505 lck_mtx_lock(&nofp->nof_lock);
4506 nofp->nof_flags &= ~NFS_OPEN_FILE_REOPENING;
b0d623f7
A
4507 if (done)
4508 nofp->nof_flags &= ~NFS_OPEN_FILE_REOPEN;
6d2010ae
A
4509 else if (error)
4510 printf("nfs4_reopen: failed, error %d, lost %d, %s\n", error,
4511 (nofp->nof_flags & NFS_OPEN_FILE_LOST) ? 1 : 0, name ? name : "???");
b0d623f7
A
4512 lck_mtx_unlock(&nofp->nof_lock);
4513 if (filename && (filename != &smallname[0]))
4514 FREE(filename, M_TEMP);
4515 if (vname)
4516 vnode_putname(vname);
4517 if (dvp != NULLVP)
4518 vnode_put(dvp);
6d2010ae 4519 return (error);
b0d623f7
A
4520}
4521
4522/*
4523 * Send a normal OPEN RPC to open/create a file.
4524 */
4525int
4526nfs4_open_rpc(
4527 struct nfs_open_file *nofp,
4528 vfs_context_t ctx,
4529 struct componentname *cnp,
4530 struct vnode_attr *vap,
4531 vnode_t dvp,
4532 vnode_t *vpp,
4533 int create,
4534 int share_access,
4535 int share_deny)
4536{
4537 return (nfs4_open_rpc_internal(nofp, ctx, vfs_context_thread(ctx), vfs_context_ucred(ctx),
4538 cnp, vap, dvp, vpp, create, share_access, share_deny));
4539}
4540
4541/*
4542 * Send an OPEN RPC to reopen a file.
4543 */
4544int
4545nfs4_open_reopen_rpc(
4546 struct nfs_open_file *nofp,
4547 thread_t thd,
4548 kauth_cred_t cred,
4549 struct componentname *cnp,
4550 vnode_t dvp,
4551 vnode_t *vpp,
4552 int share_access,
4553 int share_deny)
4554{
6d2010ae
A
4555 return (nfs4_open_rpc_internal(nofp, NULL, thd, cred, cnp, NULL, dvp, vpp, NFS_OPEN_NOCREATE, share_access, share_deny));
4556}
4557
4558/*
4559 * Send an OPEN_CONFIRM RPC to confirm an OPEN.
4560 */
4561int
4562nfs4_open_confirm_rpc(
4563 struct nfsmount *nmp,
4564 nfsnode_t dnp,
4565 u_char *fhp,
4566 int fhlen,
4567 struct nfs_open_owner *noop,
4568 nfs_stateid *sid,
4569 thread_t thd,
4570 kauth_cred_t cred,
4571 struct nfs_vattr *nvap,
4572 uint64_t *xidp)
4573{
4574 struct nfsm_chain nmreq, nmrep;
4575 int error = 0, status, numops;
4576 struct nfsreq_secinfo_args si;
4577
4578 NFSREQ_SECINFO_SET(&si, dnp, NULL, 0, NULL, 0);
4579 nfsm_chain_null(&nmreq);
4580 nfsm_chain_null(&nmrep);
4581
4582 // PUTFH, OPEN_CONFIRM, GETATTR
4583 numops = 3;
4584 nfsm_chain_build_alloc_init(error, &nmreq, 23 * NFSX_UNSIGNED);
4585 nfsm_chain_add_compound_header(error, &nmreq, "open_confirm", numops);
4586 numops--;
4587 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
4588 nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, fhp, fhlen);
4589 numops--;
4590 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPEN_CONFIRM);
4591 nfsm_chain_add_stateid(error, &nmreq, sid);
4592 nfsm_chain_add_32(error, &nmreq, noop->noo_seqid);
4593 numops--;
4594 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
4595 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, dnp);
4596 nfsm_chain_build_done(error, &nmreq);
4597 nfsm_assert(error, (numops == 0), EPROTO);
4598 nfsmout_if(error);
4599 error = nfs_request2(dnp, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, R_NOINTR, &nmrep, xidp, &status);
4600
4601 nfsm_chain_skip_tag(error, &nmrep);
4602 nfsm_chain_get_32(error, &nmrep, numops);
4603 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
4604 nfsmout_if(error);
4605 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPEN_CONFIRM);
4606 nfs_owner_seqid_increment(noop, NULL, error);
4607 nfsm_chain_get_stateid(error, &nmrep, sid);
4608 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
4609 nfsmout_if(error);
4610 error = nfs4_parsefattr(&nmrep, NULL, nvap, NULL, NULL, NULL);
4611nfsmout:
4612 nfsm_chain_cleanup(&nmreq);
4613 nfsm_chain_cleanup(&nmrep);
4614 return (error);
b0d623f7
A
4615}
4616
4617/*
4618 * common OPEN RPC code
4619 *
4620 * If create is set, ctx must be passed in.
6d2010ae 4621 * Returns a node on success if no node passed in.
b0d623f7
A
4622 */
4623int
4624nfs4_open_rpc_internal(
4625 struct nfs_open_file *nofp,
4626 vfs_context_t ctx,
4627 thread_t thd,
4628 kauth_cred_t cred,
4629 struct componentname *cnp,
4630 struct vnode_attr *vap,
4631 vnode_t dvp,
4632 vnode_t *vpp,
4633 int create,
4634 int share_access,
4635 int share_deny)
4636{
4637 struct nfsmount *nmp;
4638 struct nfs_open_owner *noop = nofp->nof_owner;
6d2010ae 4639 struct nfs_vattr nvattr;
b0d623f7 4640 int error = 0, open_error = EIO, lockerror = ENOENT, busyerror = ENOENT, status;
6d2010ae 4641 int nfsvers, namedattrs, numops, exclusive = 0, gotuid, gotgid;
b0d623f7
A
4642 u_int64_t xid, savedxid = 0;
4643 nfsnode_t dnp = VTONFS(dvp);
4644 nfsnode_t np, newnp = NULL;
4645 vnode_t newvp = NULL;
4646 struct nfsm_chain nmreq, nmrep;
4647 uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen;
6d2010ae 4648 uint32_t rflags, delegation, recall;
b0d623f7
A
4649 struct nfs_stateid stateid, dstateid, *sid;
4650 fhandle_t fh;
6d2010ae 4651 struct nfsreq rq, *req = &rq;
b0d623f7 4652 struct nfs_dulookup dul;
6d2010ae
A
4653 char sbuf[64], *s;
4654 uint32_t ace_type, ace_flags, ace_mask, len, slen;
4655 struct kauth_ace ace;
4656 struct nfsreq_secinfo_args si;
b0d623f7
A
4657
4658 if (create && !ctx)
4659 return (EINVAL);
4660
4661 nmp = VTONMP(dvp);
fe8ab488 4662 if (nfs_mount_gone(nmp))
b0d623f7
A
4663 return (ENXIO);
4664 nfsvers = nmp->nm_vers;
6d2010ae
A
4665 namedattrs = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR);
4666 if (dnp->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
4667 return (EINVAL);
b0d623f7
A
4668
4669 np = *vpp ? VTONFS(*vpp) : NULL;
4670 if (create && vap) {
4671 exclusive = (vap->va_vaflags & VA_EXCLUSIVE);
4672 nfs_avoid_needless_id_setting_on_create(dnp, vap, ctx);
4673 gotuid = VATTR_IS_ACTIVE(vap, va_uid);
4674 gotgid = VATTR_IS_ACTIVE(vap, va_gid);
6d2010ae
A
4675 if (exclusive && (!VATTR_IS_ACTIVE(vap, va_access_time) || !VATTR_IS_ACTIVE(vap, va_modify_time)))
4676 vap->va_vaflags |= VA_UTIMES_NULL;
b0d623f7
A
4677 } else {
4678 exclusive = gotuid = gotgid = 0;
4679 }
4680 if (nofp) {
4681 sid = &nofp->nof_stateid;
4682 } else {
4683 stateid.seqid = stateid.other[0] = stateid.other[1] = stateid.other[2] = 0;
4684 sid = &stateid;
4685 }
4686
4687 if ((error = nfs_open_owner_set_busy(noop, thd)))
4688 return (error);
4689again:
6d2010ae
A
4690 rflags = delegation = recall = 0;
4691 ace.ace_flags = 0;
4692 s = sbuf;
4693 slen = sizeof(sbuf);
4694 NVATTR_INIT(&nvattr);
4695 NFSREQ_SECINFO_SET(&si, dnp, NULL, 0, cnp->cn_nameptr, cnp->cn_namelen);
b0d623f7
A
4696
4697 nfsm_chain_null(&nmreq);
4698 nfsm_chain_null(&nmrep);
4699
4700 // PUTFH, SAVEFH, OPEN(CREATE?), GETATTR(FH), RESTOREFH, GETATTR
4701 numops = 6;
4702 nfsm_chain_build_alloc_init(error, &nmreq, 53 * NFSX_UNSIGNED + cnp->cn_namelen);
4703 nfsm_chain_add_compound_header(error, &nmreq, create ? "create" : "open", numops);
4704 numops--;
4705 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
4706 nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
4707 numops--;
4708 nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
4709 numops--;
4710 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPEN);
4711 nfsm_chain_add_32(error, &nmreq, noop->noo_seqid);
4712 nfsm_chain_add_32(error, &nmreq, share_access);
4713 nfsm_chain_add_32(error, &nmreq, share_deny);
6d2010ae 4714 nfsm_chain_add_64(error, &nmreq, nmp->nm_clientid);
b0d623f7 4715 nfsm_chain_add_32(error, &nmreq, NFSX_UNSIGNED);
6d2010ae 4716 nfsm_chain_add_32(error, &nmreq, kauth_cred_getuid(noop->noo_cred));
b0d623f7
A
4717 nfsm_chain_add_32(error, &nmreq, create);
4718 if (create) {
4719 if (exclusive) {
4720 static uint32_t create_verf; // XXX need a better verifier
4721 create_verf++;
4722 nfsm_chain_add_32(error, &nmreq, NFS_CREATE_EXCLUSIVE);
4723 /* insert 64 bit verifier */
4724 nfsm_chain_add_32(error, &nmreq, create_verf);
4725 nfsm_chain_add_32(error, &nmreq, create_verf);
4726 } else {
4727 nfsm_chain_add_32(error, &nmreq, NFS_CREATE_UNCHECKED);
4728 nfsm_chain_add_fattr4(error, &nmreq, vap, nmp);
4729 }
4730 }
b0d623f7 4731 nfsm_chain_add_32(error, &nmreq, NFS_CLAIM_NULL);
6d2010ae 4732 nfsm_chain_add_name(error, &nmreq, cnp->cn_nameptr, cnp->cn_namelen, nmp);
b0d623f7
A
4733 numops--;
4734 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
4735 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
4736 NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
6d2010ae 4737 nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, np);
b0d623f7
A
4738 numops--;
4739 nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
4740 numops--;
4741 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 4742 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, dnp);
b0d623f7
A
4743 nfsm_chain_build_done(error, &nmreq);
4744 nfsm_assert(error, (numops == 0), EPROTO);
4745 if (!error)
4746 error = busyerror = nfs_node_set_busy(dnp, thd);
4747 nfsmout_if(error);
4748
6d2010ae 4749 if (create && !namedattrs)
b0d623f7
A
4750 nfs_dulookup_init(&dul, dnp, cnp->cn_nameptr, cnp->cn_namelen, ctx);
4751
6d2010ae 4752 error = nfs_request_async(dnp, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, R_NOINTR, NULL, &req);
b0d623f7 4753 if (!error) {
6d2010ae 4754 if (create && !namedattrs)
b0d623f7
A
4755 nfs_dulookup_start(&dul, dnp, ctx);
4756 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
4757 savedxid = xid;
4758 }
4759
6d2010ae 4760 if (create && !namedattrs)
b0d623f7
A
4761 nfs_dulookup_finish(&dul, dnp, ctx);
4762
4763 if ((lockerror = nfs_node_lock(dnp)))
4764 error = lockerror;
4765 nfsm_chain_skip_tag(error, &nmrep);
4766 nfsm_chain_get_32(error, &nmrep, numops);
4767 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
4768 nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
4769 nfsmout_if(error);
4770 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPEN);
4771 nfs_owner_seqid_increment(noop, NULL, error);
4772 nfsm_chain_get_stateid(error, &nmrep, sid);
4773 nfsm_chain_check_change_info(error, &nmrep, dnp);
4774 nfsm_chain_get_32(error, &nmrep, rflags);
4775 bmlen = NFS_ATTR_BITMAP_LEN;
4776 nfsm_chain_get_bitmap(error, &nmrep, bitmap, bmlen);
4777 nfsm_chain_get_32(error, &nmrep, delegation);
4778 if (!error)
4779 switch (delegation) {
4780 case NFS_OPEN_DELEGATE_NONE:
4781 break;
4782 case NFS_OPEN_DELEGATE_READ:
b0d623f7
A
4783 case NFS_OPEN_DELEGATE_WRITE:
4784 nfsm_chain_get_stateid(error, &nmrep, &dstateid);
4785 nfsm_chain_get_32(error, &nmrep, recall);
6d2010ae
A
4786 if (delegation == NFS_OPEN_DELEGATE_WRITE) // space (skip) XXX
4787 nfsm_chain_adv(error, &nmrep, 3 * NFSX_UNSIGNED);
4788 /* if we have any trouble accepting the ACE, just invalidate it */
4789 ace_type = ace_flags = ace_mask = len = 0;
4790 nfsm_chain_get_32(error, &nmrep, ace_type);
4791 nfsm_chain_get_32(error, &nmrep, ace_flags);
4792 nfsm_chain_get_32(error, &nmrep, ace_mask);
4793 nfsm_chain_get_32(error, &nmrep, len);
4794 ace.ace_flags = nfs4_ace_nfstype_to_vfstype(ace_type, &error);
4795 ace.ace_flags |= nfs4_ace_nfsflags_to_vfsflags(ace_flags);
4796 ace.ace_rights = nfs4_ace_nfsmask_to_vfsrights(ace_mask);
4797 if (!error && (len >= slen)) {
4798 MALLOC(s, char*, len+1, M_TEMP, M_WAITOK);
4799 if (s)
4800 slen = len+1;
4801 else
4802 ace.ace_flags = 0;
4803 }
4804 if (s)
4805 nfsm_chain_get_opaque(error, &nmrep, len, s);
4806 else
4807 nfsm_chain_adv(error, &nmrep, nfsm_rndup(len));
4808 if (!error && s) {
4809 s[len] = '\0';
4810 if (nfs4_id2guid(s, &ace.ace_applicable, (ace_flags & NFS_ACE_IDENTIFIER_GROUP)))
4811 ace.ace_flags = 0;
4812 }
4813 if (error || !s)
4814 ace.ace_flags = 0;
4815 if (s && (s != sbuf))
4816 FREE(s, M_TEMP);
b0d623f7
A
4817 break;
4818 default:
4819 error = EBADRPC;
4820 break;
4821 }
4822 /* At this point if we have no error, the object was created/opened. */
b0d623f7
A
4823 open_error = error;
4824 nfsmout_if(error);
6d2010ae 4825 if (create && vap && !exclusive)
b0d623f7
A
4826 nfs_vattr_set_supported(bitmap, vap);
4827 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
4828 nfsmout_if(error);
6d2010ae 4829 error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL, NULL);
b0d623f7
A
4830 nfsmout_if(error);
4831 if (!NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
6d2010ae 4832 printf("nfs: open/create didn't return filehandle? %s\n", cnp->cn_nameptr);
2d21ac55
A
4833 error = EBADRPC;
4834 goto nfsmout;
4835 }
b0d623f7
A
4836 if (!create && np && !NFS_CMPFH(np, fh.fh_data, fh.fh_len)) {
4837 // XXX for the open case, what if fh doesn't match the vnode we think we're opening?
6d2010ae
A
4838 // Solaris Named Attributes may do this due to a bug.... so don't warn for named attributes.
4839 if (!(np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR))
4840 NP(np, "nfs4_open_rpc: warning: file handle mismatch");
b0d623f7 4841 }
2d21ac55
A
4842 /* directory attributes: if we don't get them, make sure to invalidate */
4843 nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
4844 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 4845 nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, &xid);
2d21ac55
A
4846 if (error)
4847 NATTRINVALIDATE(dnp);
b0d623f7
A
4848 nfsmout_if(error);
4849
4850 if (rflags & NFS_OPEN_RESULT_LOCKTYPE_POSIX)
4851 nofp->nof_flags |= NFS_OPEN_FILE_POSIXLOCK;
4852
4853 if (rflags & NFS_OPEN_RESULT_CONFIRM) {
4854 nfs_node_unlock(dnp);
4855 lockerror = ENOENT;
6d2010ae
A
4856 NVATTR_CLEANUP(&nvattr);
4857 error = nfs4_open_confirm_rpc(nmp, dnp, fh.fh_data, fh.fh_len, noop, sid, thd, cred, &nvattr, &xid);
b0d623f7
A
4858 nfsmout_if(error);
4859 savedxid = xid;
4860 if ((lockerror = nfs_node_lock(dnp)))
4861 error = lockerror;
4862 }
4863
4864nfsmout:
4865 nfsm_chain_cleanup(&nmreq);
4866 nfsm_chain_cleanup(&nmrep);
4867
4868 if (!lockerror && create) {
4869 if (!open_error && (dnp->n_flag & NNEGNCENTRIES)) {
4870 dnp->n_flag &= ~NNEGNCENTRIES;
4871 cache_purge_negatives(dvp);
4872 }
4873 dnp->n_flag |= NMODIFIED;
4874 nfs_node_unlock(dnp);
4875 lockerror = ENOENT;
6d2010ae 4876 nfs_getattr(dnp, NULL, ctx, NGA_CACHED);
b0d623f7
A
4877 }
4878 if (!lockerror)
4879 nfs_node_unlock(dnp);
6d2010ae 4880 if (!error && !np && fh.fh_len) {
b0d623f7
A
4881 /* create the vnode with the filehandle and attributes */
4882 xid = savedxid;
6d2010ae 4883 error = nfs_nget(NFSTOMP(dnp), dnp, cnp, fh.fh_data, fh.fh_len, &nvattr, &xid, rq.r_auth, NG_MAKEENTRY, &newnp);
b0d623f7
A
4884 if (!error)
4885 newvp = NFSTOV(newnp);
4886 }
6d2010ae 4887 NVATTR_CLEANUP(&nvattr);
b0d623f7
A
4888 if (!busyerror)
4889 nfs_node_clear_busy(dnp);
4890 if ((delegation == NFS_OPEN_DELEGATE_READ) || (delegation == NFS_OPEN_DELEGATE_WRITE)) {
4891 if (!np)
4892 np = newnp;
4893 if (!error && np && !recall) {
4894 /* stuff the delegation state in the node */
4895 lck_mtx_lock(&np->n_openlock);
4896 np->n_openflags &= ~N_DELEG_MASK;
4897 np->n_openflags |= ((delegation == NFS_OPEN_DELEGATE_READ) ? N_DELEG_READ : N_DELEG_WRITE);
4898 np->n_dstateid = dstateid;
6d2010ae
A
4899 np->n_dace = ace;
4900 if (np->n_dlink.tqe_next == NFSNOLIST) {
4901 lck_mtx_lock(&nmp->nm_lock);
4902 if (np->n_dlink.tqe_next == NFSNOLIST)
4903 TAILQ_INSERT_TAIL(&nmp->nm_delegations, np, n_dlink);
4904 lck_mtx_unlock(&nmp->nm_lock);
4905 }
b0d623f7 4906 lck_mtx_unlock(&np->n_openlock);
6d2010ae
A
4907 } else {
4908 /* give the delegation back */
b0d623f7 4909 if (np) {
6d2010ae
A
4910 if (NFS_CMPFH(np, fh.fh_data, fh.fh_len)) {
4911 /* update delegation state and return it */
4912 lck_mtx_lock(&np->n_openlock);
4913 np->n_openflags &= ~N_DELEG_MASK;
4914 np->n_openflags |= ((delegation == NFS_OPEN_DELEGATE_READ) ? N_DELEG_READ : N_DELEG_WRITE);
4915 np->n_dstateid = dstateid;
4916 np->n_dace = ace;
4917 if (np->n_dlink.tqe_next == NFSNOLIST) {
4918 lck_mtx_lock(&nmp->nm_lock);
4919 if (np->n_dlink.tqe_next == NFSNOLIST)
4920 TAILQ_INSERT_TAIL(&nmp->nm_delegations, np, n_dlink);
4921 lck_mtx_unlock(&nmp->nm_lock);
4922 }
4923 lck_mtx_unlock(&np->n_openlock);
4924 /* don't need to send a separate delegreturn for fh */
4925 fh.fh_len = 0;
4926 }
4927 /* return np's current delegation */
4928 nfs4_delegation_return(np, 0, thd, cred);
b0d623f7 4929 }
6d2010ae
A
4930 if (fh.fh_len) /* return fh's delegation if it wasn't for np */
4931 nfs4_delegreturn_rpc(nmp, fh.fh_data, fh.fh_len, &dstateid, 0, thd, cred);
b0d623f7
A
4932 }
4933 }
4934 if (error) {
4935 if (exclusive && (error == NFSERR_NOTSUPP)) {
4936 exclusive = 0;
4937 goto again;
4938 }
4939 if (newvp) {
4940 nfs_node_unlock(newnp);
4941 vnode_put(newvp);
4942 }
4943 } else if (create) {
4944 nfs_node_unlock(newnp);
4945 if (exclusive) {
4946 error = nfs4_setattr_rpc(newnp, vap, ctx);
4947 if (error && (gotuid || gotgid)) {
4948 /* it's possible the server didn't like our attempt to set IDs. */
4949 /* so, let's try it again without those */
4950 VATTR_CLEAR_ACTIVE(vap, va_uid);
4951 VATTR_CLEAR_ACTIVE(vap, va_gid);
4952 error = nfs4_setattr_rpc(newnp, vap, ctx);
4953 }
4954 }
4955 if (error)
4956 vnode_put(newvp);
4957 else
4958 *vpp = newvp;
4959 }
4960 nfs_open_owner_clear_busy(noop);
4961 return (error);
4962}
4963
6d2010ae
A
4964
4965/*
4966 * Send an OPEN RPC to claim a delegated open for a file
4967 */
4968int
4969nfs4_claim_delegated_open_rpc(
4970 struct nfs_open_file *nofp,
4971 int share_access,
4972 int share_deny,
4973 int flags)
4974{
4975 struct nfsmount *nmp;
4976 struct nfs_open_owner *noop = nofp->nof_owner;
4977 struct nfs_vattr nvattr;
4978 int error = 0, lockerror = ENOENT, status;
4979 int nfsvers, numops;
4980 u_int64_t xid;
4981 nfsnode_t np = nofp->nof_np;
4982 struct nfsm_chain nmreq, nmrep;
4983 uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen;
4984 uint32_t rflags = 0, delegation, recall = 0;
4985 fhandle_t fh;
4986 struct nfs_stateid dstateid;
4987 char sbuf[64], *s = sbuf;
4988 uint32_t ace_type, ace_flags, ace_mask, len, slen = sizeof(sbuf);
4989 struct kauth_ace ace;
4990 vnode_t dvp = NULL;
4991 const char *vname = NULL;
4992 const char *name = NULL;
4993 size_t namelen;
4994 char smallname[128];
4995 char *filename = NULL;
4996 struct nfsreq_secinfo_args si;
4997
4998 nmp = NFSTONMP(np);
fe8ab488 4999 if (nfs_mount_gone(nmp))
6d2010ae
A
5000 return (ENXIO);
5001 nfsvers = nmp->nm_vers;
5002
5003 nfs_node_lock_force(np);
5004 if ((vnode_vtype(NFSTOV(np)) != VDIR) && np->n_sillyrename) {
5005 /*
5006 * The node's been sillyrenamed, so we need to use
5007 * the sillyrename directory/name to do the open.
5008 */
5009 struct nfs_sillyrename *nsp = np->n_sillyrename;
5010 dvp = NFSTOV(nsp->nsr_dnp);
5011 if ((error = vnode_get(dvp))) {
5012 nfs_node_unlock(np);
5013 goto out;
5014 }
5015 name = nsp->nsr_name;
5016 } else {
5017 /*
5018 * [sigh] We can't trust VFS to get the parent right for named
5019 * attribute nodes. (It likes to reparent the nodes after we've
5020 * created them.) Luckily we can probably get the right parent
5021 * from the n_parent we have stashed away.
5022 */
5023 if ((np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR) &&
5024 (((dvp = np->n_parent)) && (error = vnode_get(dvp))))
5025 dvp = NULL;
5026 if (!dvp)
5027 dvp = vnode_getparent(NFSTOV(np));
5028 vname = vnode_getname(NFSTOV(np));
5029 if (!dvp || !vname) {
5030 if (!error)
5031 error = EIO;
5032 nfs_node_unlock(np);
5033 goto out;
5034 }
5035 name = vname;
5036 }
5037 filename = &smallname[0];
5038 namelen = snprintf(filename, sizeof(smallname), "%s", name);
5039 if (namelen >= sizeof(smallname)) {
5040 MALLOC(filename, char *, namelen+1, M_TEMP, M_WAITOK);
5041 if (!filename) {
5042 error = ENOMEM;
5043 goto out;
5044 }
5045 snprintf(filename, namelen+1, "%s", name);
5046 }
5047 nfs_node_unlock(np);
5048
5049 if ((error = nfs_open_owner_set_busy(noop, NULL)))
5050 return (error);
5051
5052 NVATTR_INIT(&nvattr);
5053 delegation = NFS_OPEN_DELEGATE_NONE;
5054 dstateid = np->n_dstateid;
5055 NFSREQ_SECINFO_SET(&si, VTONFS(dvp), NULL, 0, filename, namelen);
5056
5057 nfsm_chain_null(&nmreq);
5058 nfsm_chain_null(&nmrep);
5059
5060 // PUTFH, OPEN, GETATTR(FH)
5061 numops = 3;
5062 nfsm_chain_build_alloc_init(error, &nmreq, 48 * NFSX_UNSIGNED);
5063 nfsm_chain_add_compound_header(error, &nmreq, "open_claim_d", numops);
5064 numops--;
5065 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
5066 nfsm_chain_add_fh(error, &nmreq, nfsvers, VTONFS(dvp)->n_fhp, VTONFS(dvp)->n_fhsize);
5067 numops--;
5068 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPEN);
5069 nfsm_chain_add_32(error, &nmreq, noop->noo_seqid);
5070 nfsm_chain_add_32(error, &nmreq, share_access);
5071 nfsm_chain_add_32(error, &nmreq, share_deny);
5072 // open owner: clientid + uid
5073 nfsm_chain_add_64(error, &nmreq, nmp->nm_clientid); // open_owner4.clientid
5074 nfsm_chain_add_32(error, &nmreq, NFSX_UNSIGNED);
5075 nfsm_chain_add_32(error, &nmreq, kauth_cred_getuid(noop->noo_cred)); // open_owner4.owner
5076 // openflag4
5077 nfsm_chain_add_32(error, &nmreq, NFS_OPEN_NOCREATE);
5078 // open_claim4
5079 nfsm_chain_add_32(error, &nmreq, NFS_CLAIM_DELEGATE_CUR);
5080 nfsm_chain_add_stateid(error, &nmreq, &np->n_dstateid);
5081 nfsm_chain_add_name(error, &nmreq, filename, namelen, nmp);
5082 numops--;
5083 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
5084 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
5085 NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
5086 nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, np);
5087 nfsm_chain_build_done(error, &nmreq);
5088 nfsm_assert(error, (numops == 0), EPROTO);
5089 nfsmout_if(error);
5090
5091 error = nfs_request2(np, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, current_thread(),
5092 noop->noo_cred, &si, flags|R_NOINTR, &nmrep, &xid, &status);
5093
5094 if ((lockerror = nfs_node_lock(np)))
5095 error = lockerror;
5096 nfsm_chain_skip_tag(error, &nmrep);
5097 nfsm_chain_get_32(error, &nmrep, numops);
5098 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
5099 nfsmout_if(error);
5100 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPEN);
5101 nfs_owner_seqid_increment(noop, NULL, error);
5102 nfsm_chain_get_stateid(error, &nmrep, &nofp->nof_stateid);
5103 nfsm_chain_check_change_info(error, &nmrep, np);
5104 nfsm_chain_get_32(error, &nmrep, rflags);
5105 bmlen = NFS_ATTR_BITMAP_LEN;
5106 nfsm_chain_get_bitmap(error, &nmrep, bitmap, bmlen);
5107 nfsm_chain_get_32(error, &nmrep, delegation);
5108 if (!error)
5109 switch (delegation) {
5110 case NFS_OPEN_DELEGATE_NONE:
5111 // if (!(np->n_openflags & N_DELEG_RETURN)) /* don't warn if delegation is being returned */
5112 // printf("nfs: open delegated claim didn't return a delegation %s\n", filename ? filename : "???");
5113 break;
5114 case NFS_OPEN_DELEGATE_READ:
5115 case NFS_OPEN_DELEGATE_WRITE:
5116 if ((((np->n_openflags & N_DELEG_MASK) == N_DELEG_READ) &&
5117 (delegation == NFS_OPEN_DELEGATE_WRITE)) ||
5118 (((np->n_openflags & N_DELEG_MASK) == N_DELEG_WRITE) &&
5119 (delegation == NFS_OPEN_DELEGATE_READ)))
5120 printf("nfs: open delegated claim returned a different delegation type! have %s got %s %s\n",
5121 ((np->n_openflags & N_DELEG_MASK) == N_DELEG_WRITE) ? "W" : "R",
5122 (delegation == NFS_OPEN_DELEGATE_WRITE) ? "W" : "R", filename ? filename : "???");
5123 nfsm_chain_get_stateid(error, &nmrep, &dstateid);
5124 nfsm_chain_get_32(error, &nmrep, recall);
5125 if (delegation == NFS_OPEN_DELEGATE_WRITE) // space (skip) XXX
5126 nfsm_chain_adv(error, &nmrep, 3 * NFSX_UNSIGNED);
5127 /* if we have any trouble accepting the ACE, just invalidate it */
5128 ace_type = ace_flags = ace_mask = len = 0;
5129 nfsm_chain_get_32(error, &nmrep, ace_type);
5130 nfsm_chain_get_32(error, &nmrep, ace_flags);
5131 nfsm_chain_get_32(error, &nmrep, ace_mask);
5132 nfsm_chain_get_32(error, &nmrep, len);
5133 ace.ace_flags = nfs4_ace_nfstype_to_vfstype(ace_type, &error);
5134 ace.ace_flags |= nfs4_ace_nfsflags_to_vfsflags(ace_flags);
5135 ace.ace_rights = nfs4_ace_nfsmask_to_vfsrights(ace_mask);
5136 if (!error && (len >= slen)) {
5137 MALLOC(s, char*, len+1, M_TEMP, M_WAITOK);
5138 if (s)
5139 slen = len+1;
5140 else
5141 ace.ace_flags = 0;
5142 }
5143 if (s)
5144 nfsm_chain_get_opaque(error, &nmrep, len, s);
5145 else
5146 nfsm_chain_adv(error, &nmrep, nfsm_rndup(len));
5147 if (!error && s) {
5148 s[len] = '\0';
5149 if (nfs4_id2guid(s, &ace.ace_applicable, (ace_flags & NFS_ACE_IDENTIFIER_GROUP)))
5150 ace.ace_flags = 0;
5151 }
5152 if (error || !s)
5153 ace.ace_flags = 0;
5154 if (s && (s != sbuf))
5155 FREE(s, M_TEMP);
5156 if (!error) {
5157 /* stuff the latest delegation state in the node */
5158 lck_mtx_lock(&np->n_openlock);
5159 np->n_openflags &= ~N_DELEG_MASK;
5160 np->n_openflags |= ((delegation == NFS_OPEN_DELEGATE_READ) ? N_DELEG_READ : N_DELEG_WRITE);
5161 np->n_dstateid = dstateid;
5162 np->n_dace = ace;
5163 if (np->n_dlink.tqe_next == NFSNOLIST) {
5164 lck_mtx_lock(&nmp->nm_lock);
5165 if (np->n_dlink.tqe_next == NFSNOLIST)
5166 TAILQ_INSERT_TAIL(&nmp->nm_delegations, np, n_dlink);
5167 lck_mtx_unlock(&nmp->nm_lock);
5168 }
5169 lck_mtx_unlock(&np->n_openlock);
5170 }
5171 break;
5172 default:
5173 error = EBADRPC;
5174 break;
5175 }
5176 nfsmout_if(error);
5177 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
5178 error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL, NULL);
5179 nfsmout_if(error);
5180 if (!NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
5181 printf("nfs: open reclaim didn't return filehandle? %s\n", filename ? filename : "???");
5182 error = EBADRPC;
5183 goto nfsmout;
5184 }
5185 if (!NFS_CMPFH(np, fh.fh_data, fh.fh_len)) {
5186 // XXX what if fh doesn't match the vnode we think we're re-opening?
5187 // Solaris Named Attributes may do this due to a bug.... so don't warn for named attributes.
5188 if (!(np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR))
5189 printf("nfs4_claim_delegated_open_rpc: warning: file handle mismatch %s\n", filename ? filename : "???");
5190 }
5191 error = nfs_loadattrcache(np, &nvattr, &xid, 1);
5192 nfsmout_if(error);
5193 if (rflags & NFS_OPEN_RESULT_LOCKTYPE_POSIX)
5194 nofp->nof_flags |= NFS_OPEN_FILE_POSIXLOCK;
5195nfsmout:
5196 NVATTR_CLEANUP(&nvattr);
5197 nfsm_chain_cleanup(&nmreq);
5198 nfsm_chain_cleanup(&nmrep);
5199 if (!lockerror)
5200 nfs_node_unlock(np);
5201 nfs_open_owner_clear_busy(noop);
5202 if ((delegation == NFS_OPEN_DELEGATE_READ) || (delegation == NFS_OPEN_DELEGATE_WRITE)) {
5203 if (recall) {
5204 /*
5205 * We're making a delegated claim.
5206 * Don't return the delegation here in case we have more to claim.
5207 * Just make sure it's queued up to be returned.
5208 */
5209 nfs4_delegation_return_enqueue(np);
5210 }
5211 }
5212out:
5213 // if (!error)
5214 // printf("nfs: open claim delegated (%d, %d) succeeded for %s\n", share_access, share_deny, filename ? filename : "???");
5215 if (filename && (filename != &smallname[0]))
5216 FREE(filename, M_TEMP);
5217 if (vname)
5218 vnode_putname(vname);
5219 if (dvp != NULLVP)
5220 vnode_put(dvp);
5221 return (error);
5222}
5223
b0d623f7
A
5224/*
5225 * Send an OPEN RPC to reclaim an open file.
5226 */
5227int
5228nfs4_open_reclaim_rpc(
5229 struct nfs_open_file *nofp,
5230 int share_access,
5231 int share_deny)
5232{
5233 struct nfsmount *nmp;
5234 struct nfs_open_owner *noop = nofp->nof_owner;
5235 struct nfs_vattr nvattr;
5236 int error = 0, lockerror = ENOENT, status;
5237 int nfsvers, numops;
5238 u_int64_t xid;
5239 nfsnode_t np = nofp->nof_np;
5240 struct nfsm_chain nmreq, nmrep;
5241 uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen;
6d2010ae 5242 uint32_t rflags = 0, delegation, recall = 0;
b0d623f7
A
5243 fhandle_t fh;
5244 struct nfs_stateid dstateid;
6d2010ae
A
5245 char sbuf[64], *s = sbuf;
5246 uint32_t ace_type, ace_flags, ace_mask, len, slen = sizeof(sbuf);
5247 struct kauth_ace ace;
5248 struct nfsreq_secinfo_args si;
b0d623f7
A
5249
5250 nmp = NFSTONMP(np);
fe8ab488 5251 if (nfs_mount_gone(nmp))
b0d623f7
A
5252 return (ENXIO);
5253 nfsvers = nmp->nm_vers;
5254
6d2010ae 5255 if ((error = nfs_open_owner_set_busy(noop, NULL)))
b0d623f7
A
5256 return (error);
5257
6d2010ae 5258 NVATTR_INIT(&nvattr);
b0d623f7 5259 delegation = NFS_OPEN_DELEGATE_NONE;
6d2010ae
A
5260 dstateid = np->n_dstateid;
5261 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
b0d623f7
A
5262
5263 nfsm_chain_null(&nmreq);
5264 nfsm_chain_null(&nmrep);
5265
5266 // PUTFH, OPEN, GETATTR(FH)
5267 numops = 3;
5268 nfsm_chain_build_alloc_init(error, &nmreq, 48 * NFSX_UNSIGNED);
5269 nfsm_chain_add_compound_header(error, &nmreq, "open_reclaim", numops);
5270 numops--;
5271 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
5272 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
5273 numops--;
5274 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPEN);
5275 nfsm_chain_add_32(error, &nmreq, noop->noo_seqid);
5276 nfsm_chain_add_32(error, &nmreq, share_access);
5277 nfsm_chain_add_32(error, &nmreq, share_deny);
5278 // open owner: clientid + uid
5279 nfsm_chain_add_64(error, &nmreq, nmp->nm_clientid); // open_owner4.clientid
5280 nfsm_chain_add_32(error, &nmreq, NFSX_UNSIGNED);
5281 nfsm_chain_add_32(error, &nmreq, kauth_cred_getuid(noop->noo_cred)); // open_owner4.owner
5282 // openflag4
5283 nfsm_chain_add_32(error, &nmreq, NFS_OPEN_NOCREATE);
5284 // open_claim4
5285 nfsm_chain_add_32(error, &nmreq, NFS_CLAIM_PREVIOUS);
5286 delegation = (np->n_openflags & N_DELEG_READ) ? NFS_OPEN_DELEGATE_READ :
5287 (np->n_openflags & N_DELEG_WRITE) ? NFS_OPEN_DELEGATE_WRITE :
5288 NFS_OPEN_DELEGATE_NONE;
5289 nfsm_chain_add_32(error, &nmreq, delegation);
5290 delegation = NFS_OPEN_DELEGATE_NONE;
5291 numops--;
5292 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
5293 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
5294 NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
6d2010ae 5295 nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, np);
b0d623f7
A
5296 nfsm_chain_build_done(error, &nmreq);
5297 nfsm_assert(error, (numops == 0), EPROTO);
5298 nfsmout_if(error);
5299
6d2010ae
A
5300 error = nfs_request2(np, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, current_thread(),
5301 noop->noo_cred, &si, R_RECOVER|R_NOINTR, &nmrep, &xid, &status);
b0d623f7
A
5302
5303 if ((lockerror = nfs_node_lock(np)))
5304 error = lockerror;
5305 nfsm_chain_skip_tag(error, &nmrep);
5306 nfsm_chain_get_32(error, &nmrep, numops);
5307 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
5308 nfsmout_if(error);
5309 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPEN);
5310 nfs_owner_seqid_increment(noop, NULL, error);
5311 nfsm_chain_get_stateid(error, &nmrep, &nofp->nof_stateid);
5312 nfsm_chain_check_change_info(error, &nmrep, np);
5313 nfsm_chain_get_32(error, &nmrep, rflags);
5314 bmlen = NFS_ATTR_BITMAP_LEN;
5315 nfsm_chain_get_bitmap(error, &nmrep, bitmap, bmlen);
5316 nfsm_chain_get_32(error, &nmrep, delegation);
5317 if (!error)
5318 switch (delegation) {
5319 case NFS_OPEN_DELEGATE_NONE:
6d2010ae
A
5320 if (np->n_openflags & N_DELEG_MASK) {
5321 /*
5322 * Hey! We were supposed to get our delegation back even
5323 * if it was getting immediately recalled. Bad server!
5324 *
5325 * Just try to return the existing delegation.
5326 */
5327 // NP(np, "nfs: open reclaim didn't return delegation?");
5328 delegation = (np->n_openflags & N_DELEG_WRITE) ? NFS_OPEN_DELEGATE_WRITE : NFS_OPEN_DELEGATE_READ;
5329 recall = 1;
b0d623f7
A
5330 }
5331 break;
6d2010ae 5332 case NFS_OPEN_DELEGATE_READ:
b0d623f7
A
5333 case NFS_OPEN_DELEGATE_WRITE:
5334 nfsm_chain_get_stateid(error, &nmrep, &dstateid);
5335 nfsm_chain_get_32(error, &nmrep, recall);
6d2010ae
A
5336 if (delegation == NFS_OPEN_DELEGATE_WRITE) // space (skip) XXX
5337 nfsm_chain_adv(error, &nmrep, 3 * NFSX_UNSIGNED);
5338 /* if we have any trouble accepting the ACE, just invalidate it */
5339 ace_type = ace_flags = ace_mask = len = 0;
5340 nfsm_chain_get_32(error, &nmrep, ace_type);
5341 nfsm_chain_get_32(error, &nmrep, ace_flags);
5342 nfsm_chain_get_32(error, &nmrep, ace_mask);
5343 nfsm_chain_get_32(error, &nmrep, len);
5344 ace.ace_flags = nfs4_ace_nfstype_to_vfstype(ace_type, &error);
5345 ace.ace_flags |= nfs4_ace_nfsflags_to_vfsflags(ace_flags);
5346 ace.ace_rights = nfs4_ace_nfsmask_to_vfsrights(ace_mask);
5347 if (!error && (len >= slen)) {
5348 MALLOC(s, char*, len+1, M_TEMP, M_WAITOK);
5349 if (s)
5350 slen = len+1;
5351 else
5352 ace.ace_flags = 0;
5353 }
5354 if (s)
5355 nfsm_chain_get_opaque(error, &nmrep, len, s);
5356 else
5357 nfsm_chain_adv(error, &nmrep, nfsm_rndup(len));
5358 if (!error && s) {
5359 s[len] = '\0';
5360 if (nfs4_id2guid(s, &ace.ace_applicable, (ace_flags & NFS_ACE_IDENTIFIER_GROUP)))
5361 ace.ace_flags = 0;
5362 }
5363 if (error || !s)
5364 ace.ace_flags = 0;
5365 if (s && (s != sbuf))
5366 FREE(s, M_TEMP);
b0d623f7
A
5367 if (!error) {
5368 /* stuff the delegation state in the node */
5369 lck_mtx_lock(&np->n_openlock);
5370 np->n_openflags &= ~N_DELEG_MASK;
6d2010ae 5371 np->n_openflags |= ((delegation == NFS_OPEN_DELEGATE_READ) ? N_DELEG_READ : N_DELEG_WRITE);
b0d623f7 5372 np->n_dstateid = dstateid;
6d2010ae
A
5373 np->n_dace = ace;
5374 if (np->n_dlink.tqe_next == NFSNOLIST) {
5375 lck_mtx_lock(&nmp->nm_lock);
5376 if (np->n_dlink.tqe_next == NFSNOLIST)
5377 TAILQ_INSERT_TAIL(&nmp->nm_delegations, np, n_dlink);
5378 lck_mtx_unlock(&nmp->nm_lock);
5379 }
b0d623f7
A
5380 lck_mtx_unlock(&np->n_openlock);
5381 }
5382 break;
5383 default:
5384 error = EBADRPC;
5385 break;
5386 }
5387 nfsmout_if(error);
5388 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 5389 error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL, NULL);
b0d623f7
A
5390 nfsmout_if(error);
5391 if (!NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
6d2010ae 5392 NP(np, "nfs: open reclaim didn't return filehandle?");
b0d623f7
A
5393 error = EBADRPC;
5394 goto nfsmout;
5395 }
5396 if (!NFS_CMPFH(np, fh.fh_data, fh.fh_len)) {
5397 // XXX what if fh doesn't match the vnode we think we're re-opening?
6d2010ae
A
5398 // That should be pretty hard in this case, given that we are doing
5399 // the open reclaim using the file handle (and not a dir/name pair).
5400 // Solaris Named Attributes may do this due to a bug.... so don't warn for named attributes.
5401 if (!(np->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR))
5402 NP(np, "nfs4_open_reclaim_rpc: warning: file handle mismatch");
b0d623f7
A
5403 }
5404 error = nfs_loadattrcache(np, &nvattr, &xid, 1);
5405 nfsmout_if(error);
5406 if (rflags & NFS_OPEN_RESULT_LOCKTYPE_POSIX)
5407 nofp->nof_flags |= NFS_OPEN_FILE_POSIXLOCK;
5408nfsmout:
6d2010ae
A
5409 // if (!error)
5410 // NP(np, "nfs: open reclaim (%d, %d) succeeded", share_access, share_deny);
5411 NVATTR_CLEANUP(&nvattr);
b0d623f7
A
5412 nfsm_chain_cleanup(&nmreq);
5413 nfsm_chain_cleanup(&nmrep);
5414 if (!lockerror)
5415 nfs_node_unlock(np);
5416 nfs_open_owner_clear_busy(noop);
5417 if ((delegation == NFS_OPEN_DELEGATE_READ) || (delegation == NFS_OPEN_DELEGATE_WRITE)) {
6d2010ae
A
5418 if (recall)
5419 nfs4_delegation_return_enqueue(np);
b0d623f7
A
5420 }
5421 return (error);
5422}
2d21ac55 5423
b0d623f7
A
5424int
5425nfs4_open_downgrade_rpc(
5426 nfsnode_t np,
5427 struct nfs_open_file *nofp,
5428 vfs_context_t ctx)
5429{
5430 struct nfs_open_owner *noop = nofp->nof_owner;
5431 struct nfsmount *nmp;
5432 int error, lockerror = ENOENT, status, nfsvers, numops;
5433 struct nfsm_chain nmreq, nmrep;
5434 u_int64_t xid;
6d2010ae 5435 struct nfsreq_secinfo_args si;
2d21ac55 5436
b0d623f7 5437 nmp = NFSTONMP(np);
fe8ab488 5438 if (nfs_mount_gone(nmp))
b0d623f7
A
5439 return (ENXIO);
5440 nfsvers = nmp->nm_vers;
5441
6d2010ae 5442 if ((error = nfs_open_owner_set_busy(noop, NULL)))
b0d623f7
A
5443 return (error);
5444
6d2010ae 5445 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
b0d623f7
A
5446 nfsm_chain_null(&nmreq);
5447 nfsm_chain_null(&nmrep);
5448
5449 // PUTFH, OPEN_DOWNGRADE, GETATTR
5450 numops = 3;
5451 nfsm_chain_build_alloc_init(error, &nmreq, 23 * NFSX_UNSIGNED);
5452 nfsm_chain_add_compound_header(error, &nmreq, "open_downgrd", numops);
5453 numops--;
5454 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
5455 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
5456 numops--;
5457 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPEN_DOWNGRADE);
5458 nfsm_chain_add_stateid(error, &nmreq, &nofp->nof_stateid);
5459 nfsm_chain_add_32(error, &nmreq, noop->noo_seqid);
5460 nfsm_chain_add_32(error, &nmreq, nofp->nof_access);
5461 nfsm_chain_add_32(error, &nmreq, nofp->nof_deny);
5462 numops--;
5463 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 5464 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
b0d623f7
A
5465 nfsm_chain_build_done(error, &nmreq);
5466 nfsm_assert(error, (numops == 0), EPROTO);
5467 nfsmout_if(error);
6d2010ae
A
5468 error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND,
5469 vfs_context_thread(ctx), vfs_context_ucred(ctx),
5470 &si, R_NOINTR, &nmrep, &xid, &status);
b0d623f7
A
5471
5472 if ((lockerror = nfs_node_lock(np)))
5473 error = lockerror;
5474 nfsm_chain_skip_tag(error, &nmrep);
5475 nfsm_chain_get_32(error, &nmrep, numops);
5476 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
2d21ac55 5477 nfsmout_if(error);
b0d623f7
A
5478 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPEN_DOWNGRADE);
5479 nfs_owner_seqid_increment(noop, NULL, error);
5480 nfsm_chain_get_stateid(error, &nmrep, &nofp->nof_stateid);
5481 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 5482 nfsm_chain_loadattr(error, &nmrep, np, nfsvers, &xid);
b0d623f7
A
5483nfsmout:
5484 if (!lockerror)
5485 nfs_node_unlock(np);
5486 nfs_open_owner_clear_busy(noop);
2d21ac55
A
5487 nfsm_chain_cleanup(&nmreq);
5488 nfsm_chain_cleanup(&nmrep);
b0d623f7
A
5489 return (error);
5490}
2d21ac55 5491
b0d623f7
A
5492int
5493nfs4_close_rpc(
5494 nfsnode_t np,
5495 struct nfs_open_file *nofp,
5496 thread_t thd,
5497 kauth_cred_t cred,
6d2010ae 5498 int flags)
b0d623f7
A
5499{
5500 struct nfs_open_owner *noop = nofp->nof_owner;
5501 struct nfsmount *nmp;
5502 int error, lockerror = ENOENT, status, nfsvers, numops;
5503 struct nfsm_chain nmreq, nmrep;
5504 u_int64_t xid;
6d2010ae 5505 struct nfsreq_secinfo_args si;
b0d623f7
A
5506
5507 nmp = NFSTONMP(np);
fe8ab488 5508 if (nfs_mount_gone(nmp))
b0d623f7
A
5509 return (ENXIO);
5510 nfsvers = nmp->nm_vers;
5511
6d2010ae 5512 if ((error = nfs_open_owner_set_busy(noop, NULL)))
b0d623f7
A
5513 return (error);
5514
6d2010ae 5515 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
b0d623f7
A
5516 nfsm_chain_null(&nmreq);
5517 nfsm_chain_null(&nmrep);
5518
6d2010ae 5519 // PUTFH, CLOSE, GETATTR
b0d623f7
A
5520 numops = 3;
5521 nfsm_chain_build_alloc_init(error, &nmreq, 23 * NFSX_UNSIGNED);
5522 nfsm_chain_add_compound_header(error, &nmreq, "close", numops);
2d21ac55
A
5523 numops--;
5524 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
b0d623f7 5525 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
2d21ac55
A
5526 numops--;
5527 nfsm_chain_add_32(error, &nmreq, NFS_OP_CLOSE);
b0d623f7
A
5528 nfsm_chain_add_32(error, &nmreq, noop->noo_seqid);
5529 nfsm_chain_add_stateid(error, &nmreq, &nofp->nof_stateid);
5530 numops--;
5531 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6d2010ae 5532 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
2d21ac55
A
5533 nfsm_chain_build_done(error, &nmreq);
5534 nfsm_assert(error, (numops == 0), EPROTO);
5535 nfsmout_if(error);
6d2010ae 5536 error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, flags|R_NOINTR, &nmrep, &xid, &status);
2d21ac55 5537
b0d623f7
A
5538 if ((lockerror = nfs_node_lock(np)))
5539 error = lockerror;
2d21ac55
A
5540 nfsm_chain_skip_tag(error, &nmrep);
5541 nfsm_chain_get_32(error, &nmrep, numops);
5542 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
b0d623f7 5543 nfsmout_if(error);
2d21ac55 5544 nfsm_chain_op_check(error, &nmrep, NFS_OP_CLOSE);
b0d623f7
A
5545 nfs_owner_seqid_increment(noop, NULL, error);
5546 nfsm_chain_get_stateid(error, &nmrep, &nofp->nof_stateid);
5547 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae 5548 nfsm_chain_loadattr(error, &nmrep, np, nfsvers, &xid);
b0d623f7
A
5549nfsmout:
5550 if (!lockerror)
5551 nfs_node_unlock(np);
5552 nfs_open_owner_clear_busy(noop);
5553 nfsm_chain_cleanup(&nmreq);
5554 nfsm_chain_cleanup(&nmrep);
5555 return (error);
5556}
5557
5558
b0d623f7 5559/*
6d2010ae 5560 * Claim the delegated open combinations this open file holds.
b0d623f7
A
5561 */
5562int
6d2010ae 5563nfs4_claim_delegated_state_for_open_file(struct nfs_open_file *nofp, int flags)
b0d623f7 5564{
6d2010ae
A
5565 struct nfs_open_owner *noop = nofp->nof_owner;
5566 struct nfs_lock_owner *nlop;
5567 struct nfs_file_lock *nflp, *nextnflp;
b0d623f7 5568 struct nfsmount *nmp;
6d2010ae 5569 int error = 0, reopen = 0;
b0d623f7 5570
6d2010ae
A
5571 if (nofp->nof_d_rw_drw) {
5572 error = nfs4_claim_delegated_open_rpc(nofp, NFS_OPEN_SHARE_ACCESS_BOTH, NFS_OPEN_SHARE_DENY_BOTH, flags);
5573 if (!error) {
5574 lck_mtx_lock(&nofp->nof_lock);
5575 nofp->nof_rw_drw += nofp->nof_d_rw_drw;
5576 nofp->nof_d_rw_drw = 0;
5577 lck_mtx_unlock(&nofp->nof_lock);
5578 }
b0d623f7 5579 }
6d2010ae
A
5580 if (!error && nofp->nof_d_w_drw) {
5581 error = nfs4_claim_delegated_open_rpc(nofp, NFS_OPEN_SHARE_ACCESS_WRITE, NFS_OPEN_SHARE_DENY_BOTH, flags);
5582 if (!error) {
5583 lck_mtx_lock(&nofp->nof_lock);
5584 nofp->nof_w_drw += nofp->nof_d_w_drw;
5585 nofp->nof_d_w_drw = 0;
5586 lck_mtx_unlock(&nofp->nof_lock);
5587 }
b0d623f7 5588 }
6d2010ae
A
5589 if (!error && nofp->nof_d_r_drw) {
5590 error = nfs4_claim_delegated_open_rpc(nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_BOTH, flags);
5591 if (!error) {
5592 lck_mtx_lock(&nofp->nof_lock);
5593 nofp->nof_r_drw += nofp->nof_d_r_drw;
5594 nofp->nof_d_r_drw = 0;
5595 lck_mtx_unlock(&nofp->nof_lock);
5596 }
5597 }
5598 if (!error && nofp->nof_d_rw_dw) {
5599 error = nfs4_claim_delegated_open_rpc(nofp, NFS_OPEN_SHARE_ACCESS_BOTH, NFS_OPEN_SHARE_DENY_WRITE, flags);
5600 if (!error) {
5601 lck_mtx_lock(&nofp->nof_lock);
5602 nofp->nof_rw_dw += nofp->nof_d_rw_dw;
5603 nofp->nof_d_rw_dw = 0;
5604 lck_mtx_unlock(&nofp->nof_lock);
5605 }
5606 }
5607 if (!error && nofp->nof_d_w_dw) {
5608 error = nfs4_claim_delegated_open_rpc(nofp, NFS_OPEN_SHARE_ACCESS_WRITE, NFS_OPEN_SHARE_DENY_WRITE, flags);
5609 if (!error) {
5610 lck_mtx_lock(&nofp->nof_lock);
5611 nofp->nof_w_dw += nofp->nof_d_w_dw;
5612 nofp->nof_d_w_dw = 0;
5613 lck_mtx_unlock(&nofp->nof_lock);
5614 }
5615 }
5616 if (!error && nofp->nof_d_r_dw) {
5617 error = nfs4_claim_delegated_open_rpc(nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_WRITE, flags);
5618 if (!error) {
5619 lck_mtx_lock(&nofp->nof_lock);
5620 nofp->nof_r_dw += nofp->nof_d_r_dw;
5621 nofp->nof_d_r_dw = 0;
5622 lck_mtx_unlock(&nofp->nof_lock);
5623 }
5624 }
5625 /* non-deny-mode opens may be reopened if no locks are held */
5626 if (!error && nofp->nof_d_rw) {
5627 error = nfs4_claim_delegated_open_rpc(nofp, NFS_OPEN_SHARE_ACCESS_BOTH, NFS_OPEN_SHARE_DENY_NONE, flags);
5628 /* for some errors, we should just try reopening the file */
5629 if (nfs_mount_state_error_delegation_lost(error))
5630 reopen = error;
5631 if (!error || reopen) {
5632 lck_mtx_lock(&nofp->nof_lock);
5633 nofp->nof_rw += nofp->nof_d_rw;
5634 nofp->nof_d_rw = 0;
5635 lck_mtx_unlock(&nofp->nof_lock);
5636 }
5637 }
5638 /* if we've already set reopen, we should move these other two opens from delegated to not delegated */
5639 if ((!error || reopen) && nofp->nof_d_w) {
5640 if (!error) {
5641 error = nfs4_claim_delegated_open_rpc(nofp, NFS_OPEN_SHARE_ACCESS_WRITE, NFS_OPEN_SHARE_DENY_NONE, flags);
5642 /* for some errors, we should just try reopening the file */
5643 if (nfs_mount_state_error_delegation_lost(error))
5644 reopen = error;
5645 }
5646 if (!error || reopen) {
5647 lck_mtx_lock(&nofp->nof_lock);
5648 nofp->nof_w += nofp->nof_d_w;
5649 nofp->nof_d_w = 0;
5650 lck_mtx_unlock(&nofp->nof_lock);
5651 }
5652 }
5653 if ((!error || reopen) && nofp->nof_d_r) {
5654 if (!error) {
5655 error = nfs4_claim_delegated_open_rpc(nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, flags);
5656 /* for some errors, we should just try reopening the file */
5657 if (nfs_mount_state_error_delegation_lost(error))
5658 reopen = error;
5659 }
5660 if (!error || reopen) {
5661 lck_mtx_lock(&nofp->nof_lock);
5662 nofp->nof_r += nofp->nof_d_r;
5663 nofp->nof_d_r = 0;
5664 lck_mtx_unlock(&nofp->nof_lock);
5665 }
5666 }
5667
5668 if (reopen) {
5669 /*
5670 * Any problems with the delegation probably indicates that we
5671 * should review/return all of our current delegation state.
5672 */
5673 if ((nmp = NFSTONMP(nofp->nof_np))) {
5674 nfs4_delegation_return_enqueue(nofp->nof_np);
5675 lck_mtx_lock(&nmp->nm_lock);
5676 nfs_need_recover(nmp, NFSERR_EXPIRED);
5677 lck_mtx_unlock(&nmp->nm_lock);
5678 }
5679 if (reopen && (nfs_check_for_locks(noop, nofp) == 0)) {
5680 /* just reopen the file on next access */
5681 NP(nofp->nof_np, "nfs4_claim_delegated_state_for_open_file: %d, need reopen, %d",
5682 reopen, kauth_cred_getuid(nofp->nof_owner->noo_cred));
5683 lck_mtx_lock(&nofp->nof_lock);
5684 nofp->nof_flags |= NFS_OPEN_FILE_REOPEN;
5685 lck_mtx_unlock(&nofp->nof_lock);
5686 return (0);
5687 }
5688 if (reopen)
5689 NP(nofp->nof_np, "nfs4_claim_delegated_state_for_open_file: %d, locks prevent reopen, %d",
5690 reopen, kauth_cred_getuid(nofp->nof_owner->noo_cred));
5691 }
5692
5693 if (!error && ((nmp = NFSTONMP(nofp->nof_np)))) {
5694 /* claim delegated locks */
5695 TAILQ_FOREACH(nlop, &nofp->nof_np->n_lock_owners, nlo_link) {
5696 if (nlop->nlo_open_owner != noop)
5697 continue;
5698 TAILQ_FOREACH_SAFE(nflp, &nlop->nlo_locks, nfl_lolink, nextnflp) {
5699 /* skip dead & blocked lock requests (shouldn't be any in the held lock list) */
5700 if (nflp->nfl_flags & (NFS_FILE_LOCK_DEAD|NFS_FILE_LOCK_BLOCKED))
5701 continue;
5702 /* skip non-delegated locks */
5703 if (!(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED))
5704 continue;
5705 error = nmp->nm_funcs->nf_setlock_rpc(nofp->nof_np, nofp, nflp, 0, flags, current_thread(), noop->noo_cred);
5706 if (error) {
5707 NP(nofp->nof_np, "nfs: delegated lock claim (0x%llx, 0x%llx) failed %d, %d",
5708 nflp->nfl_start, nflp->nfl_end, error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
5709 break;
5710 }
5711 // else {
5712 // NP(nofp->nof_np, "nfs: delegated lock claim (0x%llx, 0x%llx) succeeded, %d",
5713 // nflp->nfl_start, nflp->nfl_end, kauth_cred_getuid(nofp->nof_owner->noo_cred));
5714 // }
5715 }
5716 if (error)
5717 break;
5718 }
5719 }
5720
5721 if (!error) /* all state claimed successfully! */
5722 return (0);
5723
5724 /* restart if it looks like a problem more than just losing the delegation */
5725 if (!nfs_mount_state_error_delegation_lost(error) &&
5726 ((error == ETIMEDOUT) || nfs_mount_state_error_should_restart(error))) {
5727 NP(nofp->nof_np, "nfs delegated lock claim error %d, %d", error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
5728 if ((error == ETIMEDOUT) && ((nmp = NFSTONMP(nofp->nof_np))))
5729 nfs_need_reconnect(nmp);
b0d623f7
A
5730 return (error);
5731 }
6d2010ae
A
5732
5733 /* delegated state lost (once held but now not claimable) */
5734 NP(nofp->nof_np, "nfs delegated state claim error %d, state lost, %d", error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
5735
5736 /*
5737 * Any problems with the delegation probably indicates that we
5738 * should review/return all of our current delegation state.
5739 */
5740 if ((nmp = NFSTONMP(nofp->nof_np))) {
5741 nfs4_delegation_return_enqueue(nofp->nof_np);
5742 lck_mtx_lock(&nmp->nm_lock);
5743 nfs_need_recover(nmp, NFSERR_EXPIRED);
5744 lck_mtx_unlock(&nmp->nm_lock);
5745 }
5746
5747 /* revoke all open file state */
5748 nfs_revoke_open_state_for_node(nofp->nof_np);
5749
5750 return (error);
5751}
5752
5753/*
5754 * Release all open state for the given node.
5755 */
5756void
5757nfs_release_open_state_for_node(nfsnode_t np, int force)
5758{
5759 struct nfsmount *nmp = NFSTONMP(np);
5760 struct nfs_open_file *nofp;
5761 struct nfs_file_lock *nflp, *nextnflp;
5762
5763 /* drop held locks */
5764 TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) {
5765 /* skip dead & blocked lock requests */
5766 if (nflp->nfl_flags & (NFS_FILE_LOCK_DEAD|NFS_FILE_LOCK_BLOCKED))
5767 continue;
5768 /* send an unlock if not a delegated lock */
5769 if (!force && nmp && !(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED))
5770 nmp->nm_funcs->nf_unlock_rpc(np, nflp->nfl_owner, F_WRLCK, nflp->nfl_start, nflp->nfl_end, R_RECOVER,
5771 NULL, nflp->nfl_owner->nlo_open_owner->noo_cred);
5772 /* kill/remove the lock */
5773 lck_mtx_lock(&np->n_openlock);
5774 nflp->nfl_flags |= NFS_FILE_LOCK_DEAD;
5775 lck_mtx_lock(&nflp->nfl_owner->nlo_lock);
5776 TAILQ_REMOVE(&nflp->nfl_owner->nlo_locks, nflp, nfl_lolink);
5777 lck_mtx_unlock(&nflp->nfl_owner->nlo_lock);
5778 if (nflp->nfl_blockcnt) {
5779 /* wake up anyone blocked on this lock */
5780 wakeup(nflp);
5781 } else {
5782 /* remove nflp from lock list and destroy */
5783 TAILQ_REMOVE(&np->n_locks, nflp, nfl_link);
5784 nfs_file_lock_destroy(nflp);
2d21ac55 5785 }
6d2010ae
A
5786 lck_mtx_unlock(&np->n_openlock);
5787 }
5788
5789 lck_mtx_lock(&np->n_openlock);
5790
5791 /* drop all opens */
5792 TAILQ_FOREACH(nofp, &np->n_opens, nof_link) {
5793 if (nofp->nof_flags & NFS_OPEN_FILE_LOST)
5794 continue;
5795 /* mark open state as lost */
5796 lck_mtx_lock(&nofp->nof_lock);
5797 nofp->nof_flags &= ~NFS_OPEN_FILE_REOPEN;
5798 nofp->nof_flags |= NFS_OPEN_FILE_LOST;
316670eb 5799
6d2010ae
A
5800 lck_mtx_unlock(&nofp->nof_lock);
5801 if (!force && nmp && (nmp->nm_vers >= NFS_VER4))
5802 nfs4_close_rpc(np, nofp, NULL, nofp->nof_owner->noo_cred, R_RECOVER);
5803 }
5804
5805 lck_mtx_unlock(&np->n_openlock);
5806}
5807
5808/*
5809 * State for a node has been lost, drop it, and revoke the node.
5810 * Attempt to return any state if possible in case the server
5811 * might somehow think we hold it.
5812 */
5813void
5814nfs_revoke_open_state_for_node(nfsnode_t np)
5815{
5816 struct nfsmount *nmp;
5817
5818 /* mark node as needing to be revoked */
5819 nfs_node_lock_force(np);
5820 if (np->n_flag & NREVOKE) /* already revoked? */
5821 {
5822 NP(np, "nfs_revoke_open_state_for_node(): already revoked");
5823 nfs_node_unlock(np);
5824 return;
5825 }
5826 np->n_flag |= NREVOKE;
5827 nfs_node_unlock(np);
5828
5829 nfs_release_open_state_for_node(np, 0);
5830 NP(np, "nfs: state lost for %p 0x%x", np, np->n_flag);
5831
5832 /* mark mount as needing a revoke scan and have the socket thread do it. */
5833 if ((nmp = NFSTONMP(np))) {
5834 lck_mtx_lock(&nmp->nm_lock);
5835 nmp->nm_state |= NFSSTA_REVOKE;
5836 nfs_mount_sock_thread_wake(nmp);
5837 lck_mtx_unlock(&nmp->nm_lock);
5838 }
5839}
5840
5841/*
5842 * Claim the delegated open combinations that each of this node's open files hold.
5843 */
5844int
5845nfs4_claim_delegated_state_for_node(nfsnode_t np, int flags)
5846{
5847 struct nfs_open_file *nofp;
5848 int error = 0;
5849
5850 lck_mtx_lock(&np->n_openlock);
5851
5852 /* walk the open file list looking for opens with delegated state to claim */
5853restart:
5854 TAILQ_FOREACH(nofp, &np->n_opens, nof_link) {
5855 if (!nofp->nof_d_rw_drw && !nofp->nof_d_w_drw && !nofp->nof_d_r_drw &&
5856 !nofp->nof_d_rw_dw && !nofp->nof_d_w_dw && !nofp->nof_d_r_dw &&
5857 !nofp->nof_d_rw && !nofp->nof_d_w && !nofp->nof_d_r)
5858 continue;
5859 lck_mtx_unlock(&np->n_openlock);
5860 error = nfs4_claim_delegated_state_for_open_file(nofp, flags);
5861 lck_mtx_lock(&np->n_openlock);
b0d623f7 5862 if (error)
6d2010ae
A
5863 break;
5864 goto restart;
5865 }
5866
5867 lck_mtx_unlock(&np->n_openlock);
5868
5869 return (error);
5870}
5871
5872/*
5873 * Mark a node as needed to have its delegation returned.
5874 * Queue it up on the delegation return queue.
5875 * Make sure the thread is running.
5876 */
5877void
5878nfs4_delegation_return_enqueue(nfsnode_t np)
5879{
5880 struct nfsmount *nmp;
5881
5882 nmp = NFSTONMP(np);
fe8ab488 5883 if (nfs_mount_gone(nmp))
6d2010ae
A
5884 return;
5885
5886 lck_mtx_lock(&np->n_openlock);
5887 np->n_openflags |= N_DELEG_RETURN;
5888 lck_mtx_unlock(&np->n_openlock);
5889
5890 lck_mtx_lock(&nmp->nm_lock);
5891 if (np->n_dreturn.tqe_next == NFSNOLIST)
5892 TAILQ_INSERT_TAIL(&nmp->nm_dreturnq, np, n_dreturn);
5893 nfs_mount_sock_thread_wake(nmp);
5894 lck_mtx_unlock(&nmp->nm_lock);
5895}
5896
5897/*
5898 * return any delegation we may have for the given node
5899 */
5900int
5901nfs4_delegation_return(nfsnode_t np, int flags, thread_t thd, kauth_cred_t cred)
5902{
5903 struct nfsmount *nmp;
5904 fhandle_t fh;
5905 nfs_stateid dstateid;
5906 int error;
5907
5908 nmp = NFSTONMP(np);
fe8ab488 5909 if (nfs_mount_gone(nmp))
6d2010ae
A
5910 return (ENXIO);
5911
5912 /* first, make sure the node's marked for delegation return */
5913 lck_mtx_lock(&np->n_openlock);
5914 np->n_openflags |= (N_DELEG_RETURN|N_DELEG_RETURNING);
5915 lck_mtx_unlock(&np->n_openlock);
5916
5917 /* make sure nobody else is using the delegation state */
5918 if ((error = nfs_open_state_set_busy(np, NULL)))
5919 goto out;
5920
5921 /* claim any delegated state */
5922 if ((error = nfs4_claim_delegated_state_for_node(np, flags)))
5923 goto out;
5924
5925 /* return the delegation */
5926 lck_mtx_lock(&np->n_openlock);
5927 dstateid = np->n_dstateid;
5928 fh.fh_len = np->n_fhsize;
5929 bcopy(np->n_fhp, &fh.fh_data, fh.fh_len);
5930 lck_mtx_unlock(&np->n_openlock);
5931 error = nfs4_delegreturn_rpc(NFSTONMP(np), fh.fh_data, fh.fh_len, &dstateid, flags, thd, cred);
5932 /* assume delegation is gone for all errors except ETIMEDOUT, NFSERR_*MOVED */
5933 if ((error != ETIMEDOUT) && (error != NFSERR_MOVED) && (error != NFSERR_LEASE_MOVED)) {
5934 lck_mtx_lock(&np->n_openlock);
5935 np->n_openflags &= ~N_DELEG_MASK;
5936 lck_mtx_lock(&nmp->nm_lock);
5937 if (np->n_dlink.tqe_next != NFSNOLIST) {
5938 TAILQ_REMOVE(&nmp->nm_delegations, np, n_dlink);
5939 np->n_dlink.tqe_next = NFSNOLIST;
5940 }
5941 lck_mtx_unlock(&nmp->nm_lock);
5942 lck_mtx_unlock(&np->n_openlock);
5943 }
5944
5945out:
5946 /* make sure it's no longer on the return queue and clear the return flags */
5947 lck_mtx_lock(&nmp->nm_lock);
5948 if (np->n_dreturn.tqe_next != NFSNOLIST) {
5949 TAILQ_REMOVE(&nmp->nm_dreturnq, np, n_dreturn);
5950 np->n_dreturn.tqe_next = NFSNOLIST;
5951 }
5952 lck_mtx_unlock(&nmp->nm_lock);
5953 lck_mtx_lock(&np->n_openlock);
5954 np->n_openflags &= ~(N_DELEG_RETURN|N_DELEG_RETURNING);
5955 lck_mtx_unlock(&np->n_openlock);
5956
5957 if (error) {
5958 NP(np, "nfs4_delegation_return, error %d", error);
5959 if (error == ETIMEDOUT)
5960 nfs_need_reconnect(nmp);
5961 if (nfs_mount_state_error_should_restart(error)) {
5962 /* make sure recovery happens */
5963 lck_mtx_lock(&nmp->nm_lock);
5964 nfs_need_recover(nmp, nfs_mount_state_error_delegation_lost(error) ? NFSERR_EXPIRED : 0);
5965 lck_mtx_unlock(&nmp->nm_lock);
2d21ac55
A
5966 }
5967 }
6d2010ae
A
5968
5969 nfs_open_state_clear_busy(np);
5970
5971 return (error);
b0d623f7 5972}
2d21ac55 5973
b0d623f7 5974/*
6d2010ae
A
5975 * RPC to return a delegation for a file handle
5976 */
5977int
5978nfs4_delegreturn_rpc(struct nfsmount *nmp, u_char *fhp, int fhlen, struct nfs_stateid *sid, int flags, thread_t thd, kauth_cred_t cred)
5979{
5980 int error = 0, status, numops;
5981 uint64_t xid;
5982 struct nfsm_chain nmreq, nmrep;
5983 struct nfsreq_secinfo_args si;
5984
5985 NFSREQ_SECINFO_SET(&si, NULL, fhp, fhlen, NULL, 0);
5986 nfsm_chain_null(&nmreq);
5987 nfsm_chain_null(&nmrep);
5988
5989 // PUTFH, DELEGRETURN
5990 numops = 2;
5991 nfsm_chain_build_alloc_init(error, &nmreq, 16 * NFSX_UNSIGNED);
5992 nfsm_chain_add_compound_header(error, &nmreq, "delegreturn", numops);
5993 numops--;
5994 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
5995 nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, fhp, fhlen);
5996 numops--;
5997 nfsm_chain_add_32(error, &nmreq, NFS_OP_DELEGRETURN);
5998 nfsm_chain_add_stateid(error, &nmreq, sid);
5999 nfsm_chain_build_done(error, &nmreq);
6000 nfsm_assert(error, (numops == 0), EPROTO);
6001 nfsmout_if(error);
6002 error = nfs_request2(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, flags, &nmrep, &xid, &status);
6003 nfsm_chain_skip_tag(error, &nmrep);
6004 nfsm_chain_get_32(error, &nmrep, numops);
6005 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
6006 nfsm_chain_op_check(error, &nmrep, NFS_OP_DELEGRETURN);
6007nfsmout:
6008 nfsm_chain_cleanup(&nmreq);
6009 nfsm_chain_cleanup(&nmrep);
6010 return (error);
6011}
6012
6013
6014/*
6015 * NFS read call.
6016 * Just call nfs_bioread() to do the work.
6017 *
6018 * Note: the exec code paths have a tendency to call VNOP_READ (and VNOP_MMAP)
6019 * without first calling VNOP_OPEN, so we make sure the file is open here.
6020 */
6021int
6022nfs_vnop_read(
6023 struct vnop_read_args /* {
6024 struct vnodeop_desc *a_desc;
6025 vnode_t a_vp;
6026 struct uio *a_uio;
6027 int a_ioflag;
6028 vfs_context_t a_context;
6029 } */ *ap)
6030{
6031 vnode_t vp = ap->a_vp;
6032 vfs_context_t ctx = ap->a_context;
6033 nfsnode_t np;
6034 struct nfsmount *nmp;
6035 struct nfs_open_owner *noop;
6036 struct nfs_open_file *nofp;
6037 int error;
6038
6039 if (vnode_vtype(ap->a_vp) != VREG)
39236c6e 6040 return (vnode_vtype(vp) == VDIR) ? EISDIR : EPERM;
6d2010ae
A
6041
6042 np = VTONFS(vp);
6043 nmp = NFSTONMP(np);
fe8ab488 6044 if (nfs_mount_gone(nmp))
6d2010ae
A
6045 return (ENXIO);
6046 if (np->n_flag & NREVOKE)
6047 return (EIO);
6048
6049 noop = nfs_open_owner_find(nmp, vfs_context_ucred(ctx), 1);
6050 if (!noop)
6051 return (ENOMEM);
6052restart:
6053 error = nfs_open_file_find(np, noop, &nofp, 0, 0, 1);
6054 if (!error && (nofp->nof_flags & NFS_OPEN_FILE_LOST)) {
6055 NP(np, "nfs_vnop_read: LOST %d", kauth_cred_getuid(noop->noo_cred));
6056 error = EIO;
6057 }
6058 if (!error && (nofp->nof_flags & NFS_OPEN_FILE_REOPEN)) {
6059 error = nfs4_reopen(nofp, vfs_context_thread(ctx));
6060 nofp = NULL;
6061 if (!error)
6062 goto restart;
6063 }
6064 if (error) {
6065 nfs_open_owner_rele(noop);
6066 return (error);
6067 }
6068 if (!nofp->nof_access) {
6069 /* we don't have the file open, so open it for read access */
6070 error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx));
6071 if (error) {
6072 nfs_open_owner_rele(noop);
6073 return (error);
6074 }
6075 if (np->n_flag & NREVOKE) {
6076 error = EIO;
6077 nfs_mount_state_in_use_end(nmp, 0);
6078 nfs_open_owner_rele(noop);
6079 return (error);
6080 }
6081 error = nfs_open_file_set_busy(nofp, vfs_context_thread(ctx));
6082 if (error)
6083 nofp = NULL;
6084 if (!error) {
6085 if (nmp->nm_vers < NFS_VER4) {
6086 /* NFS v2/v3 opens are always allowed - so just add it. */
6087 nfs_open_file_add_open(nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, 0);
6088 } else {
6089 error = nfs4_open(np, nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, ctx);
6090 }
6091 }
6092 if (!error)
6093 nofp->nof_flags |= NFS_OPEN_FILE_NEEDCLOSE;
6094 if (nofp)
6095 nfs_open_file_clear_busy(nofp);
6096 if (nfs_mount_state_in_use_end(nmp, error)) {
6097 nofp = NULL;
6098 goto restart;
6099 }
6100 }
6101 nfs_open_owner_rele(noop);
6102 if (error)
6103 return (error);
6104 return (nfs_bioread(VTONFS(ap->a_vp), ap->a_uio, ap->a_ioflag, ap->a_context));
6105}
6106
6107/*
6108 * Note: the NFSv4 CREATE RPC is for everything EXCEPT regular files.
6109 * Files are created using the NFSv4 OPEN RPC. So we must open the
6110 * file to create it and then close it.
6111 */
6112int
6113nfs4_vnop_create(
6114 struct vnop_create_args /* {
6115 struct vnodeop_desc *a_desc;
6116 vnode_t a_dvp;
6117 vnode_t *a_vpp;
6118 struct componentname *a_cnp;
6119 struct vnode_attr *a_vap;
6120 vfs_context_t a_context;
6121 } */ *ap)
6122{
6123 vfs_context_t ctx = ap->a_context;
6124 struct componentname *cnp = ap->a_cnp;
6125 struct vnode_attr *vap = ap->a_vap;
6126 vnode_t dvp = ap->a_dvp;
6127 vnode_t *vpp = ap->a_vpp;
6128 struct nfsmount *nmp;
6129 nfsnode_t np;
6130 int error = 0, busyerror = 0, accessMode, denyMode;
6131 struct nfs_open_owner *noop = NULL;
6132 struct nfs_open_file *newnofp = NULL, *nofp = NULL;
6133
6134 nmp = VTONMP(dvp);
fe8ab488 6135 if (nfs_mount_gone(nmp))
6d2010ae
A
6136 return (ENXIO);
6137
6138 if (vap)
6139 nfs_avoid_needless_id_setting_on_create(VTONFS(dvp), vap, ctx);
6140
6141 noop = nfs_open_owner_find(nmp, vfs_context_ucred(ctx), 1);
6142 if (!noop)
6143 return (ENOMEM);
6144
6145restart:
6146 error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx));
6147 if (error) {
6148 nfs_open_owner_rele(noop);
6149 return (error);
6150 }
6151
6152 /* grab a provisional, nodeless open file */
6153 error = nfs_open_file_find(NULL, noop, &newnofp, 0, 0, 1);
6154 if (!error && (newnofp->nof_flags & NFS_OPEN_FILE_LOST)) {
6155 printf("nfs_vnop_create: LOST\n");
6156 error = EIO;
6157 }
6158 if (!error && (newnofp->nof_flags & NFS_OPEN_FILE_REOPEN)) {
6159 /* This shouldn't happen given that this is a new, nodeless nofp */
6160 nfs_mount_state_in_use_end(nmp, 0);
6161 error = nfs4_reopen(newnofp, vfs_context_thread(ctx));
6162 nfs_open_file_destroy(newnofp);
6163 newnofp = NULL;
6164 if (!error)
6165 goto restart;
6166 }
6167 if (!error)
6168 error = nfs_open_file_set_busy(newnofp, vfs_context_thread(ctx));
6169 if (error) {
6170 if (newnofp)
6171 nfs_open_file_destroy(newnofp);
6172 newnofp = NULL;
6173 goto out;
6174 }
6175
6176 /*
6177 * We're just trying to create the file.
6178 * We'll create/open it RW, and set NFS_OPEN_FILE_CREATE.
6179 */
6180 accessMode = NFS_OPEN_SHARE_ACCESS_BOTH;
6181 denyMode = NFS_OPEN_SHARE_DENY_NONE;
6182
6183 /* Do the open/create */
6184 error = nfs4_open_rpc(newnofp, ctx, cnp, vap, dvp, vpp, NFS_OPEN_CREATE, accessMode, denyMode);
6185 if ((error == EACCES) && vap && !(vap->va_vaflags & VA_EXCLUSIVE) &&
6186 VATTR_IS_ACTIVE(vap, va_mode) && !(vap->va_mode & S_IWUSR)) {
6187 /*
6188 * Hmm... it looks like we may have a situation where the request was
6189 * retransmitted because we didn't get the first response which successfully
6190 * created/opened the file and then the second time we were denied the open
6191 * because the mode the file was created with doesn't allow write access.
6192 *
6193 * We'll try to work around this by temporarily updating the mode and
6194 * retrying the open.
6195 */
6196 struct vnode_attr vattr;
6197
6198 /* first make sure it's there */
6199 int error2 = nfs_lookitup(VTONFS(dvp), cnp->cn_nameptr, cnp->cn_namelen, ctx, &np);
6200 if (!error2 && np) {
6201 nfs_node_unlock(np);
6202 *vpp = NFSTOV(np);
6203 if (vnode_vtype(NFSTOV(np)) == VREG) {
6204 VATTR_INIT(&vattr);
6205 VATTR_SET(&vattr, va_mode, (vap->va_mode | S_IWUSR));
6206 if (!nfs4_setattr_rpc(np, &vattr, ctx)) {
6207 error2 = nfs4_open_rpc(newnofp, ctx, cnp, NULL, dvp, vpp, NFS_OPEN_NOCREATE, accessMode, denyMode);
6208 VATTR_INIT(&vattr);
6209 VATTR_SET(&vattr, va_mode, vap->va_mode);
6210 nfs4_setattr_rpc(np, &vattr, ctx);
6211 if (!error2)
6212 error = 0;
6213 }
6214 }
6215 if (error) {
6216 vnode_put(*vpp);
6217 *vpp = NULL;
6218 }
6219 }
6220 }
6221 if (!error && !*vpp) {
6222 printf("nfs4_open_rpc returned without a node?\n");
6223 /* Hmmm... with no node, we have no filehandle and can't close it */
6224 error = EIO;
6225 }
6226 if (error) {
6227 /* need to cleanup our temporary nofp */
6228 nfs_open_file_clear_busy(newnofp);
6229 nfs_open_file_destroy(newnofp);
6230 newnofp = NULL;
6231 goto out;
6232 }
6233 /* After we have a node, add our open file struct to the node */
6234 np = VTONFS(*vpp);
6235 nfs_open_file_add_open(newnofp, accessMode, denyMode, 0);
6236 nofp = newnofp;
6237 error = nfs_open_file_find_internal(np, noop, &nofp, 0, 0, 0);
6238 if (error) {
6239 /* This shouldn't happen, because we passed in a new nofp to use. */
6240 printf("nfs_open_file_find_internal failed! %d\n", error);
6241 goto out;
6242 } else if (nofp != newnofp) {
6243 /*
6244 * Hmm... an open file struct already exists.
6245 * Mark the existing one busy and merge our open into it.
6246 * Then destroy the one we created.
6247 * Note: there's no chance of an open confict because the
6248 * open has already been granted.
6249 */
6250 busyerror = nfs_open_file_set_busy(nofp, NULL);
6251 nfs_open_file_add_open(nofp, accessMode, denyMode, 0);
6252 nofp->nof_stateid = newnofp->nof_stateid;
6253 if (newnofp->nof_flags & NFS_OPEN_FILE_POSIXLOCK)
6254 nofp->nof_flags |= NFS_OPEN_FILE_POSIXLOCK;
6255 nfs_open_file_clear_busy(newnofp);
6256 nfs_open_file_destroy(newnofp);
6257 }
6258 newnofp = NULL;
6259 /* mark the node as holding a create-initiated open */
6260 nofp->nof_flags |= NFS_OPEN_FILE_CREATE;
6261 nofp->nof_creator = current_thread();
6262out:
6263 if (nofp && !busyerror)
6264 nfs_open_file_clear_busy(nofp);
6265 if (nfs_mount_state_in_use_end(nmp, error)) {
6266 nofp = newnofp = NULL;
6267 busyerror = 0;
6268 goto restart;
6269 }
6270 if (noop)
6271 nfs_open_owner_rele(noop);
6272 return (error);
6273}
6274
6275/*
6276 * Note: the NFSv4 CREATE RPC is for everything EXCEPT regular files.
6277 */
6278int
6279nfs4_create_rpc(
6280 vfs_context_t ctx,
6281 nfsnode_t dnp,
6282 struct componentname *cnp,
6283 struct vnode_attr *vap,
6284 int type,
6285 char *link,
6286 nfsnode_t *npp)
6287{
6288 struct nfsmount *nmp;
6289 struct nfs_vattr nvattr;
6290 int error = 0, create_error = EIO, lockerror = ENOENT, busyerror = ENOENT, status;
6291 int nfsvers, namedattrs, numops;
6292 u_int64_t xid, savedxid = 0;
6293 nfsnode_t np = NULL;
6294 vnode_t newvp = NULL;
6295 struct nfsm_chain nmreq, nmrep;
6296 uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen;
6297 const char *tag;
6298 nfs_specdata sd;
6299 fhandle_t fh;
6300 struct nfsreq rq, *req = &rq;
6301 struct nfs_dulookup dul;
6302 struct nfsreq_secinfo_args si;
6303
6304 nmp = NFSTONMP(dnp);
fe8ab488 6305 if (nfs_mount_gone(nmp))
6d2010ae
A
6306 return (ENXIO);
6307 nfsvers = nmp->nm_vers;
6308 namedattrs = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR);
6309 if (dnp->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
6310 return (EINVAL);
6311
6312 sd.specdata1 = sd.specdata2 = 0;
6313
6314 switch (type) {
6315 case NFLNK:
6316 tag = "symlink";
6317 break;
6318 case NFBLK:
6319 case NFCHR:
6320 tag = "mknod";
6321 if (!VATTR_IS_ACTIVE(vap, va_rdev))
6322 return (EINVAL);
6323 sd.specdata1 = major(vap->va_rdev);
6324 sd.specdata2 = minor(vap->va_rdev);
6325 break;
6326 case NFSOCK:
6327 case NFFIFO:
6328 tag = "mknod";
6329 break;
6330 case NFDIR:
6331 tag = "mkdir";
6332 break;
6333 default:
6334 return (EINVAL);
6335 }
6336
6337 nfs_avoid_needless_id_setting_on_create(dnp, vap, ctx);
6338
6339 error = busyerror = nfs_node_set_busy(dnp, vfs_context_thread(ctx));
6340 if (!namedattrs)
6341 nfs_dulookup_init(&dul, dnp, cnp->cn_nameptr, cnp->cn_namelen, ctx);
6342
6343 NFSREQ_SECINFO_SET(&si, dnp, NULL, 0, NULL, 0);
6344 NVATTR_INIT(&nvattr);
6345 nfsm_chain_null(&nmreq);
6346 nfsm_chain_null(&nmrep);
6347
6348 // PUTFH, SAVEFH, CREATE, GETATTR(FH), RESTOREFH, GETATTR
6349 numops = 6;
6350 nfsm_chain_build_alloc_init(error, &nmreq, 66 * NFSX_UNSIGNED);
6351 nfsm_chain_add_compound_header(error, &nmreq, tag, numops);
6352 numops--;
6353 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
6354 nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
6355 numops--;
6356 nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
6357 numops--;
6358 nfsm_chain_add_32(error, &nmreq, NFS_OP_CREATE);
6359 nfsm_chain_add_32(error, &nmreq, type);
6360 if (type == NFLNK) {
6361 nfsm_chain_add_name(error, &nmreq, link, strlen(link), nmp);
6362 } else if ((type == NFBLK) || (type == NFCHR)) {
6363 nfsm_chain_add_32(error, &nmreq, sd.specdata1);
6364 nfsm_chain_add_32(error, &nmreq, sd.specdata2);
6365 }
6366 nfsm_chain_add_name(error, &nmreq, cnp->cn_nameptr, cnp->cn_namelen, nmp);
6367 nfsm_chain_add_fattr4(error, &nmreq, vap, nmp);
6368 numops--;
6369 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6370 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
6371 NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
6372 nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, NULL);
6373 numops--;
6374 nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
6375 numops--;
6376 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6377 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, dnp);
6378 nfsm_chain_build_done(error, &nmreq);
6379 nfsm_assert(error, (numops == 0), EPROTO);
6380 nfsmout_if(error);
6381
6382 error = nfs_request_async(dnp, NULL, &nmreq, NFSPROC4_COMPOUND,
6383 vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
6384 if (!error) {
6385 if (!namedattrs)
6386 nfs_dulookup_start(&dul, dnp, ctx);
6387 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
6388 }
6389
6390 if ((lockerror = nfs_node_lock(dnp)))
6391 error = lockerror;
6392 nfsm_chain_skip_tag(error, &nmrep);
6393 nfsm_chain_get_32(error, &nmrep, numops);
6394 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
6395 nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
6396 nfsmout_if(error);
6397 nfsm_chain_op_check(error, &nmrep, NFS_OP_CREATE);
6398 nfsm_chain_check_change_info(error, &nmrep, dnp);
6399 bmlen = NFS_ATTR_BITMAP_LEN;
6400 nfsm_chain_get_bitmap(error, &nmrep, bitmap, bmlen);
6401 /* At this point if we have no error, the object was created. */
6402 /* if we don't get attributes, then we should lookitup. */
6403 create_error = error;
6404 nfsmout_if(error);
6405 nfs_vattr_set_supported(bitmap, vap);
6406 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6407 nfsmout_if(error);
6408 error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL, NULL);
6409 nfsmout_if(error);
6410 if (!NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
6411 printf("nfs: create/%s didn't return filehandle? %s\n", tag, cnp->cn_nameptr);
6412 error = EBADRPC;
6413 goto nfsmout;
6414 }
6415 /* directory attributes: if we don't get them, make sure to invalidate */
6416 nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
6417 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6418 savedxid = xid;
6419 nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, &xid);
6420 if (error)
6421 NATTRINVALIDATE(dnp);
6422
6423nfsmout:
6424 nfsm_chain_cleanup(&nmreq);
6425 nfsm_chain_cleanup(&nmrep);
6426
6427 if (!lockerror) {
6428 if (!create_error && (dnp->n_flag & NNEGNCENTRIES)) {
6429 dnp->n_flag &= ~NNEGNCENTRIES;
6430 cache_purge_negatives(NFSTOV(dnp));
6431 }
6432 dnp->n_flag |= NMODIFIED;
6433 nfs_node_unlock(dnp);
6434 /* nfs_getattr() will check changed and purge caches */
6435 nfs_getattr(dnp, NULL, ctx, NGA_CACHED);
6436 }
6437
6438 if (!error && fh.fh_len) {
6439 /* create the vnode with the filehandle and attributes */
6440 xid = savedxid;
6441 error = nfs_nget(NFSTOMP(dnp), dnp, cnp, fh.fh_data, fh.fh_len, &nvattr, &xid, rq.r_auth, NG_MAKEENTRY, &np);
6442 if (!error)
6443 newvp = NFSTOV(np);
6444 }
6445 NVATTR_CLEANUP(&nvattr);
6446
6447 if (!namedattrs)
6448 nfs_dulookup_finish(&dul, dnp, ctx);
6449
6450 /*
6451 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
6452 * if we can succeed in looking up the object.
6453 */
6454 if ((create_error == EEXIST) || (!create_error && !newvp)) {
6455 error = nfs_lookitup(dnp, cnp->cn_nameptr, cnp->cn_namelen, ctx, &np);
6456 if (!error) {
6457 newvp = NFSTOV(np);
6458 if (vnode_vtype(newvp) != nfstov_type(type, nfsvers))
6459 error = EEXIST;
6460 }
6461 }
6462 if (!busyerror)
6463 nfs_node_clear_busy(dnp);
6464 if (error) {
6465 if (newvp) {
6466 nfs_node_unlock(np);
6467 vnode_put(newvp);
6468 }
6469 } else {
6470 nfs_node_unlock(np);
6471 *npp = np;
6472 }
6473 return (error);
6474}
6475
6476int
6477nfs4_vnop_mknod(
6478 struct vnop_mknod_args /* {
6479 struct vnodeop_desc *a_desc;
6480 vnode_t a_dvp;
6481 vnode_t *a_vpp;
6482 struct componentname *a_cnp;
6483 struct vnode_attr *a_vap;
6484 vfs_context_t a_context;
6485 } */ *ap)
6486{
6487 nfsnode_t np = NULL;
6488 struct nfsmount *nmp;
6489 int error;
6490
6491 nmp = VTONMP(ap->a_dvp);
fe8ab488 6492 if (nfs_mount_gone(nmp))
6d2010ae
A
6493 return (ENXIO);
6494
6495 if (!VATTR_IS_ACTIVE(ap->a_vap, va_type))
6496 return (EINVAL);
6497 switch (ap->a_vap->va_type) {
6498 case VBLK:
6499 case VCHR:
6500 case VFIFO:
6501 case VSOCK:
6502 break;
6503 default:
6504 return (ENOTSUP);
6505 }
6506
6507 error = nfs4_create_rpc(ap->a_context, VTONFS(ap->a_dvp), ap->a_cnp, ap->a_vap,
6508 vtonfs_type(ap->a_vap->va_type, nmp->nm_vers), NULL, &np);
6509 if (!error)
6510 *ap->a_vpp = NFSTOV(np);
6511 return (error);
6512}
6513
6514int
6515nfs4_vnop_mkdir(
6516 struct vnop_mkdir_args /* {
6517 struct vnodeop_desc *a_desc;
6518 vnode_t a_dvp;
6519 vnode_t *a_vpp;
6520 struct componentname *a_cnp;
6521 struct vnode_attr *a_vap;
6522 vfs_context_t a_context;
6523 } */ *ap)
6524{
6525 nfsnode_t np = NULL;
6526 int error;
6527
6528 error = nfs4_create_rpc(ap->a_context, VTONFS(ap->a_dvp), ap->a_cnp, ap->a_vap,
6529 NFDIR, NULL, &np);
6530 if (!error)
6531 *ap->a_vpp = NFSTOV(np);
6532 return (error);
6533}
6534
6535int
6536nfs4_vnop_symlink(
6537 struct vnop_symlink_args /* {
6538 struct vnodeop_desc *a_desc;
6539 vnode_t a_dvp;
6540 vnode_t *a_vpp;
6541 struct componentname *a_cnp;
6542 struct vnode_attr *a_vap;
6543 char *a_target;
6544 vfs_context_t a_context;
6545 } */ *ap)
6546{
6547 nfsnode_t np = NULL;
6548 int error;
6549
6550 error = nfs4_create_rpc(ap->a_context, VTONFS(ap->a_dvp), ap->a_cnp, ap->a_vap,
6551 NFLNK, ap->a_target, &np);
6552 if (!error)
6553 *ap->a_vpp = NFSTOV(np);
6554 return (error);
6555}
6556
6557int
6558nfs4_vnop_link(
6559 struct vnop_link_args /* {
6560 struct vnodeop_desc *a_desc;
6561 vnode_t a_vp;
6562 vnode_t a_tdvp;
6563 struct componentname *a_cnp;
6564 vfs_context_t a_context;
6565 } */ *ap)
6566{
6567 vfs_context_t ctx = ap->a_context;
6568 vnode_t vp = ap->a_vp;
6569 vnode_t tdvp = ap->a_tdvp;
6570 struct componentname *cnp = ap->a_cnp;
6571 int error = 0, lockerror = ENOENT, status;
6572 struct nfsmount *nmp;
6573 nfsnode_t np = VTONFS(vp);
6574 nfsnode_t tdnp = VTONFS(tdvp);
6575 int nfsvers, numops;
6576 u_int64_t xid, savedxid;
6577 struct nfsm_chain nmreq, nmrep;
6578 struct nfsreq_secinfo_args si;
6579
6580 if (vnode_mount(vp) != vnode_mount(tdvp))
6581 return (EXDEV);
6582
6583 nmp = VTONMP(vp);
fe8ab488 6584 if (nfs_mount_gone(nmp))
6d2010ae
A
6585 return (ENXIO);
6586 nfsvers = nmp->nm_vers;
6587 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
6588 return (EINVAL);
6589 if (tdnp->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
6590 return (EINVAL);
6591
6592 /*
6593 * Push all writes to the server, so that the attribute cache
6594 * doesn't get "out of sync" with the server.
6595 * XXX There should be a better way!
6596 */
6597 nfs_flush(np, MNT_WAIT, vfs_context_thread(ctx), V_IGNORE_WRITEERR);
6598
6599 if ((error = nfs_node_set_busy2(tdnp, np, vfs_context_thread(ctx))))
6600 return (error);
6601
6602 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
6603 nfsm_chain_null(&nmreq);
6604 nfsm_chain_null(&nmrep);
6605
6606 // PUTFH(SOURCE), SAVEFH, PUTFH(DIR), LINK, GETATTR(DIR), RESTOREFH, GETATTR
6607 numops = 7;
6608 nfsm_chain_build_alloc_init(error, &nmreq, 29 * NFSX_UNSIGNED + cnp->cn_namelen);
6609 nfsm_chain_add_compound_header(error, &nmreq, "link", numops);
6610 numops--;
6611 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
6612 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
6613 numops--;
6614 nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
6615 numops--;
6616 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
6617 nfsm_chain_add_fh(error, &nmreq, nfsvers, tdnp->n_fhp, tdnp->n_fhsize);
6618 numops--;
6619 nfsm_chain_add_32(error, &nmreq, NFS_OP_LINK);
6620 nfsm_chain_add_name(error, &nmreq, cnp->cn_nameptr, cnp->cn_namelen, nmp);
6621 numops--;
6622 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6623 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, tdnp);
6624 numops--;
6625 nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
6626 numops--;
6627 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6628 nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, np);
6629 nfsm_chain_build_done(error, &nmreq);
6630 nfsm_assert(error, (numops == 0), EPROTO);
6631 nfsmout_if(error);
6632 error = nfs_request(tdnp, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &si, &nmrep, &xid, &status);
6633
6634 if ((lockerror = nfs_node_lock2(tdnp, np))) {
6635 error = lockerror;
6636 goto nfsmout;
6637 }
6638 nfsm_chain_skip_tag(error, &nmrep);
6639 nfsm_chain_get_32(error, &nmrep, numops);
6640 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
6641 nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
6642 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
6643 nfsm_chain_op_check(error, &nmrep, NFS_OP_LINK);
6644 nfsm_chain_check_change_info(error, &nmrep, tdnp);
6645 /* directory attributes: if we don't get them, make sure to invalidate */
6646 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6647 savedxid = xid;
6648 nfsm_chain_loadattr(error, &nmrep, tdnp, nfsvers, &xid);
6649 if (error)
6650 NATTRINVALIDATE(tdnp);
6651 /* link attributes: if we don't get them, make sure to invalidate */
6652 nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
6653 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6654 xid = savedxid;
6655 nfsm_chain_loadattr(error, &nmrep, np, nfsvers, &xid);
6656 if (error)
6657 NATTRINVALIDATE(np);
6658nfsmout:
6659 nfsm_chain_cleanup(&nmreq);
6660 nfsm_chain_cleanup(&nmrep);
6661 if (!lockerror)
6662 tdnp->n_flag |= NMODIFIED;
6663 /* Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */
6664 if (error == EEXIST)
6665 error = 0;
6666 if (!error && (tdnp->n_flag & NNEGNCENTRIES)) {
6667 tdnp->n_flag &= ~NNEGNCENTRIES;
6668 cache_purge_negatives(tdvp);
6669 }
6670 if (!lockerror)
6671 nfs_node_unlock2(tdnp, np);
6672 nfs_node_clear_busy2(tdnp, np);
6673 return (error);
6674}
6675
6676int
6677nfs4_vnop_rmdir(
6678 struct vnop_rmdir_args /* {
6679 struct vnodeop_desc *a_desc;
6680 vnode_t a_dvp;
6681 vnode_t a_vp;
6682 struct componentname *a_cnp;
6683 vfs_context_t a_context;
6684 } */ *ap)
6685{
6686 vfs_context_t ctx = ap->a_context;
6687 vnode_t vp = ap->a_vp;
6688 vnode_t dvp = ap->a_dvp;
6689 struct componentname *cnp = ap->a_cnp;
6690 struct nfsmount *nmp;
6691 int error = 0, namedattrs;
6692 nfsnode_t np = VTONFS(vp);
6693 nfsnode_t dnp = VTONFS(dvp);
6694 struct nfs_dulookup dul;
6695
6696 if (vnode_vtype(vp) != VDIR)
6697 return (EINVAL);
6698
6699 nmp = NFSTONMP(dnp);
fe8ab488 6700 if (nfs_mount_gone(nmp))
6d2010ae
A
6701 return (ENXIO);
6702 namedattrs = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR);
6703
6704 if ((error = nfs_node_set_busy2(dnp, np, vfs_context_thread(ctx))))
6705 return (error);
6706
6707 if (!namedattrs) {
6708 nfs_dulookup_init(&dul, dnp, cnp->cn_nameptr, cnp->cn_namelen, ctx);
6709 nfs_dulookup_start(&dul, dnp, ctx);
6710 }
6711
6712 error = nfs4_remove_rpc(dnp, cnp->cn_nameptr, cnp->cn_namelen,
6713 vfs_context_thread(ctx), vfs_context_ucred(ctx));
6714
6715 nfs_name_cache_purge(dnp, np, cnp, ctx);
6716 /* nfs_getattr() will check changed and purge caches */
6717 nfs_getattr(dnp, NULL, ctx, NGA_CACHED);
6718 if (!namedattrs)
6719 nfs_dulookup_finish(&dul, dnp, ctx);
6720 nfs_node_clear_busy2(dnp, np);
6721
6722 /*
6723 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
6724 */
6725 if (error == ENOENT)
6726 error = 0;
6727 if (!error) {
6728 /*
6729 * remove nfsnode from hash now so we can't accidentally find it
6730 * again if another object gets created with the same filehandle
6731 * before this vnode gets reclaimed
6732 */
6733 lck_mtx_lock(nfs_node_hash_mutex);
6734 if (np->n_hflag & NHHASHED) {
6735 LIST_REMOVE(np, n_hash);
6736 np->n_hflag &= ~NHHASHED;
6737 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
6738 }
6739 lck_mtx_unlock(nfs_node_hash_mutex);
6740 }
6741 return (error);
6742}
6743
6744/*
6745 * NFSv4 Named Attributes
6746 *
6747 * Both the extended attributes interface and the named streams interface
6748 * are backed by NFSv4 named attributes. The implementations for both use
6749 * a common set of routines in an attempt to reduce code duplication, to
6750 * increase efficiency, to increase caching of both names and data, and to
6751 * confine the complexity.
6752 *
6753 * Each NFS node caches its named attribute directory's file handle.
6754 * The directory nodes for the named attribute directories are handled
6755 * exactly like regular directories (with a couple minor exceptions).
6756 * Named attribute nodes are also treated as much like regular files as
6757 * possible.
6758 *
6759 * Most of the heavy lifting is done by nfs4_named_attr_get().
6760 */
6761
6762/*
6763 * Get the given node's attribute directory node.
6764 * If !fetch, then only return a cached node.
6765 * Otherwise, we will attempt to fetch the node from the server.
6766 * (Note: the node should be marked busy.)
b0d623f7 6767 */
6d2010ae
A
6768nfsnode_t
6769nfs4_named_attr_dir_get(nfsnode_t np, int fetch, vfs_context_t ctx)
b0d623f7 6770{
6d2010ae 6771 nfsnode_t adnp = NULL;
b0d623f7 6772 struct nfsmount *nmp;
6d2010ae
A
6773 int error = 0, status, numops;
6774 struct nfsm_chain nmreq, nmrep;
6775 u_int64_t xid;
6776 uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
6777 fhandle_t fh;
6778 struct nfs_vattr nvattr;
6779 struct componentname cn;
6780 struct nfsreq rq, *req = &rq;
6781 struct nfsreq_secinfo_args si;
b0d623f7 6782
6d2010ae 6783 nmp = NFSTONMP(np);
fe8ab488 6784 if (nfs_mount_gone(nmp))
6d2010ae
A
6785 return (NULL);
6786 if (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
6787 return (NULL);
b0d623f7 6788
6d2010ae
A
6789 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
6790 NVATTR_INIT(&nvattr);
6791 nfsm_chain_null(&nmreq);
6792 nfsm_chain_null(&nmrep);
b0d623f7 6793
6d2010ae
A
6794 bzero(&cn, sizeof(cn));
6795 cn.cn_nameptr = __CAST_AWAY_QUALIFIER(_PATH_FORKSPECIFIER, const, char *); /* "/..namedfork/" */
6796 cn.cn_namelen = strlen(_PATH_FORKSPECIFIER);
6797 cn.cn_nameiop = LOOKUP;
6798
6799 if (np->n_attrdirfh) {
6800 // XXX can't set parent correctly (to np) yet
6801 error = nfs_nget(nmp->nm_mountp, NULL, &cn, np->n_attrdirfh+1, *np->n_attrdirfh,
6802 NULL, NULL, RPCAUTH_UNKNOWN, NG_NOCREATE, &adnp);
6803 if (adnp)
6804 goto nfsmout;
6805 }
6806 if (!fetch) {
6807 error = ENOENT;
6808 goto nfsmout;
2d21ac55
A
6809 }
6810
6d2010ae
A
6811 // PUTFH, OPENATTR, GETATTR
6812 numops = 3;
6813 nfsm_chain_build_alloc_init(error, &nmreq, 22 * NFSX_UNSIGNED);
6814 nfsm_chain_add_compound_header(error, &nmreq, "openattr", numops);
6815 numops--;
6816 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
6817 nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, np->n_fhp, np->n_fhsize);
6818 numops--;
6819 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPENATTR);
6820 nfsm_chain_add_32(error, &nmreq, 0);
6821 numops--;
6822 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
6823 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
6824 NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
6825 nfsm_chain_add_bitmap_masked(error, &nmreq, bitmap,
6826 NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
6827 nfsm_chain_build_done(error, &nmreq);
6828 nfsm_assert(error, (numops == 0), EPROTO);
6829 nfsmout_if(error);
6830 error = nfs_request_async(np, NULL, &nmreq, NFSPROC4_COMPOUND,
6831 vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
b0d623f7 6832 if (!error)
6d2010ae 6833 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
b0d623f7 6834
6d2010ae
A
6835 nfsm_chain_skip_tag(error, &nmrep);
6836 nfsm_chain_get_32(error, &nmrep, numops);
6837 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
6838 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPENATTR);
6839 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6840 nfsmout_if(error);
6841 error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL, NULL);
6842 nfsmout_if(error);
6843 if (!NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE) || !fh.fh_len) {
6844 error = ENOENT;
6845 goto nfsmout;
2d21ac55 6846 }
6d2010ae
A
6847 if (!np->n_attrdirfh || (*np->n_attrdirfh != fh.fh_len)) {
6848 /* (re)allocate attrdir fh buffer */
6849 if (np->n_attrdirfh)
6850 FREE(np->n_attrdirfh, M_TEMP);
6851 MALLOC(np->n_attrdirfh, u_char*, fh.fh_len+1, M_TEMP, M_WAITOK);
2d21ac55 6852 }
6d2010ae
A
6853 if (!np->n_attrdirfh) {
6854 error = ENOMEM;
6855 goto nfsmout;
b0d623f7 6856 }
6d2010ae
A
6857 /* cache the attrdir fh in the node */
6858 *np->n_attrdirfh = fh.fh_len;
6859 bcopy(fh.fh_data, np->n_attrdirfh+1, fh.fh_len);
6860 /* create node for attrdir */
6861 // XXX can't set parent correctly (to np) yet
6862 error = nfs_nget(NFSTOMP(np), NULL, &cn, fh.fh_data, fh.fh_len, &nvattr, &xid, rq.r_auth, 0, &adnp);
6863nfsmout:
6864 NVATTR_CLEANUP(&nvattr);
6865 nfsm_chain_cleanup(&nmreq);
6866 nfsm_chain_cleanup(&nmrep);
2d21ac55 6867
6d2010ae
A
6868 if (adnp) {
6869 /* sanity check that this node is an attribute directory */
6870 if (adnp->n_vattr.nva_type != VDIR)
6871 error = EINVAL;
6872 if (!(adnp->n_vattr.nva_flags & NFS_FFLAG_IS_ATTR))
6873 error = EINVAL;
6874 nfs_node_unlock(adnp);
6875 if (error)
6876 vnode_put(NFSTOV(adnp));
b0d623f7 6877 }
6d2010ae 6878 return (error ? NULL : adnp);
b0d623f7
A
6879}
6880
2d21ac55 6881/*
6d2010ae
A
6882 * Get the given node's named attribute node for the name given.
6883 *
6884 * In an effort to increase the performance of named attribute access, we try
6885 * to reduce server requests by doing the following:
6886 *
6887 * - cache the node's named attribute directory file handle in the node
6888 * - maintain a directory vnode for the attribute directory
6889 * - use name cache entries (positive and negative) to speed up lookups
6890 * - optionally open the named attribute (with the given accessMode) in the same RPC
6891 * - combine attribute directory retrieval with the lookup/open RPC
6892 * - optionally prefetch the named attribute's first block of data in the same RPC
6893 *
6894 * Also, in an attempt to reduce the number of copies/variations of this code,
6895 * parts of the RPC building/processing code are conditionalized on what is
6896 * needed for any particular request (openattr, lookup vs. open, read).
6897 *
6898 * Note that because we may not have the attribute directory node when we start
6899 * the lookup/open, we lock both the node and the attribute directory node.
2d21ac55 6900 */
6d2010ae
A
6901
6902#define NFS_GET_NAMED_ATTR_CREATE 0x1
6903#define NFS_GET_NAMED_ATTR_CREATE_GUARDED 0x2
6904#define NFS_GET_NAMED_ATTR_TRUNCATE 0x4
6905#define NFS_GET_NAMED_ATTR_PREFETCH 0x8
6906
b0d623f7 6907int
6d2010ae
A
6908nfs4_named_attr_get(
6909 nfsnode_t np,
2d21ac55 6910 struct componentname *cnp,
6d2010ae
A
6911 uint32_t accessMode,
6912 int flags,
6913 vfs_context_t ctx,
6914 nfsnode_t *anpp,
6915 struct nfs_open_file **nofpp)
2d21ac55
A
6916{
6917 struct nfsmount *nmp;
6d2010ae
A
6918 int error = 0, open_error = EIO;
6919 int inuse = 0, adlockerror = ENOENT, busyerror = ENOENT, adbusyerror = ENOENT, nofpbusyerror = ENOENT;
6920 int create, guarded, prefetch, truncate, noopbusy = 0;
6921 int open, status, numops, hadattrdir, negnamecache;
6922 struct nfs_vattr nvattr;
6923 struct vnode_attr vattr;
6924 nfsnode_t adnp = NULL, anp = NULL;
6925 vnode_t avp = NULL;
2d21ac55 6926 u_int64_t xid, savedxid = 0;
2d21ac55
A
6927 struct nfsm_chain nmreq, nmrep;
6928 uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen;
6d2010ae
A
6929 uint32_t denyMode, rflags, delegation, recall, eof, rlen, retlen;
6930 nfs_stateid stateid, dstateid;
2d21ac55 6931 fhandle_t fh;
6d2010ae
A
6932 struct nfs_open_owner *noop = NULL;
6933 struct nfs_open_file *newnofp = NULL, *nofp = NULL;
6934 struct vnop_access_args naa;
6935 thread_t thd;
6936 kauth_cred_t cred;
6937 struct timeval now;
6938 char sbuf[64], *s;
6939 uint32_t ace_type, ace_flags, ace_mask, len, slen;
6940 struct kauth_ace ace;
6941 struct nfsreq rq, *req = &rq;
6942 struct nfsreq_secinfo_args si;
6943
6944 *anpp = NULL;
6945 fh.fh_len = 0;
6946 rflags = delegation = recall = eof = rlen = retlen = 0;
6947 ace.ace_flags = 0;
6948 s = sbuf;
6949 slen = sizeof(sbuf);
2d21ac55 6950
6d2010ae 6951 nmp = NFSTONMP(np);
fe8ab488 6952 if (nfs_mount_gone(nmp))
2d21ac55 6953 return (ENXIO);
6d2010ae
A
6954 NVATTR_INIT(&nvattr);
6955 negnamecache = !NMFLAG(nmp, NONEGNAMECACHE);
6956 thd = vfs_context_thread(ctx);
6957 cred = vfs_context_ucred(ctx);
6958 create = (flags & NFS_GET_NAMED_ATTR_CREATE) ? NFS_OPEN_CREATE : NFS_OPEN_NOCREATE;
6959 guarded = (flags & NFS_GET_NAMED_ATTR_CREATE_GUARDED) ? NFS_CREATE_GUARDED : NFS_CREATE_UNCHECKED;
6960 truncate = (flags & NFS_GET_NAMED_ATTR_TRUNCATE);
6961 prefetch = (flags & NFS_GET_NAMED_ATTR_PREFETCH);
6962
6963 if (!create) {
6964 error = nfs_getattr(np, &nvattr, ctx, NGA_CACHED);
6965 if (error)
6966 return (error);
6967 if (NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_NAMED_ATTR) &&
6968 !(nvattr.nva_flags & NFS_FFLAG_HAS_NAMED_ATTRS))
6969 return (ENOATTR);
6970 } else if (accessMode == NFS_OPEN_SHARE_ACCESS_NONE) {
6971 /* shouldn't happen... but just be safe */
6972 printf("nfs4_named_attr_get: create with no access %s\n", cnp->cn_nameptr);
6973 accessMode = NFS_OPEN_SHARE_ACCESS_READ;
6974 }
6975 open = (accessMode != NFS_OPEN_SHARE_ACCESS_NONE);
6976 if (open) {
6977 /*
6978 * We're trying to open the file.
6979 * We'll create/open it with the given access mode,
6980 * and set NFS_OPEN_FILE_CREATE.
6981 */
6982 denyMode = NFS_OPEN_SHARE_DENY_NONE;
6983 if (prefetch && guarded)
6984 prefetch = 0; /* no sense prefetching data that can't be there */
2d21ac55 6985
6d2010ae
A
6986 noop = nfs_open_owner_find(nmp, vfs_context_ucred(ctx), 1);
6987 if (!noop)
6988 return (ENOMEM);
2d21ac55
A
6989 }
6990
6d2010ae
A
6991 if ((error = busyerror = nfs_node_set_busy(np, vfs_context_thread(ctx))))
6992 return (error);
2d21ac55 6993
6d2010ae
A
6994 adnp = nfs4_named_attr_dir_get(np, 0, ctx);
6995 hadattrdir = (adnp != NULL);
6996 if (prefetch) {
6997 microuptime(&now);
6998 /* use the special state ID because we don't have a real one to send */
6999 stateid.seqid = stateid.other[0] = stateid.other[1] = stateid.other[2] = 0;
7000 rlen = MIN(nmp->nm_rsize, nmp->nm_biosize);
7001 }
7002 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
2d21ac55
A
7003 nfsm_chain_null(&nmreq);
7004 nfsm_chain_null(&nmrep);
7005
6d2010ae
A
7006 if (hadattrdir) {
7007 if ((error = adbusyerror = nfs_node_set_busy(adnp, vfs_context_thread(ctx))))
7008 goto nfsmout;
7009 /* nfs_getattr() will check changed and purge caches */
7010 error = nfs_getattr(adnp, NULL, ctx, NGA_CACHED);
7011 nfsmout_if(error);
7012 error = cache_lookup(NFSTOV(adnp), &avp, cnp);
7013 switch (error) {
7014 case ENOENT:
7015 /* negative cache entry */
7016 goto nfsmout;
7017 case 0:
7018 /* cache miss */
7019 /* try dir buf cache lookup */
7020 error = nfs_dir_buf_cache_lookup(adnp, &anp, cnp, ctx, 0);
7021 if (!error && anp) {
7022 /* dir buf cache hit */
7023 *anpp = anp;
7024 error = -1;
7025 }
7026 if (error != -1) /* cache miss */
7027 break;
7028 /* FALLTHROUGH */
7029 case -1:
7030 /* cache hit, not really an error */
316670eb 7031 OSAddAtomic64(1, &nfsstats.lookupcache_hits);
6d2010ae
A
7032 if (!anp && avp)
7033 *anpp = anp = VTONFS(avp);
7034
7035 nfs_node_clear_busy(adnp);
7036 adbusyerror = ENOENT;
7037
7038 /* check for directory access */
7039 naa.a_desc = &vnop_access_desc;
7040 naa.a_vp = NFSTOV(adnp);
7041 naa.a_action = KAUTH_VNODE_SEARCH;
7042 naa.a_context = ctx;
7043
7044 /* compute actual success/failure based on accessibility */
7045 error = nfs_vnop_access(&naa);
7046 /* FALLTHROUGH */
7047 default:
7048 /* we either found it, or hit an error */
7049 if (!error && guarded) {
7050 /* found cached entry but told not to use it */
7051 error = EEXIST;
7052 vnode_put(NFSTOV(anp));
7053 *anpp = anp = NULL;
7054 }
7055 /* we're done if error or we don't need to open */
7056 if (error || !open)
7057 goto nfsmout;
7058 /* no error and we need to open... */
7059 }
7060 }
7061
7062 if (open) {
7063restart:
7064 error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx));
7065 if (error) {
7066 nfs_open_owner_rele(noop);
7067 noop = NULL;
7068 goto nfsmout;
7069 }
7070 inuse = 1;
7071
7072 /* grab an open file - possibly provisional/nodeless if cache_lookup() failed */
7073 error = nfs_open_file_find(anp, noop, &newnofp, 0, 0, 1);
7074 if (!error && (newnofp->nof_flags & NFS_OPEN_FILE_LOST)) {
7075 printf("nfs4_named_attr_get: LOST %d %s\n", kauth_cred_getuid(noop->noo_cred), cnp->cn_nameptr);
7076 error = EIO;
7077 }
7078 if (!error && (newnofp->nof_flags & NFS_OPEN_FILE_REOPEN)) {
7079 nfs_mount_state_in_use_end(nmp, 0);
7080 error = nfs4_reopen(newnofp, vfs_context_thread(ctx));
7081 nfs_open_file_destroy(newnofp);
7082 newnofp = NULL;
7083 if (!error)
7084 goto restart;
7085 }
7086 if (!error)
7087 error = nfs_open_file_set_busy(newnofp, vfs_context_thread(ctx));
7088 if (error) {
7089 if (newnofp)
7090 nfs_open_file_destroy(newnofp);
7091 newnofp = NULL;
7092 goto nfsmout;
7093 }
7094 if (anp) {
7095 /*
7096 * We already have the node. So we just need to open
7097 * it - which we may be able to do with a delegation.
7098 */
7099 open_error = error = nfs4_open(anp, newnofp, accessMode, denyMode, ctx);
7100 if (!error) {
7101 /* open succeeded, so our open file is no longer temporary */
7102 nofp = newnofp;
7103 nofpbusyerror = 0;
7104 newnofp = NULL;
7105 if (nofpp)
7106 *nofpp = nofp;
7107 }
7108 goto nfsmout;
7109 }
7110 }
7111
7112 /*
7113 * We either don't have the attrdir or we didn't find the attribute
7114 * in the name cache, so we need to talk to the server.
7115 *
7116 * If we don't have the attrdir, we'll need to ask the server for that too.
7117 * If the caller is requesting that the attribute be created, we need to
7118 * make sure the attrdir is created.
7119 * The caller may also request that the first block of an existing attribute
7120 * be retrieved at the same time.
7121 */
7122
7123 if (open) {
7124 /* need to mark the open owner busy during the RPC */
7125 if ((error = nfs_open_owner_set_busy(noop, thd)))
7126 goto nfsmout;
7127 noopbusy = 1;
7128 }
7129
7130 /*
7131 * We'd like to get updated post-open/lookup attributes for the
7132 * directory and we may also want to prefetch some data via READ.
7133 * We'd like the READ results to be last so that we can leave the
7134 * data in the mbufs until the end.
7135 *
7136 * At a minimum we're sending: PUTFH, LOOKUP/OPEN, GETATTR, PUTFH, GETATTR
7137 */
7138 numops = 5;
7139 if (!hadattrdir)
7140 numops += 3; // also sending: OPENATTR, GETATTR, OPENATTR
7141 if (prefetch)
7142 numops += 4; // also sending: SAVEFH, RESTOREFH, NVERIFY, READ
7143 nfsm_chain_build_alloc_init(error, &nmreq, 64 * NFSX_UNSIGNED + cnp->cn_namelen);
7144 nfsm_chain_add_compound_header(error, &nmreq, "getnamedattr", numops);
7145 if (hadattrdir) {
7146 numops--;
7147 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
7148 nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, adnp->n_fhp, adnp->n_fhsize);
7149 } else {
7150 numops--;
7151 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
7152 nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, np->n_fhp, np->n_fhsize);
7153 numops--;
7154 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPENATTR);
7155 nfsm_chain_add_32(error, &nmreq, create ? 1 : 0);
7156 numops--;
7157 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
7158 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
7159 NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
7160 nfsm_chain_add_bitmap_masked(error, &nmreq, bitmap,
7161 NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
7162 }
7163 if (open) {
7164 numops--;
7165 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPEN);
7166 nfsm_chain_add_32(error, &nmreq, noop->noo_seqid);
7167 nfsm_chain_add_32(error, &nmreq, accessMode);
7168 nfsm_chain_add_32(error, &nmreq, denyMode);
7169 nfsm_chain_add_64(error, &nmreq, nmp->nm_clientid);
7170 nfsm_chain_add_32(error, &nmreq, NFSX_UNSIGNED);
7171 nfsm_chain_add_32(error, &nmreq, kauth_cred_getuid(noop->noo_cred));
7172 nfsm_chain_add_32(error, &nmreq, create);
7173 if (create) {
7174 nfsm_chain_add_32(error, &nmreq, guarded);
7175 VATTR_INIT(&vattr);
7176 if (truncate)
7177 VATTR_SET(&vattr, va_data_size, 0);
7178 nfsm_chain_add_fattr4(error, &nmreq, &vattr, nmp);
7179 }
7180 nfsm_chain_add_32(error, &nmreq, NFS_CLAIM_NULL);
7181 nfsm_chain_add_name(error, &nmreq, cnp->cn_nameptr, cnp->cn_namelen, nmp);
7182 } else {
7183 numops--;
7184 nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUP);
7185 nfsm_chain_add_name(error, &nmreq, cnp->cn_nameptr, cnp->cn_namelen, nmp);
2d21ac55 7186 }
2d21ac55
A
7187 numops--;
7188 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
7189 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
7190 NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
7191 nfsm_chain_add_bitmap_masked(error, &nmreq, bitmap,
7192 NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
6d2010ae
A
7193 if (prefetch) {
7194 numops--;
7195 nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
7196 }
7197 if (hadattrdir) {
7198 numops--;
7199 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
7200 nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, adnp->n_fhp, adnp->n_fhsize);
7201 } else {
7202 numops--;
7203 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
7204 nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, np->n_fhp, np->n_fhsize);
7205 numops--;
7206 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPENATTR);
7207 nfsm_chain_add_32(error, &nmreq, 0);
7208 }
2d21ac55
A
7209 numops--;
7210 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
7211 nfsm_chain_add_bitmap_masked(error, &nmreq, nfs_getattr_bitmap,
7212 NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
6d2010ae
A
7213 if (prefetch) {
7214 numops--;
7215 nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
7216 numops--;
7217 nfsm_chain_add_32(error, &nmreq, NFS_OP_NVERIFY);
7218 VATTR_INIT(&vattr);
7219 VATTR_SET(&vattr, va_data_size, 0);
7220 nfsm_chain_add_fattr4(error, &nmreq, &vattr, nmp);
7221 numops--;
7222 nfsm_chain_add_32(error, &nmreq, NFS_OP_READ);
7223 nfsm_chain_add_stateid(error, &nmreq, &stateid);
7224 nfsm_chain_add_64(error, &nmreq, 0);
7225 nfsm_chain_add_32(error, &nmreq, rlen);
7226 }
2d21ac55
A
7227 nfsm_chain_build_done(error, &nmreq);
7228 nfsm_assert(error, (numops == 0), EPROTO);
7229 nfsmout_if(error);
6d2010ae
A
7230 error = nfs_request_async(hadattrdir ? adnp : np, NULL, &nmreq, NFSPROC4_COMPOUND,
7231 vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, open ? R_NOINTR: 0, NULL, &req);
7232 if (!error)
2d21ac55 7233 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
2d21ac55 7234
6d2010ae
A
7235 if (hadattrdir && ((adlockerror = nfs_node_lock(adnp))))
7236 error = adlockerror;
7237 savedxid = xid;
2d21ac55
A
7238 nfsm_chain_skip_tag(error, &nmrep);
7239 nfsm_chain_get_32(error, &nmrep, numops);
7240 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
6d2010ae
A
7241 if (!hadattrdir) {
7242 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPENATTR);
7243 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
7244 nfsmout_if(error);
7245 error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL, NULL);
7246 nfsmout_if(error);
7247 if (NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE) && fh.fh_len) {
7248 if (!np->n_attrdirfh || (*np->n_attrdirfh != fh.fh_len)) {
7249 /* (re)allocate attrdir fh buffer */
7250 if (np->n_attrdirfh)
7251 FREE(np->n_attrdirfh, M_TEMP);
7252 MALLOC(np->n_attrdirfh, u_char*, fh.fh_len+1, M_TEMP, M_WAITOK);
7253 }
7254 if (np->n_attrdirfh) {
7255 /* remember the attrdir fh in the node */
7256 *np->n_attrdirfh = fh.fh_len;
7257 bcopy(fh.fh_data, np->n_attrdirfh+1, fh.fh_len);
7258 /* create busied node for attrdir */
7259 struct componentname cn;
7260 bzero(&cn, sizeof(cn));
7261 cn.cn_nameptr = __CAST_AWAY_QUALIFIER(_PATH_FORKSPECIFIER, const, char *); /* "/..namedfork/" */
7262 cn.cn_namelen = strlen(_PATH_FORKSPECIFIER);
7263 cn.cn_nameiop = LOOKUP;
7264 // XXX can't set parent correctly (to np) yet
7265 error = nfs_nget(NFSTOMP(np), NULL, &cn, fh.fh_data, fh.fh_len, &nvattr, &xid, rq.r_auth, 0, &adnp);
7266 if (!error) {
7267 adlockerror = 0;
7268 /* set the node busy */
7269 SET(adnp->n_flag, NBUSY);
7270 adbusyerror = 0;
7271 }
7272 /* if no adnp, oh well... */
7273 error = 0;
7274 }
7275 }
7276 NVATTR_CLEANUP(&nvattr);
7277 fh.fh_len = 0;
7278 }
7279 if (open) {
7280 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPEN);
7281 nfs_owner_seqid_increment(noop, NULL, error);
7282 nfsm_chain_get_stateid(error, &nmrep, &newnofp->nof_stateid);
7283 nfsm_chain_check_change_info(error, &nmrep, adnp);
7284 nfsm_chain_get_32(error, &nmrep, rflags);
7285 bmlen = NFS_ATTR_BITMAP_LEN;
7286 nfsm_chain_get_bitmap(error, &nmrep, bitmap, bmlen);
7287 nfsm_chain_get_32(error, &nmrep, delegation);
7288 if (!error)
7289 switch (delegation) {
7290 case NFS_OPEN_DELEGATE_NONE:
7291 break;
7292 case NFS_OPEN_DELEGATE_READ:
7293 case NFS_OPEN_DELEGATE_WRITE:
7294 nfsm_chain_get_stateid(error, &nmrep, &dstateid);
7295 nfsm_chain_get_32(error, &nmrep, recall);
7296 if (delegation == NFS_OPEN_DELEGATE_WRITE) // space (skip) XXX
7297 nfsm_chain_adv(error, &nmrep, 3 * NFSX_UNSIGNED);
7298 /* if we have any trouble accepting the ACE, just invalidate it */
7299 ace_type = ace_flags = ace_mask = len = 0;
7300 nfsm_chain_get_32(error, &nmrep, ace_type);
7301 nfsm_chain_get_32(error, &nmrep, ace_flags);
7302 nfsm_chain_get_32(error, &nmrep, ace_mask);
7303 nfsm_chain_get_32(error, &nmrep, len);
7304 ace.ace_flags = nfs4_ace_nfstype_to_vfstype(ace_type, &error);
7305 ace.ace_flags |= nfs4_ace_nfsflags_to_vfsflags(ace_flags);
7306 ace.ace_rights = nfs4_ace_nfsmask_to_vfsrights(ace_mask);
7307 if (!error && (len >= slen)) {
7308 MALLOC(s, char*, len+1, M_TEMP, M_WAITOK);
7309 if (s)
7310 slen = len+1;
7311 else
7312 ace.ace_flags = 0;
7313 }
7314 if (s)
7315 nfsm_chain_get_opaque(error, &nmrep, len, s);
7316 else
7317 nfsm_chain_adv(error, &nmrep, nfsm_rndup(len));
7318 if (!error && s) {
7319 s[len] = '\0';
7320 if (nfs4_id2guid(s, &ace.ace_applicable, (ace_flags & NFS_ACE_IDENTIFIER_GROUP)))
7321 ace.ace_flags = 0;
7322 }
7323 if (error || !s)
7324 ace.ace_flags = 0;
7325 if (s && (s != sbuf))
7326 FREE(s, M_TEMP);
7327 break;
7328 default:
7329 error = EBADRPC;
7330 break;
7331 }
7332 /* At this point if we have no error, the object was created/opened. */
7333 open_error = error;
7334 } else {
7335 nfsm_chain_op_check(error, &nmrep, NFS_OP_LOOKUP);
7336 }
2d21ac55
A
7337 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
7338 nfsmout_if(error);
6d2010ae 7339 error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL, NULL);
2d21ac55 7340 nfsmout_if(error);
6d2010ae
A
7341 if (!NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE) || !fh.fh_len) {
7342 error = EIO;
2d21ac55
A
7343 goto nfsmout;
7344 }
6d2010ae
A
7345 if (prefetch)
7346 nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
7347 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
7348 if (!hadattrdir)
7349 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPENATTR);
2d21ac55 7350 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
6d2010ae
A
7351 nfsmout_if(error);
7352 xid = savedxid;
7353 nfsm_chain_loadattr(error, &nmrep, adnp, nmp->nm_vers, &xid);
7354 nfsmout_if(error);
2d21ac55 7355
6d2010ae
A
7356 if (open) {
7357 if (rflags & NFS_OPEN_RESULT_LOCKTYPE_POSIX)
7358 newnofp->nof_flags |= NFS_OPEN_FILE_POSIXLOCK;
7359 if (rflags & NFS_OPEN_RESULT_CONFIRM) {
7360 if (adnp) {
7361 nfs_node_unlock(adnp);
7362 adlockerror = ENOENT;
7363 }
7364 NVATTR_CLEANUP(&nvattr);
7365 error = nfs4_open_confirm_rpc(nmp, adnp ? adnp : np, fh.fh_data, fh.fh_len, noop, &newnofp->nof_stateid, thd, cred, &nvattr, &xid);
7366 nfsmout_if(error);
7367 savedxid = xid;
7368 if ((adlockerror = nfs_node_lock(adnp)))
7369 error = adlockerror;
2d21ac55 7370 }
2d21ac55
A
7371 }
7372
6d2010ae
A
7373nfsmout:
7374 if (open && adnp && !adlockerror) {
7375 if (!open_error && (adnp->n_flag & NNEGNCENTRIES)) {
7376 adnp->n_flag &= ~NNEGNCENTRIES;
7377 cache_purge_negatives(NFSTOV(adnp));
7378 }
7379 adnp->n_flag |= NMODIFIED;
7380 nfs_node_unlock(adnp);
7381 adlockerror = ENOENT;
7382 nfs_getattr(adnp, NULL, ctx, NGA_CACHED);
7383 }
7384 if (adnp && !adlockerror && (error == ENOENT) &&
7385 (cnp->cn_flags & MAKEENTRY) && (cnp->cn_nameiop != CREATE) && negnamecache) {
7386 /* add a negative entry in the name cache */
7387 cache_enter(NFSTOV(adnp), NULL, cnp);
7388 adnp->n_flag |= NNEGNCENTRIES;
7389 }
7390 if (adnp && !adlockerror) {
7391 nfs_node_unlock(adnp);
7392 adlockerror = ENOENT;
7393 }
7394 if (!error && !anp && fh.fh_len) {
2d21ac55
A
7395 /* create the vnode with the filehandle and attributes */
7396 xid = savedxid;
6d2010ae
A
7397 error = nfs_nget(NFSTOMP(np), adnp, cnp, fh.fh_data, fh.fh_len, &nvattr, &xid, rq.r_auth, NG_MAKEENTRY, &anp);
7398 if (!error) {
7399 *anpp = anp;
7400 nfs_node_unlock(anp);
7401 }
7402 if (!error && open) {
7403 nfs_open_file_add_open(newnofp, accessMode, denyMode, 0);
7404 /* After we have a node, add our open file struct to the node */
7405 nofp = newnofp;
7406 error = nfs_open_file_find_internal(anp, noop, &nofp, 0, 0, 0);
7407 if (error) {
7408 /* This shouldn't happen, because we passed in a new nofp to use. */
7409 printf("nfs_open_file_find_internal failed! %d\n", error);
7410 nofp = NULL;
7411 } else if (nofp != newnofp) {
7412 /*
7413 * Hmm... an open file struct already exists.
7414 * Mark the existing one busy and merge our open into it.
7415 * Then destroy the one we created.
7416 * Note: there's no chance of an open confict because the
7417 * open has already been granted.
7418 */
7419 nofpbusyerror = nfs_open_file_set_busy(nofp, NULL);
7420 nfs_open_file_add_open(nofp, accessMode, denyMode, 0);
7421 nofp->nof_stateid = newnofp->nof_stateid;
7422 if (newnofp->nof_flags & NFS_OPEN_FILE_POSIXLOCK)
7423 nofp->nof_flags |= NFS_OPEN_FILE_POSIXLOCK;
7424 nfs_open_file_clear_busy(newnofp);
7425 nfs_open_file_destroy(newnofp);
7426 newnofp = NULL;
7427 }
7428 if (!error) {
7429 newnofp = NULL;
7430 nofpbusyerror = 0;
7431 /* mark the node as holding a create-initiated open */
7432 nofp->nof_flags |= NFS_OPEN_FILE_CREATE;
7433 nofp->nof_creator = current_thread();
7434 if (nofpp)
7435 *nofpp = nofp;
7436 }
7437 }
2d21ac55 7438 }
6d2010ae
A
7439 NVATTR_CLEANUP(&nvattr);
7440 if (open && ((delegation == NFS_OPEN_DELEGATE_READ) || (delegation == NFS_OPEN_DELEGATE_WRITE))) {
7441 if (!error && anp && !recall) {
7442 /* stuff the delegation state in the node */
7443 lck_mtx_lock(&anp->n_openlock);
7444 anp->n_openflags &= ~N_DELEG_MASK;
7445 anp->n_openflags |= ((delegation == NFS_OPEN_DELEGATE_READ) ? N_DELEG_READ : N_DELEG_WRITE);
7446 anp->n_dstateid = dstateid;
7447 anp->n_dace = ace;
7448 if (anp->n_dlink.tqe_next == NFSNOLIST) {
7449 lck_mtx_lock(&nmp->nm_lock);
7450 if (anp->n_dlink.tqe_next == NFSNOLIST)
7451 TAILQ_INSERT_TAIL(&nmp->nm_delegations, anp, n_dlink);
7452 lck_mtx_unlock(&nmp->nm_lock);
7453 }
7454 lck_mtx_unlock(&anp->n_openlock);
7455 } else {
7456 /* give the delegation back */
7457 if (anp) {
7458 if (NFS_CMPFH(anp, fh.fh_data, fh.fh_len)) {
7459 /* update delegation state and return it */
7460 lck_mtx_lock(&anp->n_openlock);
7461 anp->n_openflags &= ~N_DELEG_MASK;
7462 anp->n_openflags |= ((delegation == NFS_OPEN_DELEGATE_READ) ? N_DELEG_READ : N_DELEG_WRITE);
7463 anp->n_dstateid = dstateid;
7464 anp->n_dace = ace;
7465 if (anp->n_dlink.tqe_next == NFSNOLIST) {
7466 lck_mtx_lock(&nmp->nm_lock);
7467 if (anp->n_dlink.tqe_next == NFSNOLIST)
7468 TAILQ_INSERT_TAIL(&nmp->nm_delegations, anp, n_dlink);
7469 lck_mtx_unlock(&nmp->nm_lock);
7470 }
7471 lck_mtx_unlock(&anp->n_openlock);
7472 /* don't need to send a separate delegreturn for fh */
7473 fh.fh_len = 0;
7474 }
7475 /* return anp's current delegation */
7476 nfs4_delegation_return(anp, 0, thd, cred);
7477 }
7478 if (fh.fh_len) /* return fh's delegation if it wasn't for anp */
7479 nfs4_delegreturn_rpc(nmp, fh.fh_data, fh.fh_len, &dstateid, 0, thd, cred);
7480 }
7481 }
7482 if (open) {
7483 if (newnofp) {
7484 /* need to cleanup our temporary nofp */
7485 nfs_open_file_clear_busy(newnofp);
7486 nfs_open_file_destroy(newnofp);
7487 newnofp = NULL;
7488 } else if (nofp && !nofpbusyerror) {
7489 nfs_open_file_clear_busy(nofp);
7490 nofpbusyerror = ENOENT;
7491 }
7492 if (inuse && nfs_mount_state_in_use_end(nmp, error)) {
7493 inuse = 0;
7494 nofp = newnofp = NULL;
7495 rflags = delegation = recall = eof = rlen = retlen = 0;
7496 ace.ace_flags = 0;
7497 s = sbuf;
7498 slen = sizeof(sbuf);
7499 nfsm_chain_cleanup(&nmreq);
7500 nfsm_chain_cleanup(&nmrep);
7501 if (anp) {
7502 vnode_put(NFSTOV(anp));
7503 *anpp = anp = NULL;
7504 }
7505 hadattrdir = (adnp != NULL);
7506 if (noopbusy) {
7507 nfs_open_owner_clear_busy(noop);
7508 noopbusy = 0;
7509 }
7510 goto restart;
7511 }
7512 if (noop) {
7513 if (noopbusy) {
7514 nfs_open_owner_clear_busy(noop);
7515 noopbusy = 0;
7516 }
7517 nfs_open_owner_rele(noop);
7518 }
7519 }
7520 if (!error && prefetch && nmrep.nmc_mhead) {
7521 nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
7522 nfsm_chain_op_check(error, &nmrep, NFS_OP_NVERIFY);
7523 nfsm_chain_op_check(error, &nmrep, NFS_OP_READ);
7524 nfsm_chain_get_32(error, &nmrep, eof);
7525 nfsm_chain_get_32(error, &nmrep, retlen);
7526 if (!error && anp) {
7527 /*
7528 * There can be one problem with doing the prefetch.
7529 * Because we don't have the node before we start the RPC, we
7530 * can't have the buffer busy while the READ is performed.
7531 * So there is a chance that other I/O occured on the same
7532 * range of data while we were performing this RPC. If that
7533 * happens, then it's possible the data we have in the READ
7534 * response is no longer up to date.
7535 * Once we have the node and the buffer, we need to make sure
7536 * that there's no chance we could be putting stale data in
7537 * the buffer.
7538 * So, we check if the range read is dirty or if any I/O may
7539 * have occured on it while we were performing our RPC.
7540 */
7541 struct nfsbuf *bp = NULL;
7542 int lastpg;
7543 uint32_t pagemask;
7544
7545 retlen = MIN(retlen, rlen);
7546
7547 /* check if node needs size update or invalidation */
7548 if (ISSET(anp->n_flag, NUPDATESIZE))
7549 nfs_data_update_size(anp, 0);
7550 if (!(error = nfs_node_lock(anp))) {
7551 if (anp->n_flag & NNEEDINVALIDATE) {
7552 anp->n_flag &= ~NNEEDINVALIDATE;
7553 nfs_node_unlock(anp);
7554 error = nfs_vinvalbuf(NFSTOV(anp), V_SAVE|V_IGNORE_WRITEERR, ctx, 1);
7555 if (!error) /* lets play it safe and just drop the data */
7556 error = EIO;
7557 } else {
7558 nfs_node_unlock(anp);
7559 }
7560 }
2d21ac55 7561
6d2010ae
A
7562 /* calculate page mask for the range of data read */
7563 lastpg = (trunc_page_32(retlen) - 1) / PAGE_SIZE;
7564 pagemask = ((1 << (lastpg + 1)) - 1);
7565
7566 if (!error)
7567 error = nfs_buf_get(anp, 0, nmp->nm_biosize, thd, NBLK_READ|NBLK_NOWAIT, &bp);
7568 /* don't save the data if dirty or potential I/O conflict */
7569 if (!error && bp && !bp->nb_dirtyoff && !(bp->nb_dirty & pagemask) &&
7570 timevalcmp(&anp->n_lastio, &now, <)) {
316670eb 7571 OSAddAtomic64(1, &nfsstats.read_bios);
6d2010ae
A
7572 CLR(bp->nb_flags, (NB_DONE|NB_ASYNC));
7573 SET(bp->nb_flags, NB_READ);
7574 NFS_BUF_MAP(bp);
7575 nfsm_chain_get_opaque(error, &nmrep, retlen, bp->nb_data);
7576 if (error) {
7577 bp->nb_error = error;
7578 SET(bp->nb_flags, NB_ERROR);
7579 } else {
7580 bp->nb_offio = 0;
7581 bp->nb_endio = rlen;
7582 if ((retlen > 0) && (bp->nb_endio < (int)retlen))
7583 bp->nb_endio = retlen;
7584 if (eof || (retlen == 0)) {
7585 /* zero out the remaining data (up to EOF) */
7586 off_t rpcrem, eofrem, rem;
7587 rpcrem = (rlen - retlen);
7588 eofrem = anp->n_size - (NBOFF(bp) + retlen);
7589 rem = (rpcrem < eofrem) ? rpcrem : eofrem;
7590 if (rem > 0)
7591 bzero(bp->nb_data + retlen, rem);
7592 } else if ((retlen < rlen) && !ISSET(bp->nb_flags, NB_ERROR)) {
7593 /* ugh... short read ... just invalidate for now... */
7594 SET(bp->nb_flags, NB_INVAL);
7595 }
7596 }
7597 nfs_buf_read_finish(bp);
7598 microuptime(&anp->n_lastio);
7599 }
7600 if (bp)
7601 nfs_buf_release(bp, 1);
2d21ac55 7602 }
6d2010ae 7603 error = 0; /* ignore any transient error in processing the prefetch */
2d21ac55 7604 }
6d2010ae
A
7605 if (adnp && !adbusyerror) {
7606 nfs_node_clear_busy(adnp);
7607 adbusyerror = ENOENT;
7608 }
7609 if (!busyerror) {
7610 nfs_node_clear_busy(np);
7611 busyerror = ENOENT;
7612 }
7613 if (adnp)
7614 vnode_put(NFSTOV(adnp));
7615 if (error && *anpp) {
7616 vnode_put(NFSTOV(*anpp));
7617 *anpp = NULL;
7618 }
7619 nfsm_chain_cleanup(&nmreq);
7620 nfsm_chain_cleanup(&nmrep);
7621 return (error);
7622}
7623
7624/*
7625 * Remove a named attribute.
7626 */
7627int
7628nfs4_named_attr_remove(nfsnode_t np, nfsnode_t anp, const char *name, vfs_context_t ctx)
7629{
7630 nfsnode_t adnp = NULL;
7631 struct nfsmount *nmp;
7632 struct componentname cn;
7633 struct vnop_remove_args vra;
7634 int error, putanp = 0;
7635
7636 nmp = NFSTONMP(np);
fe8ab488 7637 if (nfs_mount_gone(nmp))
6d2010ae
A
7638 return (ENXIO);
7639
7640 bzero(&cn, sizeof(cn));
7641 cn.cn_nameptr = __CAST_AWAY_QUALIFIER(name, const, char *);
7642 cn.cn_namelen = strlen(name);
7643 cn.cn_nameiop = DELETE;
7644 cn.cn_flags = 0;
7645
7646 if (!anp) {
7647 error = nfs4_named_attr_get(np, &cn, NFS_OPEN_SHARE_ACCESS_NONE,
7648 0, ctx, &anp, NULL);
7649 if ((!error && !anp) || (error == ENOATTR))
7650 error = ENOENT;
7651 if (error) {
7652 if (anp) {
7653 vnode_put(NFSTOV(anp));
7654 anp = NULL;
7655 }
7656 goto out;
2d21ac55 7657 }
6d2010ae
A
7658 putanp = 1;
7659 }
7660
7661 if ((error = nfs_node_set_busy(np, vfs_context_thread(ctx))))
7662 goto out;
7663 adnp = nfs4_named_attr_dir_get(np, 1, ctx);
7664 nfs_node_clear_busy(np);
7665 if (!adnp) {
7666 error = ENOENT;
7667 goto out;
2d21ac55 7668 }
6d2010ae
A
7669
7670 vra.a_desc = &vnop_remove_desc;
7671 vra.a_dvp = NFSTOV(adnp);
7672 vra.a_vp = NFSTOV(anp);
7673 vra.a_cnp = &cn;
7674 vra.a_flags = 0;
7675 vra.a_context = ctx;
7676 error = nfs_vnop_remove(&vra);
7677out:
7678 if (adnp)
7679 vnode_put(NFSTOV(adnp));
7680 if (putanp)
7681 vnode_put(NFSTOV(anp));
2d21ac55
A
7682 return (error);
7683}
7684
7685int
6d2010ae
A
7686nfs4_vnop_getxattr(
7687 struct vnop_getxattr_args /* {
2d21ac55 7688 struct vnodeop_desc *a_desc;
6d2010ae
A
7689 vnode_t a_vp;
7690 const char * a_name;
7691 uio_t a_uio;
7692 size_t *a_size;
7693 int a_options;
2d21ac55
A
7694 vfs_context_t a_context;
7695 } */ *ap)
7696{
6d2010ae 7697 vfs_context_t ctx = ap->a_context;
2d21ac55 7698 struct nfsmount *nmp;
6d2010ae
A
7699 struct nfs_vattr nvattr;
7700 struct componentname cn;
7701 nfsnode_t anp;
7702 int error = 0, isrsrcfork;
2d21ac55 7703
6d2010ae 7704 nmp = VTONMP(ap->a_vp);
fe8ab488 7705 if (nfs_mount_gone(nmp))
2d21ac55
A
7706 return (ENXIO);
7707
6d2010ae 7708 if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR))
2d21ac55 7709 return (ENOTSUP);
6d2010ae
A
7710 error = nfs_getattr(VTONFS(ap->a_vp), &nvattr, ctx, NGA_CACHED);
7711 if (error)
7712 return (error);
7713 if (NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_NAMED_ATTR) &&
7714 !(nvattr.nva_flags & NFS_FFLAG_HAS_NAMED_ATTRS))
7715 return (ENOATTR);
7716
7717 bzero(&cn, sizeof(cn));
7718 cn.cn_nameptr = __CAST_AWAY_QUALIFIER(ap->a_name, const, char *);
7719 cn.cn_namelen = strlen(ap->a_name);
7720 cn.cn_nameiop = LOOKUP;
7721 cn.cn_flags = MAKEENTRY;
7722
7723 /* we'll normally try to prefetch data for xattrs... the resource fork is really a stream */
7724 isrsrcfork = (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0);
7725
7726 error = nfs4_named_attr_get(VTONFS(ap->a_vp), &cn, NFS_OPEN_SHARE_ACCESS_NONE,
7727 !isrsrcfork ? NFS_GET_NAMED_ATTR_PREFETCH : 0, ctx, &anp, NULL);
7728 if ((!error && !anp) || (error == ENOENT))
7729 error = ENOATTR;
7730 if (!error) {
7731 if (ap->a_uio)
7732 error = nfs_bioread(anp, ap->a_uio, 0, ctx);
7733 else
7734 *ap->a_size = anp->n_size;
2d21ac55 7735 }
6d2010ae
A
7736 if (anp)
7737 vnode_put(NFSTOV(anp));
7738 return (error);
7739}
2d21ac55 7740
6d2010ae
A
7741int
7742nfs4_vnop_setxattr(
7743 struct vnop_setxattr_args /* {
7744 struct vnodeop_desc *a_desc;
7745 vnode_t a_vp;
7746 const char * a_name;
7747 uio_t a_uio;
7748 int a_options;
7749 vfs_context_t a_context;
7750 } */ *ap)
7751{
7752 vfs_context_t ctx = ap->a_context;
7753 int options = ap->a_options;
7754 uio_t uio = ap->a_uio;
7755 const char *name = ap->a_name;
7756 struct nfsmount *nmp;
7757 struct componentname cn;
7758 nfsnode_t anp = NULL;
7759 int error = 0, closeerror = 0, flags, isrsrcfork, isfinderinfo, empty = 0, i;
7760#define FINDERINFOSIZE 32
7761 uint8_t finfo[FINDERINFOSIZE];
7762 uint32_t *finfop;
7763 struct nfs_open_file *nofp = NULL;
7764 char uio_buf [ UIO_SIZEOF(1) ];
7765 uio_t auio;
7766 struct vnop_write_args vwa;
7767
7768 nmp = VTONMP(ap->a_vp);
fe8ab488 7769 if (nfs_mount_gone(nmp))
6d2010ae
A
7770 return (ENXIO);
7771
7772 if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR))
7773 return (ENOTSUP);
7774
7775 if ((options & XATTR_CREATE) && (options & XATTR_REPLACE))
7776 return (EINVAL);
7777
7778 /* XXX limitation based on need to back up uio on short write */
7779 if (uio_iovcnt(uio) > 1) {
7780 printf("nfs4_vnop_setxattr: iovcnt > 1\n");
7781 return (EINVAL);
7782 }
7783
7784 bzero(&cn, sizeof(cn));
7785 cn.cn_nameptr = __CAST_AWAY_QUALIFIER(name, const, char *);
7786 cn.cn_namelen = strlen(name);
7787 cn.cn_nameiop = CREATE;
7788 cn.cn_flags = MAKEENTRY;
7789
7790 isfinderinfo = (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0);
7791 isrsrcfork = isfinderinfo ? 0 : (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0);
7792 if (!isrsrcfork)
7793 uio_setoffset(uio, 0);
7794 if (isfinderinfo) {
7795 if (uio_resid(uio) != sizeof(finfo))
7796 return (ERANGE);
7797 error = uiomove((char*)&finfo, sizeof(finfo), uio);
7798 if (error)
7799 return (error);
7800 /* setting a FinderInfo of all zeroes means remove the FinderInfo */
7801 empty = 1;
7802 for (i=0, finfop=(uint32_t*)&finfo; i < (int)(sizeof(finfo)/sizeof(uint32_t)); i++)
7803 if (finfop[i]) {
7804 empty = 0;
7805 break;
7806 }
7807 if (empty && !(options & (XATTR_CREATE|XATTR_REPLACE))) {
7808 error = nfs4_named_attr_remove(VTONFS(ap->a_vp), anp, name, ctx);
7809 if (error == ENOENT)
7810 error = 0;
7811 return (error);
7812 }
7813 /* first, let's see if we get a create/replace error */
7814 }
7815
7816 /*
7817 * create/open the xattr
7818 *
7819 * We need to make sure not to create it if XATTR_REPLACE.
7820 * For all xattrs except the resource fork, we also want to
7821 * truncate the xattr to remove any current data. We'll do
7822 * that by setting the size to 0 on create/open.
7823 */
7824 flags = 0;
7825 if (!(options & XATTR_REPLACE))
7826 flags |= NFS_GET_NAMED_ATTR_CREATE;
7827 if (options & XATTR_CREATE)
7828 flags |= NFS_GET_NAMED_ATTR_CREATE_GUARDED;
7829 if (!isrsrcfork)
7830 flags |= NFS_GET_NAMED_ATTR_TRUNCATE;
7831
7832 error = nfs4_named_attr_get(VTONFS(ap->a_vp), &cn, NFS_OPEN_SHARE_ACCESS_BOTH,
7833 flags, ctx, &anp, &nofp);
7834 if (!error && !anp)
7835 error = ENOATTR;
7836 if (error)
7837 goto out;
7838 /* grab the open state from the get/create/open */
7839 if (nofp && !(error = nfs_open_file_set_busy(nofp, NULL))) {
7840 nofp->nof_flags &= ~NFS_OPEN_FILE_CREATE;
7841 nofp->nof_creator = NULL;
7842 nfs_open_file_clear_busy(nofp);
7843 }
7844
7845 /* Setting an empty FinderInfo really means remove it, skip to the close/remove */
7846 if (isfinderinfo && empty)
7847 goto doclose;
7848
7849 /*
7850 * Write the data out and flush.
7851 *
7852 * For FinderInfo, we've already copied the data to finfo, so do I/O from there.
7853 */
7854 vwa.a_desc = &vnop_write_desc;
7855 vwa.a_vp = NFSTOV(anp);
7856 vwa.a_uio = NULL;
7857 vwa.a_ioflag = 0;
7858 vwa.a_context = ctx;
7859 if (isfinderinfo) {
7860 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, &uio_buf, sizeof(uio_buf));
7861 uio_addiov(auio, (uintptr_t)&finfo, sizeof(finfo));
7862 vwa.a_uio = auio;
7863 } else if (uio_resid(uio) > 0) {
7864 vwa.a_uio = uio;
7865 }
7866 if (vwa.a_uio) {
7867 error = nfs_vnop_write(&vwa);
7868 if (!error)
7869 error = nfs_flush(anp, MNT_WAIT, vfs_context_thread(ctx), 0);
7870 }
7871doclose:
7872 /* Close the xattr. */
7873 if (nofp) {
7874 int busyerror = nfs_open_file_set_busy(nofp, NULL);
7875 closeerror = nfs_close(anp, nofp, NFS_OPEN_SHARE_ACCESS_BOTH, NFS_OPEN_SHARE_DENY_NONE, ctx);
7876 if (!busyerror)
7877 nfs_open_file_clear_busy(nofp);
7878 }
7879 if (!error && isfinderinfo && empty) { /* Setting an empty FinderInfo really means remove it */
7880 error = nfs4_named_attr_remove(VTONFS(ap->a_vp), anp, name, ctx);
7881 if (error == ENOENT)
7882 error = 0;
7883 }
2d21ac55 7884 if (!error)
6d2010ae
A
7885 error = closeerror;
7886out:
7887 if (anp)
7888 vnode_put(NFSTOV(anp));
7889 if (error == ENOENT)
7890 error = ENOATTR;
2d21ac55
A
7891 return (error);
7892}
7893
7894int
6d2010ae
A
7895nfs4_vnop_removexattr(
7896 struct vnop_removexattr_args /* {
2d21ac55 7897 struct vnodeop_desc *a_desc;
6d2010ae
A
7898 vnode_t a_vp;
7899 const char * a_name;
7900 int a_options;
2d21ac55
A
7901 vfs_context_t a_context;
7902 } */ *ap)
7903{
6d2010ae 7904 struct nfsmount *nmp = VTONMP(ap->a_vp);
2d21ac55
A
7905 int error;
7906
fe8ab488 7907 if (nfs_mount_gone(nmp))
6d2010ae
A
7908 return (ENXIO);
7909 if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR))
7910 return (ENOTSUP);
7911
7912 error = nfs4_named_attr_remove(VTONFS(ap->a_vp), NULL, ap->a_name, ap->a_context);
7913 if (error == ENOENT)
7914 error = ENOATTR;
2d21ac55
A
7915 return (error);
7916}
7917
7918int
6d2010ae
A
7919nfs4_vnop_listxattr(
7920 struct vnop_listxattr_args /* {
2d21ac55 7921 struct vnodeop_desc *a_desc;
6d2010ae
A
7922 vnode_t a_vp;
7923 uio_t a_uio;
7924 size_t *a_size;
7925 int a_options;
2d21ac55
A
7926 vfs_context_t a_context;
7927 } */ *ap)
7928{
6d2010ae
A
7929 vfs_context_t ctx = ap->a_context;
7930 nfsnode_t np = VTONFS(ap->a_vp);
7931 uio_t uio = ap->a_uio;
7932 nfsnode_t adnp = NULL;
7933 struct nfsmount *nmp;
7934 int error, done, i;
7935 struct nfs_vattr nvattr;
7936 uint64_t cookie, nextcookie, lbn = 0;
7937 struct nfsbuf *bp = NULL;
7938 struct nfs_dir_buf_header *ndbhp;
7939 struct direntry *dp;
2d21ac55 7940
6d2010ae 7941 nmp = VTONMP(ap->a_vp);
fe8ab488 7942 if (nfs_mount_gone(nmp))
6d2010ae
A
7943 return (ENXIO);
7944
7945 if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR))
7946 return (ENOTSUP);
7947
7948 error = nfs_getattr(np, &nvattr, ctx, NGA_CACHED);
7949 if (error)
7950 return (error);
7951 if (NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_NAMED_ATTR) &&
7952 !(nvattr.nva_flags & NFS_FFLAG_HAS_NAMED_ATTRS))
7953 return (0);
7954
7955 if ((error = nfs_node_set_busy(np, vfs_context_thread(ctx))))
7956 return (error);
7957 adnp = nfs4_named_attr_dir_get(np, 1, ctx);
7958 nfs_node_clear_busy(np);
7959 if (!adnp)
7960 goto out;
7961
7962 if ((error = nfs_node_lock(adnp)))
7963 goto out;
7964
7965 if (adnp->n_flag & NNEEDINVALIDATE) {
7966 adnp->n_flag &= ~NNEEDINVALIDATE;
7967 nfs_invaldir(adnp);
7968 nfs_node_unlock(adnp);
7969 error = nfs_vinvalbuf(NFSTOV(adnp), 0, ctx, 1);
7970 if (!error)
7971 error = nfs_node_lock(adnp);
7972 if (error)
7973 goto out;
7974 }
7975
7976 /*
7977 * check for need to invalidate when (re)starting at beginning
7978 */
7979 if (adnp->n_flag & NMODIFIED) {
7980 nfs_invaldir(adnp);
7981 nfs_node_unlock(adnp);
7982 if ((error = nfs_vinvalbuf(NFSTOV(adnp), 0, ctx, 1)))
7983 goto out;
7984 } else {
7985 nfs_node_unlock(adnp);
7986 }
7987 /* nfs_getattr() will check changed and purge caches */
7988 if ((error = nfs_getattr(adnp, &nvattr, ctx, NGA_UNCACHED)))
7989 goto out;
7990
7991 if (uio && (uio_resid(uio) == 0))
7992 goto out;
7993
7994 done = 0;
7995 nextcookie = lbn = 0;
7996
7997 while (!error && !done) {
316670eb 7998 OSAddAtomic64(1, &nfsstats.biocache_readdirs);
6d2010ae
A
7999 cookie = nextcookie;
8000getbuffer:
8001 error = nfs_buf_get(adnp, lbn, NFS_DIRBLKSIZ, vfs_context_thread(ctx), NBLK_READ, &bp);
8002 if (error)
8003 goto out;
8004 ndbhp = (struct nfs_dir_buf_header*)bp->nb_data;
8005 if (!ISSET(bp->nb_flags, NB_CACHE) || !ISSET(ndbhp->ndbh_flags, NDB_FULL)) {
8006 if (!ISSET(bp->nb_flags, NB_CACHE)) { /* initialize the buffer */
8007 ndbhp->ndbh_flags = 0;
8008 ndbhp->ndbh_count = 0;
8009 ndbhp->ndbh_entry_end = sizeof(*ndbhp);
8010 ndbhp->ndbh_ncgen = adnp->n_ncgen;
8011 }
8012 error = nfs_buf_readdir(bp, ctx);
8013 if (error == NFSERR_DIRBUFDROPPED)
8014 goto getbuffer;
8015 if (error)
8016 nfs_buf_release(bp, 1);
8017 if (error && (error != ENXIO) && (error != ETIMEDOUT) && (error != EINTR) && (error != ERESTART)) {
8018 if (!nfs_node_lock(adnp)) {
8019 nfs_invaldir(adnp);
8020 nfs_node_unlock(adnp);
8021 }
8022 nfs_vinvalbuf(NFSTOV(adnp), 0, ctx, 1);
8023 if (error == NFSERR_BAD_COOKIE)
8024 error = ENOENT;
8025 }
8026 if (error)
8027 goto out;
8028 }
8029
8030 /* go through all the entries copying/counting */
8031 dp = NFS_DIR_BUF_FIRST_DIRENTRY(bp);
8032 for (i=0; i < ndbhp->ndbh_count; i++) {
8033 if (!xattr_protected(dp->d_name)) {
8034 if (uio == NULL) {
8035 *ap->a_size += dp->d_namlen + 1;
8036 } else if (uio_resid(uio) < (dp->d_namlen + 1)) {
8037 error = ERANGE;
8038 } else {
8039 error = uiomove(dp->d_name, dp->d_namlen+1, uio);
8040 if (error && (error != EFAULT))
8041 error = ERANGE;
8042 }
8043 }
8044 nextcookie = dp->d_seekoff;
8045 dp = NFS_DIRENTRY_NEXT(dp);
8046 }
8047
8048 if (i == ndbhp->ndbh_count) {
8049 /* hit end of buffer, move to next buffer */
8050 lbn = nextcookie;
8051 /* if we also hit EOF, we're done */
8052 if (ISSET(ndbhp->ndbh_flags, NDB_EOF))
8053 done = 1;
8054 }
8055 if (!error && !done && (nextcookie == cookie)) {
8056 printf("nfs readdir cookie didn't change 0x%llx, %d/%d\n", cookie, i, ndbhp->ndbh_count);
8057 error = EIO;
8058 }
8059 nfs_buf_release(bp, 1);
8060 }
8061out:
8062 if (adnp)
8063 vnode_put(NFSTOV(adnp));
2d21ac55
A
8064 return (error);
8065}
8066
6d2010ae 8067#if NAMEDSTREAMS
2d21ac55 8068int
6d2010ae
A
8069nfs4_vnop_getnamedstream(
8070 struct vnop_getnamedstream_args /* {
2d21ac55
A
8071 struct vnodeop_desc *a_desc;
8072 vnode_t a_vp;
6d2010ae
A
8073 vnode_t *a_svpp;
8074 const char *a_name;
8075 enum nsoperation a_operation;
8076 int a_flags;
2d21ac55
A
8077 vfs_context_t a_context;
8078 } */ *ap)
8079{
8080 vfs_context_t ctx = ap->a_context;
2d21ac55 8081 struct nfsmount *nmp;
6d2010ae
A
8082 struct nfs_vattr nvattr;
8083 struct componentname cn;
8084 nfsnode_t anp;
8085 int error = 0;
2d21ac55 8086
6d2010ae 8087 nmp = VTONMP(ap->a_vp);
fe8ab488 8088 if (nfs_mount_gone(nmp))
2d21ac55 8089 return (ENXIO);
2d21ac55 8090
6d2010ae
A
8091 if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR))
8092 return (ENOTSUP);
8093 error = nfs_getattr(VTONFS(ap->a_vp), &nvattr, ctx, NGA_CACHED);
8094 if (error)
2d21ac55 8095 return (error);
6d2010ae
A
8096 if (NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_NAMED_ATTR) &&
8097 !(nvattr.nva_flags & NFS_FFLAG_HAS_NAMED_ATTRS))
8098 return (ENOATTR);
2d21ac55 8099
6d2010ae
A
8100 bzero(&cn, sizeof(cn));
8101 cn.cn_nameptr = __CAST_AWAY_QUALIFIER(ap->a_name, const, char *);
8102 cn.cn_namelen = strlen(ap->a_name);
8103 cn.cn_nameiop = LOOKUP;
8104 cn.cn_flags = MAKEENTRY;
8105
8106 error = nfs4_named_attr_get(VTONFS(ap->a_vp), &cn, NFS_OPEN_SHARE_ACCESS_NONE,
8107 0, ctx, &anp, NULL);
8108 if ((!error && !anp) || (error == ENOENT))
8109 error = ENOATTR;
8110 if (!error && anp)
8111 *ap->a_svpp = NFSTOV(anp);
8112 else if (anp)
8113 vnode_put(NFSTOV(anp));
2d21ac55
A
8114 return (error);
8115}
8116
8117int
6d2010ae
A
8118nfs4_vnop_makenamedstream(
8119 struct vnop_makenamedstream_args /* {
2d21ac55 8120 struct vnodeop_desc *a_desc;
6d2010ae 8121 vnode_t *a_svpp;
2d21ac55 8122 vnode_t a_vp;
6d2010ae
A
8123 const char *a_name;
8124 int a_flags;
2d21ac55
A
8125 vfs_context_t a_context;
8126 } */ *ap)
8127{
8128 vfs_context_t ctx = ap->a_context;
6d2010ae
A
8129 struct nfsmount *nmp;
8130 struct componentname cn;
8131 nfsnode_t anp;
2d21ac55 8132 int error = 0;
2d21ac55 8133
6d2010ae 8134 nmp = VTONMP(ap->a_vp);
fe8ab488 8135 if (nfs_mount_gone(nmp))
6d2010ae 8136 return (ENXIO);
2d21ac55 8137
6d2010ae
A
8138 if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR))
8139 return (ENOTSUP);
2d21ac55 8140
6d2010ae
A
8141 bzero(&cn, sizeof(cn));
8142 cn.cn_nameptr = __CAST_AWAY_QUALIFIER(ap->a_name, const, char *);
8143 cn.cn_namelen = strlen(ap->a_name);
8144 cn.cn_nameiop = CREATE;
8145 cn.cn_flags = MAKEENTRY;
8146
8147 error = nfs4_named_attr_get(VTONFS(ap->a_vp), &cn, NFS_OPEN_SHARE_ACCESS_BOTH,
8148 NFS_GET_NAMED_ATTR_CREATE, ctx, &anp, NULL);
8149 if ((!error && !anp) || (error == ENOENT))
8150 error = ENOATTR;
8151 if (!error && anp)
8152 *ap->a_svpp = NFSTOV(anp);
8153 else if (anp)
8154 vnode_put(NFSTOV(anp));
8155 return (error);
8156}
2d21ac55 8157
6d2010ae
A
8158int
8159nfs4_vnop_removenamedstream(
8160 struct vnop_removenamedstream_args /* {
8161 struct vnodeop_desc *a_desc;
8162 vnode_t a_vp;
8163 vnode_t a_svp;
8164 const char *a_name;
8165 int a_flags;
8166 vfs_context_t a_context;
8167 } */ *ap)
8168{
8169 struct nfsmount *nmp = VTONMP(ap->a_vp);
8170 nfsnode_t np = ap->a_vp ? VTONFS(ap->a_vp) : NULL;
8171 nfsnode_t anp = ap->a_svp ? VTONFS(ap->a_svp) : NULL;
2d21ac55 8172
fe8ab488 8173 if (nfs_mount_gone(nmp))
6d2010ae 8174 return (ENXIO);
2d21ac55
A
8175
8176 /*
6d2010ae
A
8177 * Given that a_svp is a named stream, checking for
8178 * named attribute support is kinda pointless.
2d21ac55 8179 */
6d2010ae
A
8180 if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR))
8181 return (ENOTSUP);
8182
8183 return (nfs4_named_attr_remove(np, anp, ap->a_name, ap->a_context));
2d21ac55
A
8184}
8185
6d2010ae 8186#endif