Print this page
5382 pvn_getpages handles lengths <= PAGESIZE just fine
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/fs/cachefs/cachefs_vnops.c
+++ new/usr/src/uts/common/fs/cachefs/cachefs_vnops.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
23 + * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
23 24 */
24 25
25 26 #include <sys/param.h>
26 27 #include <sys/types.h>
27 28 #include <sys/systm.h>
28 29 #include <sys/cred.h>
29 30 #include <sys/proc.h>
30 31 #include <sys/user.h>
31 32 #include <sys/time.h>
32 33 #include <sys/vnode.h>
33 34 #include <sys/vfs.h>
34 35 #include <sys/vfs_opreg.h>
35 36 #include <sys/file.h>
36 37 #include <sys/filio.h>
37 38 #include <sys/uio.h>
38 39 #include <sys/buf.h>
39 40 #include <sys/mman.h>
40 41 #include <sys/tiuser.h>
41 42 #include <sys/pathname.h>
42 43 #include <sys/dirent.h>
43 44 #include <sys/conf.h>
44 45 #include <sys/debug.h>
45 46 #include <sys/vmsystm.h>
46 47 #include <sys/fcntl.h>
47 48 #include <sys/flock.h>
48 49 #include <sys/swap.h>
49 50 #include <sys/errno.h>
50 51 #include <sys/sysmacros.h>
51 52 #include <sys/disp.h>
52 53 #include <sys/kmem.h>
53 54 #include <sys/cmn_err.h>
54 55 #include <sys/vtrace.h>
55 56 #include <sys/mount.h>
56 57 #include <sys/bootconf.h>
57 58 #include <sys/dnlc.h>
58 59 #include <sys/stat.h>
59 60 #include <sys/acl.h>
60 61 #include <sys/policy.h>
61 62 #include <rpc/types.h>
62 63
63 64 #include <vm/hat.h>
64 65 #include <vm/as.h>
65 66 #include <vm/page.h>
66 67 #include <vm/pvn.h>
67 68 #include <vm/seg.h>
68 69 #include <vm/seg_map.h>
69 70 #include <vm/seg_vn.h>
70 71 #include <vm/rm.h>
71 72 #include <sys/fs/cachefs_fs.h>
72 73 #include <sys/fs/cachefs_dir.h>
73 74 #include <sys/fs/cachefs_dlog.h>
74 75 #include <sys/fs/cachefs_ioctl.h>
75 76 #include <sys/fs/cachefs_log.h>
76 77 #include <fs/fs_subr.h>
77 78
78 79 int cachefs_dnlc; /* use dnlc, debugging */
79 80
80 81 static void cachefs_attr_setup(vattr_t *srcp, vattr_t *targp, cnode_t *cp,
81 82 cred_t *cr);
82 83 static void cachefs_creategid(cnode_t *dcp, cnode_t *newcp, vattr_t *vap,
83 84 cred_t *cr);
84 85 static void cachefs_createacl(cnode_t *dcp, cnode_t *newcp);
85 86 static int cachefs_getaclfromcache(cnode_t *cp, vsecattr_t *vsec);
86 87 static int cachefs_getacldirvp(cnode_t *cp);
87 88 static void cachefs_acl2perm(cnode_t *cp, vsecattr_t *vsec);
88 89 static int cachefs_access_local(void *cp, int mode, cred_t *cr);
89 90 static int cachefs_acl_access(struct cnode *cp, int mode, cred_t *cr);
90 91 static int cachefs_push_connected(vnode_t *vp, struct buf *bp, size_t iolen,
91 92 u_offset_t iooff, cred_t *cr);
92 93 static int cachefs_push_front(vnode_t *vp, struct buf *bp, size_t iolen,
93 94 u_offset_t iooff, cred_t *cr);
94 95 static int cachefs_setattr_connected(vnode_t *vp, vattr_t *vap, int flags,
95 96 cred_t *cr, caller_context_t *ct);
96 97 static int cachefs_setattr_disconnected(vnode_t *vp, vattr_t *vap,
97 98 int flags, cred_t *cr, caller_context_t *ct);
98 99 static int cachefs_access_connected(struct vnode *vp, int mode,
99 100 int flags, cred_t *cr);
100 101 static int cachefs_lookup_back(vnode_t *dvp, char *nm, vnode_t **vpp,
101 102 cred_t *cr);
102 103 static int cachefs_symlink_connected(vnode_t *dvp, char *lnm, vattr_t *tva,
103 104 char *tnm, cred_t *cr);
104 105 static int cachefs_symlink_disconnected(vnode_t *dvp, char *lnm,
105 106 vattr_t *tva, char *tnm, cred_t *cr);
106 107 static int cachefs_link_connected(vnode_t *tdvp, vnode_t *fvp, char *tnm,
107 108 cred_t *cr);
108 109 static int cachefs_link_disconnected(vnode_t *tdvp, vnode_t *fvp,
109 110 char *tnm, cred_t *cr);
110 111 static int cachefs_mkdir_connected(vnode_t *dvp, char *nm, vattr_t *vap,
111 112 vnode_t **vpp, cred_t *cr);
112 113 static int cachefs_mkdir_disconnected(vnode_t *dvp, char *nm, vattr_t *vap,
113 114 vnode_t **vpp, cred_t *cr);
114 115 static int cachefs_stickyrmchk(struct cnode *dcp, struct cnode *cp, cred_t *cr);
115 116 static int cachefs_rmdir_connected(vnode_t *dvp, char *nm,
116 117 vnode_t *cdir, cred_t *cr, vnode_t *vp);
117 118 static int cachefs_rmdir_disconnected(vnode_t *dvp, char *nm,
118 119 vnode_t *cdir, cred_t *cr, vnode_t *vp);
119 120 static char *cachefs_newname(void);
120 121 static int cachefs_remove_dolink(vnode_t *dvp, vnode_t *vp, char *nm,
121 122 cred_t *cr);
122 123 static int cachefs_rename_connected(vnode_t *odvp, char *onm,
123 124 vnode_t *ndvp, char *nnm, cred_t *cr, vnode_t *delvp);
124 125 static int cachefs_rename_disconnected(vnode_t *odvp, char *onm,
125 126 vnode_t *ndvp, char *nnm, cred_t *cr, vnode_t *delvp);
126 127 static int cachefs_readdir_connected(vnode_t *vp, uio_t *uiop, cred_t *cr,
127 128 int *eofp);
128 129 static int cachefs_readdir_disconnected(vnode_t *vp, uio_t *uiop,
129 130 cred_t *cr, int *eofp);
130 131 static int cachefs_readback_translate(cnode_t *cp, uio_t *uiop,
131 132 cred_t *cr, int *eofp);
132 133
133 134 static int cachefs_setattr_common(vnode_t *vp, vattr_t *vap, int flags,
134 135 cred_t *cr, caller_context_t *ct);
135 136
136 137 static int cachefs_open(struct vnode **, int, cred_t *,
137 138 caller_context_t *);
138 139 static int cachefs_close(struct vnode *, int, int, offset_t,
139 140 cred_t *, caller_context_t *);
140 141 static int cachefs_read(struct vnode *, struct uio *, int, cred_t *,
141 142 caller_context_t *);
142 143 static int cachefs_write(struct vnode *, struct uio *, int, cred_t *,
143 144 caller_context_t *);
144 145 static int cachefs_ioctl(struct vnode *, int, intptr_t, int, cred_t *,
145 146 int *, caller_context_t *);
146 147 static int cachefs_getattr(struct vnode *, struct vattr *, int,
147 148 cred_t *, caller_context_t *);
148 149 static int cachefs_setattr(struct vnode *, struct vattr *,
149 150 int, cred_t *, caller_context_t *);
150 151 static int cachefs_access(struct vnode *, int, int, cred_t *,
151 152 caller_context_t *);
152 153 static int cachefs_lookup(struct vnode *, char *, struct vnode **,
153 154 struct pathname *, int, struct vnode *, cred_t *,
154 155 caller_context_t *, int *, pathname_t *);
155 156 static int cachefs_create(struct vnode *, char *, struct vattr *,
156 157 enum vcexcl, int, struct vnode **, cred_t *, int,
157 158 caller_context_t *, vsecattr_t *);
158 159 static int cachefs_create_connected(vnode_t *dvp, char *nm,
159 160 vattr_t *vap, enum vcexcl exclusive, int mode,
160 161 vnode_t **vpp, cred_t *cr);
161 162 static int cachefs_create_disconnected(vnode_t *dvp, char *nm,
162 163 vattr_t *vap, enum vcexcl exclusive, int mode,
163 164 vnode_t **vpp, cred_t *cr);
164 165 static int cachefs_remove(struct vnode *, char *, cred_t *,
165 166 caller_context_t *, int);
166 167 static int cachefs_link(struct vnode *, struct vnode *, char *,
167 168 cred_t *, caller_context_t *, int);
168 169 static int cachefs_rename(struct vnode *, char *, struct vnode *,
169 170 char *, cred_t *, caller_context_t *, int);
170 171 static int cachefs_mkdir(struct vnode *, char *, struct
171 172 vattr *, struct vnode **, cred_t *, caller_context_t *,
172 173 int, vsecattr_t *);
173 174 static int cachefs_rmdir(struct vnode *, char *, struct vnode *,
174 175 cred_t *, caller_context_t *, int);
175 176 static int cachefs_readdir(struct vnode *, struct uio *,
176 177 cred_t *, int *, caller_context_t *, int);
177 178 static int cachefs_symlink(struct vnode *, char *, struct vattr *,
178 179 char *, cred_t *, caller_context_t *, int);
179 180 static int cachefs_readlink(struct vnode *, struct uio *, cred_t *,
180 181 caller_context_t *);
181 182 static int cachefs_readlink_connected(vnode_t *vp, uio_t *uiop, cred_t *cr);
182 183 static int cachefs_readlink_disconnected(vnode_t *vp, uio_t *uiop);
183 184 static int cachefs_fsync(struct vnode *, int, cred_t *,
184 185 caller_context_t *);
185 186 static void cachefs_inactive(struct vnode *, cred_t *, caller_context_t *);
186 187 static int cachefs_fid(struct vnode *, struct fid *, caller_context_t *);
187 188 static int cachefs_rwlock(struct vnode *, int, caller_context_t *);
188 189 static void cachefs_rwunlock(struct vnode *, int, caller_context_t *);
189 190 static int cachefs_seek(struct vnode *, offset_t, offset_t *,
190 191 caller_context_t *);
191 192 static int cachefs_frlock(struct vnode *, int, struct flock64 *,
192 193 int, offset_t, struct flk_callback *, cred_t *,
193 194 caller_context_t *);
194 195 static int cachefs_space(struct vnode *, int, struct flock64 *, int,
195 196 offset_t, cred_t *, caller_context_t *);
196 197 static int cachefs_realvp(struct vnode *, struct vnode **,
197 198 caller_context_t *);
198 199 static int cachefs_getpage(struct vnode *, offset_t, size_t, uint_t *,
199 200 struct page *[], size_t, struct seg *, caddr_t,
200 201 enum seg_rw, cred_t *, caller_context_t *);
201 202 static int cachefs_getapage(struct vnode *, u_offset_t, size_t, uint_t *,
202 203 struct page *[], size_t, struct seg *, caddr_t,
203 204 enum seg_rw, cred_t *);
204 205 static int cachefs_getapage_back(struct vnode *, u_offset_t, size_t,
205 206 uint_t *, struct page *[], size_t, struct seg *, caddr_t,
206 207 enum seg_rw, cred_t *);
207 208 static int cachefs_putpage(struct vnode *, offset_t, size_t, int,
208 209 cred_t *, caller_context_t *);
209 210 static int cachefs_map(struct vnode *, offset_t, struct as *,
210 211 caddr_t *, size_t, uchar_t, uchar_t, uint_t, cred_t *,
211 212 caller_context_t *);
212 213 static int cachefs_addmap(struct vnode *, offset_t, struct as *,
213 214 caddr_t, size_t, uchar_t, uchar_t, uint_t, cred_t *,
214 215 caller_context_t *);
215 216 static int cachefs_delmap(struct vnode *, offset_t, struct as *,
216 217 caddr_t, size_t, uint_t, uint_t, uint_t, cred_t *,
217 218 caller_context_t *);
218 219 static int cachefs_setsecattr(vnode_t *vp, vsecattr_t *vsec,
219 220 int flag, cred_t *cr, caller_context_t *);
220 221 static int cachefs_getsecattr(vnode_t *vp, vsecattr_t *vsec,
221 222 int flag, cred_t *cr, caller_context_t *);
222 223 static int cachefs_shrlock(vnode_t *, int, struct shrlock *, int,
223 224 cred_t *, caller_context_t *);
224 225 static int cachefs_getsecattr_connected(vnode_t *vp, vsecattr_t *vsec, int flag,
225 226 cred_t *cr);
226 227 static int cachefs_getsecattr_disconnected(vnode_t *vp, vsecattr_t *vsec,
227 228 int flag, cred_t *cr);
228 229
229 230 static int cachefs_dump(struct vnode *, caddr_t, offset_t, offset_t,
230 231 caller_context_t *);
231 232 static int cachefs_pageio(struct vnode *, page_t *,
232 233 u_offset_t, size_t, int, cred_t *, caller_context_t *);
233 234 static int cachefs_writepage(struct vnode *vp, caddr_t base,
234 235 int tcount, struct uio *uiop);
235 236 static int cachefs_pathconf(vnode_t *, int, ulong_t *, cred_t *,
236 237 caller_context_t *);
237 238
238 239 static int cachefs_read_backfs_nfsv4(vnode_t *vp, uio_t *uiop, int ioflag,
239 240 cred_t *cr, caller_context_t *ct);
240 241 static int cachefs_write_backfs_nfsv4(vnode_t *vp, uio_t *uiop, int ioflag,
241 242 cred_t *cr, caller_context_t *ct);
242 243 static int cachefs_getattr_backfs_nfsv4(vnode_t *vp, vattr_t *vap,
243 244 int flags, cred_t *cr, caller_context_t *ct);
244 245 static int cachefs_remove_backfs_nfsv4(vnode_t *dvp, char *nm, cred_t *cr,
245 246 vnode_t *vp);
246 247 static int cachefs_getpage_backfs_nfsv4(struct vnode *vp, offset_t off,
247 248 size_t len, uint_t *protp, struct page *pl[],
248 249 size_t plsz, struct seg *seg, caddr_t addr,
249 250 enum seg_rw rw, cred_t *cr);
250 251 static int cachefs_putpage_backfs_nfsv4(vnode_t *vp, offset_t off,
251 252 size_t len, int flags, cred_t *cr);
252 253 static int cachefs_map_backfs_nfsv4(struct vnode *vp, offset_t off,
253 254 struct as *as, caddr_t *addrp, size_t len, uchar_t prot,
254 255 uchar_t maxprot, uint_t flags, cred_t *cr);
255 256 static int cachefs_space_backfs_nfsv4(struct vnode *vp, int cmd,
256 257 struct flock64 *bfp, int flag, offset_t offset,
257 258 cred_t *cr, caller_context_t *ct);
258 259
259 260 struct vnodeops *cachefs_vnodeops;
260 261
261 262 static const fs_operation_def_t cachefs_vnodeops_template[] = {
262 263 VOPNAME_OPEN, { .vop_open = cachefs_open },
263 264 VOPNAME_CLOSE, { .vop_close = cachefs_close },
264 265 VOPNAME_READ, { .vop_read = cachefs_read },
265 266 VOPNAME_WRITE, { .vop_write = cachefs_write },
266 267 VOPNAME_IOCTL, { .vop_ioctl = cachefs_ioctl },
267 268 VOPNAME_GETATTR, { .vop_getattr = cachefs_getattr },
268 269 VOPNAME_SETATTR, { .vop_setattr = cachefs_setattr },
269 270 VOPNAME_ACCESS, { .vop_access = cachefs_access },
270 271 VOPNAME_LOOKUP, { .vop_lookup = cachefs_lookup },
271 272 VOPNAME_CREATE, { .vop_create = cachefs_create },
272 273 VOPNAME_REMOVE, { .vop_remove = cachefs_remove },
273 274 VOPNAME_LINK, { .vop_link = cachefs_link },
274 275 VOPNAME_RENAME, { .vop_rename = cachefs_rename },
275 276 VOPNAME_MKDIR, { .vop_mkdir = cachefs_mkdir },
276 277 VOPNAME_RMDIR, { .vop_rmdir = cachefs_rmdir },
277 278 VOPNAME_READDIR, { .vop_readdir = cachefs_readdir },
278 279 VOPNAME_SYMLINK, { .vop_symlink = cachefs_symlink },
279 280 VOPNAME_READLINK, { .vop_readlink = cachefs_readlink },
280 281 VOPNAME_FSYNC, { .vop_fsync = cachefs_fsync },
281 282 VOPNAME_INACTIVE, { .vop_inactive = cachefs_inactive },
282 283 VOPNAME_FID, { .vop_fid = cachefs_fid },
283 284 VOPNAME_RWLOCK, { .vop_rwlock = cachefs_rwlock },
284 285 VOPNAME_RWUNLOCK, { .vop_rwunlock = cachefs_rwunlock },
285 286 VOPNAME_SEEK, { .vop_seek = cachefs_seek },
286 287 VOPNAME_FRLOCK, { .vop_frlock = cachefs_frlock },
287 288 VOPNAME_SPACE, { .vop_space = cachefs_space },
288 289 VOPNAME_REALVP, { .vop_realvp = cachefs_realvp },
289 290 VOPNAME_GETPAGE, { .vop_getpage = cachefs_getpage },
290 291 VOPNAME_PUTPAGE, { .vop_putpage = cachefs_putpage },
291 292 VOPNAME_MAP, { .vop_map = cachefs_map },
292 293 VOPNAME_ADDMAP, { .vop_addmap = cachefs_addmap },
293 294 VOPNAME_DELMAP, { .vop_delmap = cachefs_delmap },
294 295 VOPNAME_DUMP, { .vop_dump = cachefs_dump },
295 296 VOPNAME_PATHCONF, { .vop_pathconf = cachefs_pathconf },
296 297 VOPNAME_PAGEIO, { .vop_pageio = cachefs_pageio },
297 298 VOPNAME_SETSECATTR, { .vop_setsecattr = cachefs_setsecattr },
298 299 VOPNAME_GETSECATTR, { .vop_getsecattr = cachefs_getsecattr },
299 300 VOPNAME_SHRLOCK, { .vop_shrlock = cachefs_shrlock },
300 301 NULL, NULL
301 302 };
302 303
303 304 /* forward declarations of statics */
304 305 static void cachefs_modified(cnode_t *cp);
305 306 static int cachefs_modified_alloc(cnode_t *cp);
306 307
307 308 int
308 309 cachefs_init_vnops(char *name)
309 310 {
310 311 return (vn_make_ops(name,
311 312 cachefs_vnodeops_template, &cachefs_vnodeops));
312 313 }
313 314
314 315 struct vnodeops *
315 316 cachefs_getvnodeops(void)
316 317 {
317 318 return (cachefs_vnodeops);
318 319 }
319 320
320 321 static int
321 322 cachefs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
322 323 {
323 324 int error = 0;
324 325 cnode_t *cp = VTOC(*vpp);
325 326 fscache_t *fscp = C_TO_FSCACHE(cp);
326 327 int held = 0;
327 328 int type;
328 329 int connected = 0;
329 330
330 331 #ifdef CFSDEBUG
331 332 CFS_DEBUG(CFSDEBUG_VOPS)
332 333 printf("cachefs_open: ENTER vpp %p flag %x\n",
333 334 (void *)vpp, flag);
334 335 #endif
335 336 if (getzoneid() != GLOBAL_ZONEID) {
336 337 error = EPERM;
337 338 goto out;
338 339 }
339 340 if ((flag & FWRITE) &&
340 341 ((*vpp)->v_type == VDIR || (*vpp)->v_type == VLNK)) {
341 342 error = EISDIR;
342 343 goto out;
343 344 }
344 345
345 346 /*
346 347 * Cachefs only provides pass-through support for NFSv4,
347 348 * and all vnode operations are passed through to the
348 349 * back file system. For NFSv4 pass-through to work, only
349 350 * connected operation is supported, the cnode backvp must
350 351 * exist, and cachefs optional (eg., disconnectable) flags
351 352 * are turned off. Assert these conditions to ensure that
352 353 * the backfilesystem is called for the open operation.
353 354 */
354 355 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
355 356 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
356 357
357 358 for (;;) {
358 359 /* get (or renew) access to the file system */
359 360 if (held) {
360 361 /* Won't loop with NFSv4 connected behavior */
361 362 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
362 363 cachefs_cd_release(fscp);
363 364 held = 0;
364 365 }
365 366 error = cachefs_cd_access(fscp, connected, 0);
366 367 if (error)
367 368 goto out;
368 369 held = 1;
369 370
370 371 mutex_enter(&cp->c_statelock);
371 372
372 373 /* grab creds if we do not have any yet */
373 374 if (cp->c_cred == NULL) {
374 375 crhold(cr);
375 376 cp->c_cred = cr;
376 377 }
377 378 cp->c_flags |= CN_NEEDOPEN;
378 379
379 380 /* if we are disconnected */
380 381 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
381 382 /* if we cannot write to the file system */
382 383 if ((flag & FWRITE) && CFS_ISFS_WRITE_AROUND(fscp)) {
383 384 mutex_exit(&cp->c_statelock);
384 385 connected = 1;
385 386 continue;
386 387 }
387 388 /*
388 389 * Allow read only requests to continue
389 390 */
390 391 if ((flag & (FWRITE|FREAD)) == FREAD) {
391 392 /* track the flag for opening the backvp */
392 393 cp->c_rdcnt++;
393 394 mutex_exit(&cp->c_statelock);
394 395 error = 0;
395 396 break;
396 397 }
397 398
398 399 /*
399 400 * check credentials - if this procs
400 401 * credentials don't match the creds in the
401 402 * cnode disallow writing while disconnected.
402 403 */
403 404 if (crcmp(cp->c_cred, CRED()) != 0 &&
404 405 secpolicy_vnode_access2(CRED(), *vpp,
405 406 cp->c_attr.va_uid, 0, VWRITE) != 0) {
406 407 mutex_exit(&cp->c_statelock);
407 408 connected = 1;
408 409 continue;
409 410 }
410 411 /* to get here, we know that the WRITE flag is on */
411 412 cp->c_wrcnt++;
412 413 if (flag & FREAD)
413 414 cp->c_rdcnt++;
414 415 }
415 416
416 417 /* else if we are connected */
417 418 else {
418 419 /* if cannot use the cached copy of the file */
419 420 if ((flag & FWRITE) && CFS_ISFS_WRITE_AROUND(fscp) &&
420 421 ((cp->c_flags & CN_NOCACHE) == 0))
421 422 cachefs_nocache(cp);
422 423
423 424 /* pass open to the back file */
424 425 if (cp->c_backvp) {
425 426 cp->c_flags &= ~CN_NEEDOPEN;
426 427 CFS_DPRINT_BACKFS_NFSV4(fscp,
427 428 ("cachefs_open (nfsv4): cnode %p, "
428 429 "backvp %p\n", cp, cp->c_backvp));
429 430 error = VOP_OPEN(&cp->c_backvp, flag, cr, ct);
430 431 if (CFS_TIMEOUT(fscp, error)) {
431 432 mutex_exit(&cp->c_statelock);
432 433 cachefs_cd_release(fscp);
433 434 held = 0;
434 435 cachefs_cd_timedout(fscp);
435 436 continue;
436 437 } else if (error) {
437 438 mutex_exit(&cp->c_statelock);
438 439 break;
439 440 }
440 441 } else {
441 442 /* backvp will be VOP_OPEN'd later */
442 443 if (flag & FREAD)
443 444 cp->c_rdcnt++;
444 445 if (flag & FWRITE)
445 446 cp->c_wrcnt++;
446 447 }
447 448
448 449 /*
449 450 * Now perform a consistency check on the file.
450 451 * If strict consistency then force a check to
451 452 * the backfs even if the timeout has not expired
452 453 * for close-to-open consistency.
453 454 */
454 455 type = 0;
455 456 if (fscp->fs_consttype == CFS_FS_CONST_STRICT)
456 457 type = C_BACK_CHECK;
457 458 error = CFSOP_CHECK_COBJECT(fscp, cp, type, cr);
458 459 if (CFS_TIMEOUT(fscp, error)) {
459 460 mutex_exit(&cp->c_statelock);
460 461 cachefs_cd_release(fscp);
461 462 held = 0;
462 463 cachefs_cd_timedout(fscp);
463 464 continue;
464 465 }
465 466 }
466 467 mutex_exit(&cp->c_statelock);
467 468 break;
468 469 }
469 470 if (held)
470 471 cachefs_cd_release(fscp);
471 472 out:
472 473 #ifdef CFS_CD_DEBUG
473 474 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
474 475 #endif
475 476 #ifdef CFSDEBUG
476 477 CFS_DEBUG(CFSDEBUG_VOPS)
477 478 printf("cachefs_open: EXIT vpp %p error %d\n",
478 479 (void *)vpp, error);
479 480 #endif
480 481 return (error);
481 482 }
482 483
483 484 /* ARGSUSED */
484 485 static int
485 486 cachefs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
486 487 caller_context_t *ct)
487 488 {
488 489 int error = 0;
489 490 cnode_t *cp = VTOC(vp);
490 491 fscache_t *fscp = C_TO_FSCACHE(cp);
491 492 int held = 0;
492 493 int connected = 0;
493 494 int close_cnt = 1;
494 495 cachefscache_t *cachep;
495 496
496 497 #ifdef CFSDEBUG
497 498 CFS_DEBUG(CFSDEBUG_VOPS)
498 499 printf("cachefs_close: ENTER vp %p\n", (void *)vp);
499 500 #endif
500 501 /*
501 502 * Cachefs only provides pass-through support for NFSv4,
502 503 * and all vnode operations are passed through to the
503 504 * back file system. For NFSv4 pass-through to work, only
504 505 * connected operation is supported, the cnode backvp must
505 506 * exist, and cachefs optional (eg., disconnectable) flags
506 507 * are turned off. Assert these conditions to ensure that
507 508 * the backfilesystem is called for the close operation.
508 509 */
509 510 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
510 511 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
511 512
512 513 /*
513 514 * File could have been passed in or inherited from the global zone, so
514 515 * we don't want to flat out reject the request; we'll just leave things
515 516 * the way they are and let the backfs (NFS) deal with it.
516 517 */
517 518 /* get rid of any local locks */
518 519 if (CFS_ISFS_LLOCK(fscp)) {
519 520 (void) cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
520 521 }
521 522
522 523 /* clean up if this is the daemon closing down */
523 524 if ((fscp->fs_cddaemonid == ttoproc(curthread)->p_pid) &&
524 525 ((ttoproc(curthread)->p_pid) != 0) &&
525 526 (vp == fscp->fs_rootvp) &&
526 527 (count == 1)) {
527 528 mutex_enter(&fscp->fs_cdlock);
528 529 fscp->fs_cddaemonid = 0;
529 530 if (fscp->fs_dlogfile)
530 531 fscp->fs_cdconnected = CFS_CD_DISCONNECTED;
531 532 else
532 533 fscp->fs_cdconnected = CFS_CD_CONNECTED;
533 534 cv_broadcast(&fscp->fs_cdwaitcv);
534 535 mutex_exit(&fscp->fs_cdlock);
535 536 if (fscp->fs_flags & CFS_FS_ROOTFS) {
536 537 cachep = fscp->fs_cache;
537 538 mutex_enter(&cachep->c_contentslock);
538 539 ASSERT(cachep->c_rootdaemonid != 0);
539 540 cachep->c_rootdaemonid = 0;
540 541 mutex_exit(&cachep->c_contentslock);
541 542 }
542 543 return (0);
543 544 }
544 545
545 546 for (;;) {
546 547 /* get (or renew) access to the file system */
547 548 if (held) {
548 549 /* Won't loop with NFSv4 connected behavior */
549 550 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
550 551 cachefs_cd_release(fscp);
551 552 held = 0;
552 553 }
553 554 error = cachefs_cd_access(fscp, connected, 0);
554 555 if (error)
555 556 goto out;
556 557 held = 1;
557 558 connected = 0;
558 559
559 560 /* if not the last close */
560 561 if (count > 1) {
561 562 if (fscp->fs_cdconnected != CFS_CD_CONNECTED)
562 563 goto out;
563 564 mutex_enter(&cp->c_statelock);
564 565 if (cp->c_backvp) {
565 566 CFS_DPRINT_BACKFS_NFSV4(fscp,
566 567 ("cachefs_close (nfsv4): cnode %p, "
567 568 "backvp %p\n", cp, cp->c_backvp));
568 569 error = VOP_CLOSE(cp->c_backvp, flag, count,
569 570 offset, cr, ct);
570 571 if (CFS_TIMEOUT(fscp, error)) {
571 572 mutex_exit(&cp->c_statelock);
572 573 cachefs_cd_release(fscp);
573 574 held = 0;
574 575 cachefs_cd_timedout(fscp);
575 576 continue;
576 577 }
577 578 }
578 579 mutex_exit(&cp->c_statelock);
579 580 goto out;
580 581 }
581 582
582 583 /*
583 584 * If the file is an unlinked file, then flush the lookup
584 585 * cache so that inactive will be called if this is
585 586 * the last reference. It will invalidate all of the
586 587 * cached pages, without writing them out. Writing them
587 588 * out is not required because they will be written to a
588 589 * file which will be immediately removed.
589 590 */
590 591 if (cp->c_unldvp != NULL) {
591 592 dnlc_purge_vp(vp);
592 593 mutex_enter(&cp->c_statelock);
593 594 error = cp->c_error;
594 595 cp->c_error = 0;
595 596 mutex_exit(&cp->c_statelock);
596 597 /* always call VOP_CLOSE() for back fs vnode */
597 598 }
598 599
599 600 /* force dirty data to stable storage */
600 601 else if ((vp->v_type == VREG) && (flag & FWRITE) &&
601 602 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
602 603 /* clean the cachefs pages synchronously */
603 604 error = cachefs_putpage_common(vp, (offset_t)0,
604 605 0, 0, cr);
605 606 if (CFS_TIMEOUT(fscp, error)) {
606 607 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
607 608 cachefs_cd_release(fscp);
608 609 held = 0;
609 610 cachefs_cd_timedout(fscp);
610 611 continue;
611 612 } else {
612 613 connected = 1;
613 614 continue;
614 615 }
615 616 }
616 617
617 618 /* if no space left in cache, wait until connected */
618 619 if ((error == ENOSPC) &&
619 620 (fscp->fs_cdconnected != CFS_CD_CONNECTED)) {
620 621 connected = 1;
621 622 continue;
622 623 }
623 624
624 625 /* clear the cnode error if putpage worked */
625 626 if ((error == 0) && cp->c_error) {
626 627 mutex_enter(&cp->c_statelock);
627 628 cp->c_error = 0;
628 629 mutex_exit(&cp->c_statelock);
629 630 }
630 631
631 632 /* if any other important error */
632 633 if (cp->c_error) {
633 634 /* get rid of the pages */
634 635 (void) cachefs_putpage_common(vp,
635 636 (offset_t)0, 0, B_INVAL | B_FORCE, cr);
636 637 dnlc_purge_vp(vp);
637 638 }
638 639 }
639 640
640 641 mutex_enter(&cp->c_statelock);
641 642 if (cp->c_backvp &&
642 643 (fscp->fs_cdconnected == CFS_CD_CONNECTED)) {
643 644 error = VOP_CLOSE(cp->c_backvp, flag, close_cnt,
644 645 offset, cr, ct);
645 646 if (CFS_TIMEOUT(fscp, error)) {
646 647 mutex_exit(&cp->c_statelock);
647 648 cachefs_cd_release(fscp);
648 649 held = 0;
649 650 cachefs_cd_timedout(fscp);
650 651 /* don't decrement the vnode counts again */
651 652 close_cnt = 0;
652 653 continue;
653 654 }
654 655 }
655 656 mutex_exit(&cp->c_statelock);
656 657 break;
657 658 }
658 659
659 660 mutex_enter(&cp->c_statelock);
660 661 if (!error)
661 662 error = cp->c_error;
662 663 cp->c_error = 0;
663 664 mutex_exit(&cp->c_statelock);
664 665
665 666 out:
666 667 if (held)
667 668 cachefs_cd_release(fscp);
668 669 #ifdef CFS_CD_DEBUG
669 670 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
670 671 #endif
671 672
672 673 #ifdef CFSDEBUG
673 674 CFS_DEBUG(CFSDEBUG_VOPS)
674 675 printf("cachefs_close: EXIT vp %p\n", (void *)vp);
675 676 #endif
676 677 return (error);
677 678 }
678 679
679 680 /*ARGSUSED*/
680 681 static int
681 682 cachefs_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
682 683 caller_context_t *ct)
683 684 {
684 685 struct cnode *cp = VTOC(vp);
685 686 fscache_t *fscp = C_TO_FSCACHE(cp);
686 687 register u_offset_t off;
687 688 register int mapoff;
688 689 register caddr_t base;
689 690 int n;
690 691 offset_t diff;
691 692 uint_t flags = 0;
692 693 int error = 0;
693 694
694 695 #if 0
695 696 if (vp->v_flag & VNOCACHE)
696 697 flags = SM_INVAL;
697 698 #endif
698 699 if (getzoneid() != GLOBAL_ZONEID)
699 700 return (EPERM);
700 701 if (vp->v_type != VREG)
701 702 return (EISDIR);
702 703
703 704 ASSERT(RW_READ_HELD(&cp->c_rwlock));
704 705
705 706 if (uiop->uio_resid == 0)
706 707 return (0);
707 708
708 709
709 710 if (uiop->uio_loffset < (offset_t)0)
710 711 return (EINVAL);
711 712
712 713 /*
713 714 * Call backfilesystem to read if NFSv4, the cachefs code
714 715 * does the read from the back filesystem asynchronously
715 716 * which is not supported by pass-through functionality.
716 717 */
717 718 if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
718 719 error = cachefs_read_backfs_nfsv4(vp, uiop, ioflag, cr, ct);
719 720 goto out;
720 721 }
721 722
722 723 if (MANDLOCK(vp, cp->c_attr.va_mode)) {
723 724 error = chklock(vp, FREAD, (offset_t)uiop->uio_loffset,
724 725 uiop->uio_resid, uiop->uio_fmode, ct);
725 726 if (error)
726 727 return (error);
727 728 }
728 729
729 730 /*
730 731 * Sit in a loop and transfer (uiomove) the data in up to
731 732 * MAXBSIZE chunks. Each chunk is mapped into the kernel's
732 733 * address space as needed and then released.
733 734 */
734 735 do {
735 736 /*
736 737 * off Offset of current MAXBSIZE chunk
737 738 * mapoff Offset within the current chunk
738 739 * n Number of bytes to move from this chunk
739 740 * base kernel address of mapped in chunk
740 741 */
741 742 off = uiop->uio_loffset & (offset_t)MAXBMASK;
742 743 mapoff = uiop->uio_loffset & MAXBOFFSET;
743 744 n = MAXBSIZE - mapoff;
744 745 if (n > uiop->uio_resid)
745 746 n = (uint_t)uiop->uio_resid;
746 747
747 748 /* perform consistency check */
748 749 error = cachefs_cd_access(fscp, 0, 0);
749 750 if (error)
750 751 break;
751 752 mutex_enter(&cp->c_statelock);
752 753 error = CFSOP_CHECK_COBJECT(fscp, cp, 0, cr);
753 754 diff = cp->c_size - uiop->uio_loffset;
754 755 mutex_exit(&cp->c_statelock);
755 756 if (CFS_TIMEOUT(fscp, error)) {
756 757 cachefs_cd_release(fscp);
757 758 cachefs_cd_timedout(fscp);
758 759 error = 0;
759 760 continue;
760 761 }
761 762 cachefs_cd_release(fscp);
762 763
763 764 if (error)
764 765 break;
765 766
766 767 if (diff <= (offset_t)0)
767 768 break;
768 769 if (diff < (offset_t)n)
769 770 n = diff;
770 771
771 772 base = segmap_getmapflt(segkmap, vp, off, (uint_t)n, 1, S_READ);
772 773
773 774 error = segmap_fault(kas.a_hat, segkmap, base, n,
774 775 F_SOFTLOCK, S_READ);
775 776 if (error) {
776 777 (void) segmap_release(segkmap, base, 0);
777 778 if (FC_CODE(error) == FC_OBJERR)
778 779 error = FC_ERRNO(error);
779 780 else
780 781 error = EIO;
781 782 break;
782 783 }
783 784 error = uiomove(base+mapoff, n, UIO_READ, uiop);
784 785 (void) segmap_fault(kas.a_hat, segkmap, base, n,
785 786 F_SOFTUNLOCK, S_READ);
786 787 if (error == 0) {
787 788 /*
788 789 * if we read a whole page(s), or to eof,
789 790 * we won't need this page(s) again soon.
790 791 */
791 792 if (n + mapoff == MAXBSIZE ||
792 793 uiop->uio_loffset == cp->c_size)
793 794 flags |= SM_DONTNEED;
794 795 }
795 796 (void) segmap_release(segkmap, base, flags);
796 797 } while (error == 0 && uiop->uio_resid > 0);
797 798
798 799 out:
799 800 #ifdef CFSDEBUG
800 801 CFS_DEBUG(CFSDEBUG_VOPS)
801 802 printf("cachefs_read: EXIT error %d resid %ld\n", error,
802 803 uiop->uio_resid);
803 804 #endif
804 805 return (error);
805 806 }
806 807
807 808 /*
808 809 * cachefs_read_backfs_nfsv4
809 810 *
810 811 * Call NFSv4 back filesystem to handle the read (cachefs
811 812 * pass-through support for NFSv4).
812 813 */
813 814 static int
814 815 cachefs_read_backfs_nfsv4(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
815 816 caller_context_t *ct)
816 817 {
817 818 cnode_t *cp = VTOC(vp);
818 819 fscache_t *fscp = C_TO_FSCACHE(cp);
819 820 vnode_t *backvp;
820 821 int error;
821 822
822 823 /*
823 824 * For NFSv4 pass-through to work, only connected operation
824 825 * is supported, the cnode backvp must exist, and cachefs
825 826 * optional (eg., disconnectable) flags are turned off. Assert
826 827 * these conditions for the read operation.
827 828 */
828 829 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
829 830 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
830 831
831 832 /* Call backfs vnode op after extracting backvp */
832 833 mutex_enter(&cp->c_statelock);
833 834 backvp = cp->c_backvp;
834 835 mutex_exit(&cp->c_statelock);
835 836
836 837 CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_read_backfs_nfsv4: cnode %p, "
837 838 "backvp %p\n", cp, backvp));
838 839
839 840 (void) VOP_RWLOCK(backvp, V_WRITELOCK_FALSE, ct);
840 841 error = VOP_READ(backvp, uiop, ioflag, cr, ct);
841 842 VOP_RWUNLOCK(backvp, V_WRITELOCK_FALSE, ct);
842 843
843 844 /* Increment cache miss counter */
844 845 fscp->fs_stats.st_misses++;
845 846
846 847 return (error);
847 848 }
848 849
849 850 /*ARGSUSED*/
850 851 static int
851 852 cachefs_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
852 853 caller_context_t *ct)
853 854 {
854 855 struct cnode *cp = VTOC(vp);
855 856 fscache_t *fscp = C_TO_FSCACHE(cp);
856 857 int error = 0;
857 858 u_offset_t off;
858 859 caddr_t base;
859 860 uint_t bsize;
860 861 uint_t flags;
861 862 int n, on;
862 863 rlim64_t limit = uiop->uio_llimit;
863 864 ssize_t resid;
864 865 offset_t offset;
865 866 offset_t remainder;
866 867
867 868 #ifdef CFSDEBUG
868 869 CFS_DEBUG(CFSDEBUG_VOPS)
869 870 printf(
870 871 "cachefs_write: ENTER vp %p offset %llu count %ld cflags %x\n",
871 872 (void *)vp, uiop->uio_loffset, uiop->uio_resid,
872 873 cp->c_flags);
873 874 #endif
874 875 if (getzoneid() != GLOBAL_ZONEID) {
875 876 error = EPERM;
876 877 goto out;
877 878 }
878 879 if (vp->v_type != VREG) {
879 880 error = EISDIR;
880 881 goto out;
881 882 }
882 883
883 884 ASSERT(RW_WRITE_HELD(&cp->c_rwlock));
884 885
885 886 if (uiop->uio_resid == 0) {
886 887 goto out;
887 888 }
888 889
889 890 /* Call backfilesystem to write if NFSv4 */
890 891 if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
891 892 error = cachefs_write_backfs_nfsv4(vp, uiop, ioflag, cr, ct);
892 893 goto out2;
893 894 }
894 895
895 896 if (MANDLOCK(vp, cp->c_attr.va_mode)) {
896 897 error = chklock(vp, FWRITE, (offset_t)uiop->uio_loffset,
897 898 uiop->uio_resid, uiop->uio_fmode, ct);
898 899 if (error)
899 900 goto out;
900 901 }
901 902
902 903 if (ioflag & FAPPEND) {
903 904 for (;;) {
904 905 /* do consistency check to get correct file size */
905 906 error = cachefs_cd_access(fscp, 0, 1);
906 907 if (error)
907 908 goto out;
908 909 mutex_enter(&cp->c_statelock);
909 910 error = CFSOP_CHECK_COBJECT(fscp, cp, 0, cr);
910 911 uiop->uio_loffset = cp->c_size;
911 912 mutex_exit(&cp->c_statelock);
912 913 if (CFS_TIMEOUT(fscp, error)) {
913 914 cachefs_cd_release(fscp);
914 915 cachefs_cd_timedout(fscp);
915 916 continue;
916 917 }
917 918 cachefs_cd_release(fscp);
918 919 if (error)
919 920 goto out;
920 921 break;
921 922 }
922 923 }
923 924
924 925 if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
925 926 limit = MAXOFFSET_T;
926 927
927 928 if (uiop->uio_loffset >= limit) {
928 929 proc_t *p = ttoproc(curthread);
929 930
930 931 mutex_enter(&p->p_lock);
931 932 (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls,
932 933 p, RCA_UNSAFE_SIGINFO);
933 934 mutex_exit(&p->p_lock);
934 935 error = EFBIG;
935 936 goto out;
936 937 }
937 938 if (uiop->uio_loffset > fscp->fs_offmax) {
938 939 error = EFBIG;
939 940 goto out;
940 941 }
941 942
942 943 if (limit > fscp->fs_offmax)
943 944 limit = fscp->fs_offmax;
944 945
945 946 if (uiop->uio_loffset < (offset_t)0) {
946 947 error = EINVAL;
947 948 goto out;
948 949 }
949 950
950 951 offset = uiop->uio_loffset + uiop->uio_resid;
951 952 /*
952 953 * Check to make sure that the process will not exceed
953 954 * its limit on file size. It is okay to write up to
954 955 * the limit, but not beyond. Thus, the write which
955 956 * reaches the limit will be short and the next write
956 957 * will return an error.
957 958 */
958 959 remainder = 0;
959 960 if (offset > limit) {
960 961 remainder = (int)(offset - (u_offset_t)limit);
961 962 uiop->uio_resid = limit - uiop->uio_loffset;
962 963 if (uiop->uio_resid <= 0) {
963 964 proc_t *p = ttoproc(curthread);
964 965
965 966 uiop->uio_resid += remainder;
966 967 mutex_enter(&p->p_lock);
967 968 (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
968 969 p->p_rctls, p, RCA_UNSAFE_SIGINFO);
969 970 mutex_exit(&p->p_lock);
970 971 error = EFBIG;
971 972 goto out;
972 973 }
973 974 }
974 975
975 976 resid = uiop->uio_resid;
976 977 offset = uiop->uio_loffset;
977 978 bsize = vp->v_vfsp->vfs_bsize;
978 979
979 980 /* loop around and do the write in MAXBSIZE chunks */
980 981 do {
981 982 /* mapping offset */
982 983 off = uiop->uio_loffset & (offset_t)MAXBMASK;
983 984 on = uiop->uio_loffset & MAXBOFFSET; /* Rel. offset */
984 985 n = MAXBSIZE - on;
985 986 if (n > uiop->uio_resid)
986 987 n = (int)uiop->uio_resid;
987 988
988 989 /*
989 990 * Touch the page and fault it in if it is not in
990 991 * core before segmap_getmapflt can lock it. This
991 992 * is to avoid the deadlock if the buffer is mapped
992 993 * to the same file through mmap which we want to
993 994 * write to.
994 995 */
995 996 uio_prefaultpages((long)n, uiop);
996 997
997 998 base = segmap_getmap(segkmap, vp, off);
998 999 error = cachefs_writepage(vp, (base + on), n, uiop);
999 1000 if (error == 0) {
1000 1001 flags = 0;
1001 1002 /*
1002 1003 * Have written a whole block.Start an
1003 1004 * asynchronous write and mark the buffer to
1004 1005 * indicate that it won't be needed again
1005 1006 * soon.
1006 1007 */
1007 1008 if (n + on == bsize) {
1008 1009 flags = SM_WRITE |SM_ASYNC |SM_DONTNEED;
1009 1010 }
1010 1011 #if 0
1011 1012 /* XXX need to understand this */
1012 1013 if ((ioflag & (FSYNC|FDSYNC)) ||
1013 1014 (cp->c_backvp && vn_has_flocks(cp->c_backvp))) {
1014 1015 flags &= ~SM_ASYNC;
1015 1016 flags |= SM_WRITE;
1016 1017 }
1017 1018 #else
1018 1019 if (ioflag & (FSYNC|FDSYNC)) {
1019 1020 flags &= ~SM_ASYNC;
1020 1021 flags |= SM_WRITE;
1021 1022 }
1022 1023 #endif
1023 1024 error = segmap_release(segkmap, base, flags);
1024 1025 } else {
1025 1026 (void) segmap_release(segkmap, base, 0);
1026 1027 }
1027 1028 } while (error == 0 && uiop->uio_resid > 0);
1028 1029
1029 1030 out:
1030 1031 if (error == EINTR && (ioflag & (FSYNC|FDSYNC))) {
1031 1032 uiop->uio_resid = resid;
1032 1033 uiop->uio_loffset = offset;
1033 1034 } else
1034 1035 uiop->uio_resid += remainder;
1035 1036
1036 1037 out2:
1037 1038 #ifdef CFSDEBUG
1038 1039 CFS_DEBUG(CFSDEBUG_VOPS)
1039 1040 printf("cachefs_write: EXIT error %d\n", error);
1040 1041 #endif
1041 1042 return (error);
1042 1043 }
1043 1044
1044 1045 /*
1045 1046 * cachefs_write_backfs_nfsv4
1046 1047 *
1047 1048 * Call NFSv4 back filesystem to handle the write (cachefs
1048 1049 * pass-through support for NFSv4).
1049 1050 */
1050 1051 static int
1051 1052 cachefs_write_backfs_nfsv4(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
1052 1053 caller_context_t *ct)
1053 1054 {
1054 1055 cnode_t *cp = VTOC(vp);
1055 1056 fscache_t *fscp = C_TO_FSCACHE(cp);
1056 1057 vnode_t *backvp;
1057 1058 int error;
1058 1059
1059 1060 /*
1060 1061 * For NFSv4 pass-through to work, only connected operation
1061 1062 * is supported, the cnode backvp must exist, and cachefs
1062 1063 * optional (eg., disconnectable) flags are turned off. Assert
1063 1064 * these conditions for the read operation.
1064 1065 */
1065 1066 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
1066 1067 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
1067 1068
1068 1069 /* Call backfs vnode op after extracting the backvp */
1069 1070 mutex_enter(&cp->c_statelock);
1070 1071 backvp = cp->c_backvp;
1071 1072 mutex_exit(&cp->c_statelock);
1072 1073
1073 1074 CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_write_backfs_nfsv4: cnode %p, "
1074 1075 "backvp %p\n", cp, backvp));
1075 1076 (void) VOP_RWLOCK(backvp, V_WRITELOCK_TRUE, ct);
1076 1077 error = VOP_WRITE(backvp, uiop, ioflag, cr, ct);
1077 1078 VOP_RWUNLOCK(backvp, V_WRITELOCK_TRUE, ct);
1078 1079
1079 1080 return (error);
1080 1081 }
1081 1082
1082 1083 /*
1083 1084 * see if we've charged ourselves for frontfile data at
1084 1085 * the given offset. If not, allocate a block for it now.
1085 1086 */
1086 1087 static int
1087 1088 cachefs_charge_page(struct cnode *cp, u_offset_t offset)
1088 1089 {
1089 1090 u_offset_t blockoff;
1090 1091 int error;
1091 1092 int inc;
1092 1093
1093 1094 ASSERT(MUTEX_HELD(&cp->c_statelock));
1094 1095 /*LINTED*/
1095 1096 ASSERT(PAGESIZE <= MAXBSIZE);
1096 1097
1097 1098 error = 0;
1098 1099 blockoff = offset & (offset_t)MAXBMASK;
1099 1100
1100 1101 /* get the front file if necessary so allocblocks works */
1101 1102 if ((cp->c_frontvp == NULL) &&
1102 1103 ((cp->c_flags & CN_NOCACHE) == 0)) {
1103 1104 (void) cachefs_getfrontfile(cp);
1104 1105 }
1105 1106 if (cp->c_flags & CN_NOCACHE)
1106 1107 return (1);
1107 1108
1108 1109 if (cachefs_check_allocmap(cp, blockoff))
1109 1110 return (0);
1110 1111
1111 1112 for (inc = PAGESIZE; inc < MAXBSIZE; inc += PAGESIZE)
1112 1113 if (cachefs_check_allocmap(cp, blockoff+inc))
1113 1114 return (0);
1114 1115
1115 1116 error = cachefs_allocblocks(C_TO_FSCACHE(cp)->fs_cache, 1,
1116 1117 cp->c_metadata.md_rltype);
1117 1118 if (error == 0) {
1118 1119 cp->c_metadata.md_frontblks++;
1119 1120 cp->c_flags |= CN_UPDATED;
1120 1121 }
1121 1122 return (error);
1122 1123 }
1123 1124
1124 1125 /*
1125 1126 * Called only by cachefs_write to write 1 page or less of data.
1126 1127 * base - base address kernel addr space
1127 1128 * tcount - Total bytes to move - < MAXBSIZE
1128 1129 */
1129 1130 static int
1130 1131 cachefs_writepage(vnode_t *vp, caddr_t base, int tcount, uio_t *uiop)
1131 1132 {
1132 1133 struct cnode *cp = VTOC(vp);
1133 1134 fscache_t *fscp = C_TO_FSCACHE(cp);
1134 1135 register int n;
1135 1136 register u_offset_t offset;
1136 1137 int error = 0, terror;
1137 1138 extern struct as kas;
1138 1139 u_offset_t lastpage_off;
1139 1140 int pagecreate = 0;
1140 1141 int newpage;
1141 1142
1142 1143 #ifdef CFSDEBUG
1143 1144 CFS_DEBUG(CFSDEBUG_VOPS)
1144 1145 printf(
1145 1146 "cachefs_writepage: ENTER vp %p offset %llu len %ld\\\n",
1146 1147 (void *)vp, uiop->uio_loffset, uiop->uio_resid);
1147 1148 #endif
1148 1149
1149 1150 /*
1150 1151 * Move bytes in PAGESIZE chunks. We must avoid spanning pages in
1151 1152 * uiomove() because page faults may cause the cache to be invalidated
1152 1153 * out from under us.
1153 1154 */
1154 1155 do {
1155 1156 offset = uiop->uio_loffset;
1156 1157 lastpage_off = (cp->c_size - 1) & (offset_t)PAGEMASK;
1157 1158
1158 1159 /*
1159 1160 * If not connected then need to make sure we have space
1160 1161 * to perform the write. We could make this check
1161 1162 * a little tighter by only doing it if we are growing the file.
1162 1163 */
1163 1164 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
1164 1165 error = cachefs_allocblocks(fscp->fs_cache, 1,
1165 1166 cp->c_metadata.md_rltype);
1166 1167 if (error)
1167 1168 break;
1168 1169 cachefs_freeblocks(fscp->fs_cache, 1,
1169 1170 cp->c_metadata.md_rltype);
1170 1171 }
1171 1172
1172 1173 /*
1173 1174 * n is the number of bytes required to satisfy the request
1174 1175 * or the number of bytes to fill out the page.
1175 1176 */
1176 1177 n = (int)(PAGESIZE - ((uintptr_t)base & PAGEOFFSET));
1177 1178 if (n > tcount)
1178 1179 n = tcount;
1179 1180
1180 1181 /*
1181 1182 * The number of bytes of data in the last page can not
1182 1183 * be accurately be determined while page is being
1183 1184 * uiomove'd to and the size of the file being updated.
1184 1185 * Thus, inform threads which need to know accurately
1185 1186 * how much data is in the last page of the file. They
1186 1187 * will not do the i/o immediately, but will arrange for
1187 1188 * the i/o to happen later when this modify operation
1188 1189 * will have finished.
1189 1190 *
1190 1191 * in similar NFS code, this is done right before the
1191 1192 * uiomove(), which is best. but here in cachefs, we
1192 1193 * have two uiomove()s, so we must do it here.
1193 1194 */
1194 1195 ASSERT(!(cp->c_flags & CN_CMODINPROG));
1195 1196 mutex_enter(&cp->c_statelock);
1196 1197 cp->c_flags |= CN_CMODINPROG;
1197 1198 cp->c_modaddr = (offset & (offset_t)MAXBMASK);
1198 1199 mutex_exit(&cp->c_statelock);
1199 1200
1200 1201 /*
1201 1202 * Check to see if we can skip reading in the page
1202 1203 * and just allocate the memory. We can do this
1203 1204 * if we are going to rewrite the entire mapping
1204 1205 * or if we are going to write to or beyond the current
1205 1206 * end of file from the beginning of the mapping.
1206 1207 */
1207 1208 if ((offset > (lastpage_off + PAGEOFFSET)) ||
1208 1209 ((cp->c_size == 0) && (offset < PAGESIZE)) ||
1209 1210 ((uintptr_t)base & PAGEOFFSET) == 0 && (n == PAGESIZE ||
1210 1211 ((offset + n) >= cp->c_size))) {
1211 1212 pagecreate = 1;
1212 1213
1213 1214 /*
1214 1215 * segmap_pagecreate() returns 1 if it calls
1215 1216 * page_create_va() to allocate any pages.
1216 1217 */
1217 1218 newpage = segmap_pagecreate(segkmap,
1218 1219 (caddr_t)((uintptr_t)base & (uintptr_t)PAGEMASK),
1219 1220 PAGESIZE, 0);
1220 1221 /* do not zero page if we are overwriting all of it */
1221 1222 if (!((((uintptr_t)base & PAGEOFFSET) == 0) &&
1222 1223 (n == PAGESIZE))) {
1223 1224 (void) kzero((void *)
1224 1225 ((uintptr_t)base & (uintptr_t)PAGEMASK),
1225 1226 PAGESIZE);
1226 1227 }
1227 1228 error = uiomove(base, n, UIO_WRITE, uiop);
1228 1229
1229 1230 /*
1230 1231 * Unlock the page allocated by page_create_va()
1231 1232 * in segmap_pagecreate()
1232 1233 */
1233 1234 if (newpage)
1234 1235 segmap_pageunlock(segkmap,
1235 1236 (caddr_t)((uintptr_t)base &
1236 1237 (uintptr_t)PAGEMASK),
1237 1238 PAGESIZE, S_WRITE);
1238 1239 } else {
1239 1240 /*
1240 1241 * KLUDGE ! Use segmap_fault instead of faulting and
1241 1242 * using as_fault() to avoid a recursive readers lock
1242 1243 * on kas.
1243 1244 */
1244 1245 error = segmap_fault(kas.a_hat, segkmap, (caddr_t)
1245 1246 ((uintptr_t)base & (uintptr_t)PAGEMASK),
1246 1247 PAGESIZE, F_SOFTLOCK, S_WRITE);
1247 1248 if (error) {
1248 1249 if (FC_CODE(error) == FC_OBJERR)
1249 1250 error = FC_ERRNO(error);
1250 1251 else
1251 1252 error = EIO;
1252 1253 break;
1253 1254 }
1254 1255 error = uiomove(base, n, UIO_WRITE, uiop);
1255 1256 (void) segmap_fault(kas.a_hat, segkmap, (caddr_t)
1256 1257 ((uintptr_t)base & (uintptr_t)PAGEMASK),
1257 1258 PAGESIZE, F_SOFTUNLOCK, S_WRITE);
1258 1259 }
1259 1260 n = (int)(uiop->uio_loffset - offset); /* n = # bytes written */
1260 1261 base += n;
1261 1262 tcount -= n;
1262 1263
1263 1264 /* get access to the file system */
1264 1265 if ((terror = cachefs_cd_access(fscp, 0, 1)) != 0) {
1265 1266 error = terror;
1266 1267 break;
1267 1268 }
1268 1269
1269 1270 /*
1270 1271 * cp->c_attr.va_size is the maximum number of
1271 1272 * bytes known to be in the file.
1272 1273 * Make sure it is at least as high as the
1273 1274 * last byte we just wrote into the buffer.
1274 1275 */
1275 1276 mutex_enter(&cp->c_statelock);
1276 1277 if (cp->c_size < uiop->uio_loffset) {
1277 1278 cp->c_size = uiop->uio_loffset;
1278 1279 }
1279 1280 if (cp->c_size != cp->c_attr.va_size) {
1280 1281 cp->c_attr.va_size = cp->c_size;
1281 1282 cp->c_flags |= CN_UPDATED;
1282 1283 }
1283 1284 /* c_size is now correct, so we can clear modinprog */
1284 1285 cp->c_flags &= ~CN_CMODINPROG;
1285 1286 if (error == 0) {
1286 1287 cp->c_flags |= CDIRTY;
1287 1288 if (pagecreate && (cp->c_flags & CN_NOCACHE) == 0) {
1288 1289 /*
1289 1290 * if we're not in NOCACHE mode
1290 1291 * (i.e., single-writer), we update the
1291 1292 * allocmap here rather than waiting until
1292 1293 * cachefspush is called. This prevents
1293 1294 * getpage from clustering up pages from
1294 1295 * the backfile and stomping over the changes
1295 1296 * we make here.
1296 1297 */
1297 1298 if (cachefs_charge_page(cp, offset) == 0) {
1298 1299 cachefs_update_allocmap(cp,
1299 1300 offset & (offset_t)PAGEMASK,
1300 1301 (size_t)PAGESIZE);
1301 1302 }
1302 1303
1303 1304 /* else we ran out of space */
1304 1305 else {
1305 1306 /* nocache file if connected */
1306 1307 if (fscp->fs_cdconnected ==
1307 1308 CFS_CD_CONNECTED)
1308 1309 cachefs_nocache(cp);
1309 1310 /*
1310 1311 * If disconnected then cannot
1311 1312 * nocache the file. Let it have
1312 1313 * the space.
1313 1314 */
1314 1315 else {
1315 1316 cp->c_metadata.md_frontblks++;
1316 1317 cp->c_flags |= CN_UPDATED;
1317 1318 cachefs_update_allocmap(cp,
1318 1319 offset & (offset_t)PAGEMASK,
1319 1320 (size_t)PAGESIZE);
1320 1321 }
1321 1322 }
1322 1323 }
1323 1324 }
1324 1325 mutex_exit(&cp->c_statelock);
1325 1326 cachefs_cd_release(fscp);
1326 1327 } while (tcount > 0 && error == 0);
1327 1328
1328 1329 if (cp->c_flags & CN_CMODINPROG) {
1329 1330 /* XXX assert error != 0? FC_ERRNO() makes this more risky. */
1330 1331 mutex_enter(&cp->c_statelock);
1331 1332 cp->c_flags &= ~CN_CMODINPROG;
1332 1333 mutex_exit(&cp->c_statelock);
1333 1334 }
1334 1335
1335 1336 #ifdef CFS_CD_DEBUG
1336 1337 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
1337 1338 #endif
1338 1339
1339 1340 #ifdef CFSDEBUG
1340 1341 CFS_DEBUG(CFSDEBUG_VOPS)
1341 1342 printf("cachefs_writepage: EXIT error %d\n", error);
1342 1343 #endif
1343 1344
1344 1345 return (error);
1345 1346 }
1346 1347
1347 1348 /*
1348 1349 * Pushes out pages to the back and/or front file system.
1349 1350 */
1350 1351 static int
1351 1352 cachefs_push(vnode_t *vp, page_t *pp, u_offset_t *offp, size_t *lenp,
1352 1353 int flags, cred_t *cr)
1353 1354 {
1354 1355 struct cnode *cp = VTOC(vp);
1355 1356 struct buf *bp;
1356 1357 int error;
1357 1358 fscache_t *fscp = C_TO_FSCACHE(cp);
1358 1359 u_offset_t iooff;
1359 1360 size_t iolen;
1360 1361 u_offset_t lbn;
1361 1362 u_offset_t lbn_off;
1362 1363 uint_t bsize;
1363 1364
1364 1365 ASSERT((flags & B_ASYNC) == 0);
1365 1366 ASSERT(!vn_is_readonly(vp));
1366 1367 ASSERT(pp != NULL);
1367 1368 ASSERT(cr != NULL);
1368 1369
1369 1370 bsize = MAX(vp->v_vfsp->vfs_bsize, PAGESIZE);
1370 1371 lbn = pp->p_offset / bsize;
1371 1372 lbn_off = lbn * bsize;
1372 1373
1373 1374 /*
1374 1375 * Find a kluster that fits in one block, or in
1375 1376 * one page if pages are bigger than blocks. If
1376 1377 * there is less file space allocated than a whole
1377 1378 * page, we'll shorten the i/o request below.
1378 1379 */
1379 1380
1380 1381 pp = pvn_write_kluster(vp, pp, &iooff, &iolen, lbn_off,
1381 1382 roundup(bsize, PAGESIZE), flags);
1382 1383
1383 1384 /*
1384 1385 * The CN_CMODINPROG flag makes sure that we use a correct
1385 1386 * value of c_size, below. CN_CMODINPROG is set in
1386 1387 * cachefs_writepage(). When CN_CMODINPROG is set it
1387 1388 * indicates that a uiomove() is in progress and the c_size
1388 1389 * has not been made consistent with the new size of the
1389 1390 * file. When the uiomove() completes the c_size is updated
1390 1391 * and the CN_CMODINPROG flag is cleared.
1391 1392 *
1392 1393 * The CN_CMODINPROG flag makes sure that cachefs_push_front
1393 1394 * and cachefs_push_connected see a consistent value of
1394 1395 * c_size. Without this handshaking, it is possible that
1395 1396 * these routines will pick up the old value of c_size before
1396 1397 * the uiomove() in cachefs_writepage() completes. This will
1397 1398 * result in the vn_rdwr() being too small, and data loss.
1398 1399 *
1399 1400 * More precisely, there is a window between the time the
1400 1401 * uiomove() completes and the time the c_size is updated. If
1401 1402 * a VOP_PUTPAGE() operation intervenes in this window, the
1402 1403 * page will be picked up, because it is dirty; it will be
1403 1404 * unlocked, unless it was pagecreate'd. When the page is
1404 1405 * picked up as dirty, the dirty bit is reset
1405 1406 * (pvn_getdirty()). In cachefs_push_connected(), c_size is
1406 1407 * checked. This will still be the old size. Therefore, the
1407 1408 * page will not be written out to the correct length, and the
1408 1409 * page will be clean, so the data may disappear.
1409 1410 */
1410 1411 if (cp->c_flags & CN_CMODINPROG) {
1411 1412 mutex_enter(&cp->c_statelock);
1412 1413 if ((cp->c_flags & CN_CMODINPROG) &&
1413 1414 cp->c_modaddr + MAXBSIZE > iooff &&
1414 1415 cp->c_modaddr < iooff + iolen) {
1415 1416 page_t *plist;
1416 1417
1417 1418 /*
1418 1419 * A write is in progress for this region of
1419 1420 * the file. If we did not detect
1420 1421 * CN_CMODINPROG here then this path through
1421 1422 * cachefs_push_connected() would eventually
1422 1423 * do the vn_rdwr() and may not write out all
1423 1424 * of the data in the pages. We end up losing
1424 1425 * data. So we decide to set the modified bit
1425 1426 * on each page in the page list and mark the
1426 1427 * cnode with CDIRTY. This push will be
1427 1428 * restarted at some later time.
1428 1429 */
1429 1430
1430 1431 plist = pp;
1431 1432 while (plist != NULL) {
1432 1433 pp = plist;
1433 1434 page_sub(&plist, pp);
1434 1435 hat_setmod(pp);
1435 1436 page_io_unlock(pp);
1436 1437 page_unlock(pp);
1437 1438 }
1438 1439 cp->c_flags |= CDIRTY;
1439 1440 mutex_exit(&cp->c_statelock);
1440 1441 if (offp)
1441 1442 *offp = iooff;
1442 1443 if (lenp)
1443 1444 *lenp = iolen;
1444 1445 return (0);
1445 1446 }
1446 1447 mutex_exit(&cp->c_statelock);
1447 1448 }
1448 1449
1449 1450 /*
1450 1451 * Set the pages up for pageout.
1451 1452 */
1452 1453 bp = pageio_setup(pp, iolen, CTOV(cp), B_WRITE | flags);
1453 1454 if (bp == NULL) {
1454 1455
1455 1456 /*
1456 1457 * currently, there is no way for pageio_setup() to
1457 1458 * return NULL, since it uses its own scheme for
1458 1459 * kmem_alloc()ing that shouldn't return NULL, and
1459 1460 * since pageio_setup() itself dereferences the thing
1460 1461 * it's about to return. still, we need to be ready
1461 1462 * in case this ever does start happening.
1462 1463 */
1463 1464
1464 1465 error = ENOMEM;
1465 1466 goto writedone;
1466 1467 }
1467 1468 /*
1468 1469 * pageio_setup should have set b_addr to 0. This
1469 1470 * is correct since we want to do I/O on a page
1470 1471 * boundary. bp_mapin will use this addr to calculate
1471 1472 * an offset, and then set b_addr to the kernel virtual
1472 1473 * address it allocated for us.
1473 1474 */
1474 1475 bp->b_edev = 0;
1475 1476 bp->b_dev = 0;
1476 1477 bp->b_lblkno = (diskaddr_t)lbtodb(iooff);
1477 1478 bp_mapin(bp);
1478 1479
1479 1480 iolen = cp->c_size - ldbtob(bp->b_blkno);
1480 1481 if (iolen > bp->b_bcount)
1481 1482 iolen = bp->b_bcount;
1482 1483
1483 1484 /* if connected */
1484 1485 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
1485 1486 /* write to the back file first */
1486 1487 error = cachefs_push_connected(vp, bp, iolen, iooff, cr);
1487 1488
1488 1489 /* write to the front file if allowed */
1489 1490 if ((error == 0) && CFS_ISFS_NONSHARED(fscp) &&
1490 1491 ((cp->c_flags & CN_NOCACHE) == 0)) {
1491 1492 /* try to write to the front file */
1492 1493 (void) cachefs_push_front(vp, bp, iolen, iooff, cr);
1493 1494 }
1494 1495 }
1495 1496
1496 1497 /* else if disconnected */
1497 1498 else {
1498 1499 /* try to write to the front file */
1499 1500 error = cachefs_push_front(vp, bp, iolen, iooff, cr);
1500 1501 }
1501 1502
1502 1503 bp_mapout(bp);
1503 1504 pageio_done(bp);
1504 1505
1505 1506 writedone:
1506 1507
1507 1508 pvn_write_done(pp, ((error) ? B_ERROR : 0) | B_WRITE | flags);
1508 1509 if (offp)
1509 1510 *offp = iooff;
1510 1511 if (lenp)
1511 1512 *lenp = iolen;
1512 1513
1513 1514 /* XXX ask bob mastors how to fix this someday */
1514 1515 mutex_enter(&cp->c_statelock);
1515 1516 if (error) {
1516 1517 if (error == ENOSPC) {
1517 1518 if ((fscp->fs_cdconnected == CFS_CD_CONNECTED) ||
1518 1519 CFS_ISFS_SOFT(fscp)) {
1519 1520 CFSOP_INVALIDATE_COBJECT(fscp, cp, cr);
1520 1521 cp->c_error = error;
1521 1522 }
1522 1523 } else if ((CFS_TIMEOUT(fscp, error) == 0) &&
1523 1524 (error != EINTR)) {
1524 1525 CFSOP_INVALIDATE_COBJECT(fscp, cp, cr);
1525 1526 cp->c_error = error;
1526 1527 }
1527 1528 } else if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
1528 1529 CFSOP_MODIFY_COBJECT(fscp, cp, cr);
1529 1530 }
1530 1531 mutex_exit(&cp->c_statelock);
1531 1532
1532 1533 return (error);
1533 1534 }
1534 1535
1535 1536 /*
1536 1537 * Pushes out pages to the back file system.
1537 1538 */
1538 1539 static int
1539 1540 cachefs_push_connected(vnode_t *vp, struct buf *bp, size_t iolen,
1540 1541 u_offset_t iooff, cred_t *cr)
1541 1542 {
1542 1543 struct cnode *cp = VTOC(vp);
1543 1544 int error = 0;
1544 1545 int mode = 0;
1545 1546 fscache_t *fscp = C_TO_FSCACHE(cp);
1546 1547 ssize_t resid;
1547 1548 vnode_t *backvp;
1548 1549
1549 1550 /* get the back file if necessary */
1550 1551 mutex_enter(&cp->c_statelock);
1551 1552 if (cp->c_backvp == NULL) {
1552 1553 error = cachefs_getbackvp(fscp, cp);
1553 1554 if (error) {
1554 1555 mutex_exit(&cp->c_statelock);
1555 1556 goto out;
1556 1557 }
1557 1558 }
1558 1559 backvp = cp->c_backvp;
1559 1560 VN_HOLD(backvp);
1560 1561 mutex_exit(&cp->c_statelock);
1561 1562
1562 1563 if (CFS_ISFS_NONSHARED(fscp) && CFS_ISFS_SNR(fscp))
1563 1564 mode = FSYNC;
1564 1565
1565 1566 /* write to the back file */
1566 1567 error = bp->b_error = vn_rdwr(UIO_WRITE, backvp, bp->b_un.b_addr,
1567 1568 iolen, iooff, UIO_SYSSPACE, mode,
1568 1569 RLIM64_INFINITY, cr, &resid);
1569 1570 if (error) {
1570 1571 #ifdef CFSDEBUG
1571 1572 CFS_DEBUG(CFSDEBUG_VOPS | CFSDEBUG_BACK)
1572 1573 printf("cachefspush: error %d cr %p\n",
1573 1574 error, (void *)cr);
1574 1575 #endif
1575 1576 bp->b_flags |= B_ERROR;
1576 1577 }
1577 1578 VN_RELE(backvp);
1578 1579 out:
1579 1580 return (error);
1580 1581 }
1581 1582
1582 1583 /*
1583 1584 * Pushes out pages to the front file system.
1584 1585 * Called for both connected and disconnected states.
1585 1586 */
1586 1587 static int
1587 1588 cachefs_push_front(vnode_t *vp, struct buf *bp, size_t iolen,
1588 1589 u_offset_t iooff, cred_t *cr)
1589 1590 {
1590 1591 struct cnode *cp = VTOC(vp);
1591 1592 fscache_t *fscp = C_TO_FSCACHE(cp);
1592 1593 int error = 0;
1593 1594 ssize_t resid;
1594 1595 u_offset_t popoff;
1595 1596 off_t commit = 0;
1596 1597 uint_t seq;
1597 1598 enum cachefs_rl_type type;
1598 1599 vnode_t *frontvp = NULL;
1599 1600
1600 1601 mutex_enter(&cp->c_statelock);
1601 1602
1602 1603 if (!CFS_ISFS_NONSHARED(fscp)) {
1603 1604 error = ETIMEDOUT;
1604 1605 goto out;
1605 1606 }
1606 1607
1607 1608 /* get the front file if necessary */
1608 1609 if ((cp->c_frontvp == NULL) &&
1609 1610 ((cp->c_flags & CN_NOCACHE) == 0)) {
1610 1611 (void) cachefs_getfrontfile(cp);
1611 1612 }
1612 1613 if (cp->c_flags & CN_NOCACHE) {
1613 1614 error = ETIMEDOUT;
1614 1615 goto out;
1615 1616 }
1616 1617
1617 1618 /* if disconnected, needs to be populated and have good attributes */
1618 1619 if ((fscp->fs_cdconnected != CFS_CD_CONNECTED) &&
1619 1620 (((cp->c_metadata.md_flags & MD_POPULATED) == 0) ||
1620 1621 (cp->c_metadata.md_flags & MD_NEEDATTRS))) {
1621 1622 error = ETIMEDOUT;
1622 1623 goto out;
1623 1624 }
1624 1625
1625 1626 for (popoff = iooff; popoff < (iooff + iolen); popoff += MAXBSIZE) {
1626 1627 if (cachefs_charge_page(cp, popoff)) {
1627 1628 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
1628 1629 cachefs_nocache(cp);
1629 1630 goto out;
1630 1631 } else {
1631 1632 error = ENOSPC;
1632 1633 goto out;
1633 1634 }
1634 1635 }
1635 1636 }
1636 1637
1637 1638 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
1638 1639 /* log the first putpage to a file */
1639 1640 if ((cp->c_metadata.md_flags & MD_PUTPAGE) == 0) {
1640 1641 /* uses open's creds if we have them */
1641 1642 if (cp->c_cred)
1642 1643 cr = cp->c_cred;
1643 1644
1644 1645 if ((cp->c_metadata.md_flags & MD_MAPPING) == 0) {
1645 1646 error = cachefs_dlog_cidmap(fscp);
1646 1647 if (error) {
1647 1648 error = ENOSPC;
1648 1649 goto out;
1649 1650 }
1650 1651 cp->c_metadata.md_flags |= MD_MAPPING;
1651 1652 }
1652 1653
1653 1654 commit = cachefs_dlog_modify(fscp, cp, cr, &seq);
1654 1655 if (commit == 0) {
1655 1656 /* out of space */
1656 1657 error = ENOSPC;
1657 1658 goto out;
1658 1659 }
1659 1660
1660 1661 cp->c_metadata.md_seq = seq;
1661 1662 type = cp->c_metadata.md_rltype;
1662 1663 cachefs_modified(cp);
1663 1664 cp->c_metadata.md_flags |= MD_PUTPAGE;
1664 1665 cp->c_metadata.md_flags &= ~MD_PUSHDONE;
1665 1666 cp->c_flags |= CN_UPDATED;
1666 1667 }
1667 1668
1668 1669 /* subsequent putpages just get a new sequence number */
1669 1670 else {
1670 1671 /* but only if it matters */
1671 1672 if (cp->c_metadata.md_seq != fscp->fs_dlogseq) {
1672 1673 seq = cachefs_dlog_seqnext(fscp);
1673 1674 if (seq == 0) {
1674 1675 error = ENOSPC;
1675 1676 goto out;
1676 1677 }
1677 1678 cp->c_metadata.md_seq = seq;
1678 1679 cp->c_flags |= CN_UPDATED;
1679 1680 /* XXX maybe should do write_metadata here */
1680 1681 }
1681 1682 }
1682 1683 }
1683 1684
1684 1685 frontvp = cp->c_frontvp;
1685 1686 VN_HOLD(frontvp);
1686 1687 mutex_exit(&cp->c_statelock);
1687 1688 error = bp->b_error = vn_rdwr(UIO_WRITE, frontvp,
1688 1689 bp->b_un.b_addr, iolen, iooff, UIO_SYSSPACE, 0,
1689 1690 RLIM64_INFINITY, kcred, &resid);
1690 1691 mutex_enter(&cp->c_statelock);
1691 1692 VN_RELE(frontvp);
1692 1693 frontvp = NULL;
1693 1694 if (error) {
1694 1695 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
1695 1696 cachefs_nocache(cp);
1696 1697 error = 0;
1697 1698 goto out;
1698 1699 } else {
1699 1700 goto out;
1700 1701 }
1701 1702 }
1702 1703
1703 1704 (void) cachefs_update_allocmap(cp, iooff, iolen);
1704 1705 cp->c_flags |= (CN_UPDATED | CN_NEED_FRONT_SYNC |
1705 1706 CN_POPULATION_PENDING);
1706 1707 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
1707 1708 gethrestime(&cp->c_metadata.md_localmtime);
1708 1709 cp->c_metadata.md_flags |= MD_LOCALMTIME;
1709 1710 }
1710 1711
1711 1712 out:
1712 1713 if (commit) {
1713 1714 /* commit the log record */
1714 1715 ASSERT(fscp->fs_cdconnected == CFS_CD_DISCONNECTED);
1715 1716 if (cachefs_dlog_commit(fscp, commit, error)) {
1716 1717 /*EMPTY*/
1717 1718 /* XXX fix on panic */
1718 1719 }
1719 1720 }
1720 1721
1721 1722 if (error && commit) {
1722 1723 cp->c_metadata.md_flags &= ~MD_PUTPAGE;
1723 1724 cachefs_rlent_moveto(fscp->fs_cache, type,
1724 1725 cp->c_metadata.md_rlno, cp->c_metadata.md_frontblks);
1725 1726 cp->c_metadata.md_rltype = type;
1726 1727 cp->c_flags |= CN_UPDATED;
1727 1728 }
1728 1729 mutex_exit(&cp->c_statelock);
1729 1730 return (error);
1730 1731 }
1731 1732
1732 1733 /*ARGSUSED*/
1733 1734 static int
1734 1735 cachefs_dump(struct vnode *vp, caddr_t foo1, offset_t foo2, offset_t foo3,
1735 1736 caller_context_t *ct)
1736 1737 {
1737 1738 return (ENOSYS); /* should we panic if we get here? */
1738 1739 }
1739 1740
1740 1741 /*ARGSUSED*/
1741 1742 static int
1742 1743 cachefs_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, cred_t *cred,
1743 1744 int *rvalp, caller_context_t *ct)
1744 1745 {
1745 1746 int error;
1746 1747 struct cnode *cp = VTOC(vp);
1747 1748 struct fscache *fscp = C_TO_FSCACHE(cp);
1748 1749 struct cachefscache *cachep;
1749 1750 extern kmutex_t cachefs_cachelock;
1750 1751 extern cachefscache_t *cachefs_cachelist;
1751 1752 cachefsio_pack_t *packp;
1752 1753 STRUCT_DECL(cachefsio_dcmd, dcmd);
1753 1754 int inlen, outlen; /* LP64: generic int for struct in/out len */
1754 1755 void *dinp, *doutp;
1755 1756 int (*dcmd_routine)(vnode_t *, void *, void *);
1756 1757
1757 1758 if (getzoneid() != GLOBAL_ZONEID)
1758 1759 return (EPERM);
1759 1760
1760 1761 /*
1761 1762 * Cachefs only provides pass-through support for NFSv4,
1762 1763 * and all vnode operations are passed through to the
1763 1764 * back file system. For NFSv4 pass-through to work, only
1764 1765 * connected operation is supported, the cnode backvp must
1765 1766 * exist, and cachefs optional (eg., disconnectable) flags
1766 1767 * are turned off. Assert these conditions which ensure
1767 1768 * that only a subset of the ioctls are "truly supported"
1768 1769 * for NFSv4 (these are CFSDCMD_DAEMONID and CFSDCMD_GETSTATS.
1769 1770 * The packing operations are meaningless since there is
1770 1771 * no caching for NFSv4, and the called functions silently
1771 1772 * return if the backfilesystem is NFSv4. The daemon
1772 1773 * commands except for those above are essentially used
1773 1774 * for disconnectable operation support (including log
1774 1775 * rolling), so in each called function, we assert that
1775 1776 * NFSv4 is not in use. The _FIO* calls (except _FIOCOD)
1776 1777 * are from "cfsfstype" which is not a documented
1777 1778 * command. However, the command is visible in
1778 1779 * /usr/lib/fs/cachefs so the commands are simply let
1779 1780 * through (don't seem to impact pass-through functionality).
1780 1781 */
1781 1782 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
1782 1783 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
1783 1784
1784 1785 switch (cmd) {
1785 1786 case CACHEFSIO_PACK:
1786 1787 packp = cachefs_kmem_alloc(sizeof (cachefsio_pack_t), KM_SLEEP);
1787 1788 error = xcopyin((void *)arg, packp, sizeof (cachefsio_pack_t));
1788 1789 if (!error)
1789 1790 error = cachefs_pack(vp, packp->p_name, cred);
1790 1791 cachefs_kmem_free(packp, sizeof (cachefsio_pack_t));
1791 1792 break;
1792 1793
1793 1794 case CACHEFSIO_UNPACK:
1794 1795 packp = cachefs_kmem_alloc(sizeof (cachefsio_pack_t), KM_SLEEP);
1795 1796 error = xcopyin((void *)arg, packp, sizeof (cachefsio_pack_t));
1796 1797 if (!error)
1797 1798 error = cachefs_unpack(vp, packp->p_name, cred);
1798 1799 cachefs_kmem_free(packp, sizeof (cachefsio_pack_t));
1799 1800 break;
1800 1801
1801 1802 case CACHEFSIO_PACKINFO:
1802 1803 packp = cachefs_kmem_alloc(sizeof (cachefsio_pack_t), KM_SLEEP);
1803 1804 error = xcopyin((void *)arg, packp, sizeof (cachefsio_pack_t));
1804 1805 if (!error)
1805 1806 error = cachefs_packinfo(vp, packp->p_name,
1806 1807 &packp->p_status, cred);
1807 1808 if (!error)
1808 1809 error = xcopyout(packp, (void *)arg,
1809 1810 sizeof (cachefsio_pack_t));
1810 1811 cachefs_kmem_free(packp, sizeof (cachefsio_pack_t));
1811 1812 break;
1812 1813
1813 1814 case CACHEFSIO_UNPACKALL:
1814 1815 error = cachefs_unpackall(vp);
1815 1816 break;
1816 1817
1817 1818 case CACHEFSIO_DCMD:
1818 1819 /*
1819 1820 * This is a private interface between the cachefsd and
1820 1821 * this file system.
1821 1822 */
1822 1823
1823 1824 /* must be root to use these commands */
1824 1825 if (secpolicy_fs_config(cred, vp->v_vfsp) != 0)
1825 1826 return (EPERM);
1826 1827
1827 1828 /* get the command packet */
1828 1829 STRUCT_INIT(dcmd, flag & DATAMODEL_MASK);
1829 1830 error = xcopyin((void *)arg, STRUCT_BUF(dcmd),
1830 1831 SIZEOF_STRUCT(cachefsio_dcmd, DATAMODEL_NATIVE));
1831 1832 if (error)
1832 1833 return (error);
1833 1834
1834 1835 /* copy in the data for the operation */
1835 1836 dinp = NULL;
1836 1837 if ((inlen = STRUCT_FGET(dcmd, d_slen)) > 0) {
1837 1838 dinp = cachefs_kmem_alloc(inlen, KM_SLEEP);
1838 1839 error = xcopyin(STRUCT_FGETP(dcmd, d_sdata), dinp,
1839 1840 inlen);
1840 1841 if (error)
1841 1842 return (error);
1842 1843 }
1843 1844
1844 1845 /* allocate space for the result */
1845 1846 doutp = NULL;
1846 1847 if ((outlen = STRUCT_FGET(dcmd, d_rlen)) > 0)
1847 1848 doutp = cachefs_kmem_alloc(outlen, KM_SLEEP);
1848 1849
1849 1850 /*
1850 1851 * Assert NFSv4 only allows the daemonid and getstats
1851 1852 * daemon requests
1852 1853 */
1853 1854 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0 ||
1854 1855 STRUCT_FGET(dcmd, d_cmd) == CFSDCMD_DAEMONID ||
1855 1856 STRUCT_FGET(dcmd, d_cmd) == CFSDCMD_GETSTATS);
1856 1857
1857 1858 /* get the routine to execute */
1858 1859 dcmd_routine = NULL;
1859 1860 switch (STRUCT_FGET(dcmd, d_cmd)) {
1860 1861 case CFSDCMD_DAEMONID:
1861 1862 dcmd_routine = cachefs_io_daemonid;
1862 1863 break;
1863 1864 case CFSDCMD_STATEGET:
1864 1865 dcmd_routine = cachefs_io_stateget;
1865 1866 break;
1866 1867 case CFSDCMD_STATESET:
1867 1868 dcmd_routine = cachefs_io_stateset;
1868 1869 break;
1869 1870 case CFSDCMD_XWAIT:
1870 1871 dcmd_routine = cachefs_io_xwait;
1871 1872 break;
1872 1873 case CFSDCMD_EXISTS:
1873 1874 dcmd_routine = cachefs_io_exists;
1874 1875 break;
1875 1876 case CFSDCMD_LOSTFOUND:
1876 1877 dcmd_routine = cachefs_io_lostfound;
1877 1878 break;
1878 1879 case CFSDCMD_GETINFO:
1879 1880 dcmd_routine = cachefs_io_getinfo;
1880 1881 break;
1881 1882 case CFSDCMD_CIDTOFID:
1882 1883 dcmd_routine = cachefs_io_cidtofid;
1883 1884 break;
1884 1885 case CFSDCMD_GETATTRFID:
1885 1886 dcmd_routine = cachefs_io_getattrfid;
1886 1887 break;
1887 1888 case CFSDCMD_GETATTRNAME:
1888 1889 dcmd_routine = cachefs_io_getattrname;
1889 1890 break;
1890 1891 case CFSDCMD_GETSTATS:
1891 1892 dcmd_routine = cachefs_io_getstats;
1892 1893 break;
1893 1894 case CFSDCMD_ROOTFID:
1894 1895 dcmd_routine = cachefs_io_rootfid;
1895 1896 break;
1896 1897 case CFSDCMD_CREATE:
1897 1898 dcmd_routine = cachefs_io_create;
1898 1899 break;
1899 1900 case CFSDCMD_REMOVE:
1900 1901 dcmd_routine = cachefs_io_remove;
1901 1902 break;
1902 1903 case CFSDCMD_LINK:
1903 1904 dcmd_routine = cachefs_io_link;
1904 1905 break;
1905 1906 case CFSDCMD_RENAME:
1906 1907 dcmd_routine = cachefs_io_rename;
1907 1908 break;
1908 1909 case CFSDCMD_MKDIR:
1909 1910 dcmd_routine = cachefs_io_mkdir;
1910 1911 break;
1911 1912 case CFSDCMD_RMDIR:
1912 1913 dcmd_routine = cachefs_io_rmdir;
1913 1914 break;
1914 1915 case CFSDCMD_SYMLINK:
1915 1916 dcmd_routine = cachefs_io_symlink;
1916 1917 break;
1917 1918 case CFSDCMD_SETATTR:
1918 1919 dcmd_routine = cachefs_io_setattr;
1919 1920 break;
1920 1921 case CFSDCMD_SETSECATTR:
1921 1922 dcmd_routine = cachefs_io_setsecattr;
1922 1923 break;
1923 1924 case CFSDCMD_PUSHBACK:
1924 1925 dcmd_routine = cachefs_io_pushback;
1925 1926 break;
1926 1927 default:
1927 1928 error = ENOTTY;
1928 1929 break;
1929 1930 }
1930 1931
1931 1932 /* execute the routine */
1932 1933 if (dcmd_routine)
1933 1934 error = (*dcmd_routine)(vp, dinp, doutp);
1934 1935
1935 1936 /* copy out the result */
1936 1937 if ((error == 0) && doutp)
1937 1938 error = xcopyout(doutp, STRUCT_FGETP(dcmd, d_rdata),
1938 1939 outlen);
1939 1940
1940 1941 /* free allocated memory */
1941 1942 if (dinp)
1942 1943 cachefs_kmem_free(dinp, inlen);
1943 1944 if (doutp)
1944 1945 cachefs_kmem_free(doutp, outlen);
1945 1946
1946 1947 break;
1947 1948
1948 1949 case _FIOCOD:
1949 1950 if (secpolicy_fs_config(cred, vp->v_vfsp) != 0) {
1950 1951 error = EPERM;
1951 1952 break;
1952 1953 }
1953 1954
1954 1955 error = EBUSY;
1955 1956 if (arg) {
1956 1957 /* non-zero arg means do all filesystems */
1957 1958 mutex_enter(&cachefs_cachelock);
1958 1959 for (cachep = cachefs_cachelist; cachep != NULL;
1959 1960 cachep = cachep->c_next) {
1960 1961 mutex_enter(&cachep->c_fslistlock);
1961 1962 for (fscp = cachep->c_fslist;
1962 1963 fscp != NULL;
1963 1964 fscp = fscp->fs_next) {
1964 1965 if (CFS_ISFS_CODCONST(fscp)) {
1965 1966 gethrestime(&fscp->fs_cod_time);
1966 1967 error = 0;
1967 1968 }
1968 1969 }
1969 1970 mutex_exit(&cachep->c_fslistlock);
1970 1971 }
1971 1972 mutex_exit(&cachefs_cachelock);
1972 1973 } else {
1973 1974 if (CFS_ISFS_CODCONST(fscp)) {
1974 1975 gethrestime(&fscp->fs_cod_time);
1975 1976 error = 0;
1976 1977 }
1977 1978 }
1978 1979 break;
1979 1980
1980 1981 case _FIOSTOPCACHE:
1981 1982 error = cachefs_stop_cache(cp);
1982 1983 break;
1983 1984
1984 1985 default:
1985 1986 error = ENOTTY;
1986 1987 break;
1987 1988 }
1988 1989
1989 1990 /* return the result */
1990 1991 return (error);
1991 1992 }
1992 1993
1993 1994 ino64_t
1994 1995 cachefs_fileno_conflict(fscache_t *fscp, ino64_t old)
1995 1996 {
1996 1997 ino64_t new;
1997 1998
1998 1999 ASSERT(MUTEX_HELD(&fscp->fs_fslock));
1999 2000
2000 2001 for (;;) {
2001 2002 fscp->fs_info.fi_localfileno++;
2002 2003 if (fscp->fs_info.fi_localfileno == 0)
2003 2004 fscp->fs_info.fi_localfileno = 3;
2004 2005 fscp->fs_flags |= CFS_FS_DIRTYINFO;
2005 2006
2006 2007 new = fscp->fs_info.fi_localfileno;
2007 2008 if (! cachefs_fileno_inuse(fscp, new))
2008 2009 break;
2009 2010 }
2010 2011
2011 2012 cachefs_inum_register(fscp, old, new);
2012 2013 cachefs_inum_register(fscp, new, 0);
2013 2014 return (new);
2014 2015 }
2015 2016
2016 2017 /*ARGSUSED*/
2017 2018 static int
2018 2019 cachefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
2019 2020 caller_context_t *ct)
2020 2021 {
2021 2022 struct cnode *cp = VTOC(vp);
2022 2023 fscache_t *fscp = C_TO_FSCACHE(cp);
2023 2024 int error = 0;
2024 2025 int held = 0;
2025 2026 int connected = 0;
2026 2027
2027 2028 #ifdef CFSDEBUG
2028 2029 CFS_DEBUG(CFSDEBUG_VOPS)
2029 2030 printf("cachefs_getattr: ENTER vp %p\n", (void *)vp);
2030 2031 #endif
2031 2032
2032 2033 if (getzoneid() != GLOBAL_ZONEID)
2033 2034 return (EPERM);
2034 2035
2035 2036 /* Call backfilesystem getattr if NFSv4 */
2036 2037 if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
2037 2038 error = cachefs_getattr_backfs_nfsv4(vp, vap, flags, cr, ct);
2038 2039 goto out;
2039 2040 }
2040 2041
2041 2042 /*
2042 2043 * If it has been specified that the return value will
2043 2044 * just be used as a hint, and we are only being asked
2044 2045 * for size, fsid or rdevid, then return the client's
2045 2046 * notion of these values without checking to make sure
2046 2047 * that the attribute cache is up to date.
2047 2048 * The whole point is to avoid an over the wire GETATTR
2048 2049 * call.
2049 2050 */
2050 2051 if (flags & ATTR_HINT) {
2051 2052 if (vap->va_mask ==
2052 2053 (vap->va_mask & (AT_SIZE | AT_FSID | AT_RDEV))) {
2053 2054 if (vap->va_mask | AT_SIZE)
2054 2055 vap->va_size = cp->c_size;
2055 2056 /*
2056 2057 * Return the FSID of the cachefs filesystem,
2057 2058 * not the back filesystem
2058 2059 */
2059 2060 if (vap->va_mask | AT_FSID)
2060 2061 vap->va_fsid = vp->v_vfsp->vfs_dev;
2061 2062 if (vap->va_mask | AT_RDEV)
2062 2063 vap->va_rdev = cp->c_attr.va_rdev;
2063 2064 return (0);
2064 2065 }
2065 2066 }
2066 2067
2067 2068 /*
2068 2069 * Only need to flush pages if asking for the mtime
2069 2070 * and if there any dirty pages.
2070 2071 */
2071 2072 if (vap->va_mask & AT_MTIME) {
2072 2073 /*EMPTY*/
2073 2074 #if 0
2074 2075 /*
2075 2076 * XXX bob: stolen from nfs code, need to do something similar
2076 2077 */
2077 2078 rp = VTOR(vp);
2078 2079 if ((rp->r_flags & RDIRTY) || rp->r_iocnt > 0)
2079 2080 (void) nfs3_putpage(vp, (offset_t)0, 0, 0, cr);
2080 2081 #endif
2081 2082 }
2082 2083
2083 2084 for (;;) {
2084 2085 /* get (or renew) access to the file system */
2085 2086 if (held) {
2086 2087 cachefs_cd_release(fscp);
2087 2088 held = 0;
2088 2089 }
2089 2090 error = cachefs_cd_access(fscp, connected, 0);
2090 2091 if (error)
2091 2092 goto out;
2092 2093 held = 1;
2093 2094
2094 2095 /*
2095 2096 * If it has been specified that the return value will
2096 2097 * just be used as a hint, and we are only being asked
2097 2098 * for size, fsid or rdevid, then return the client's
2098 2099 * notion of these values without checking to make sure
2099 2100 * that the attribute cache is up to date.
2100 2101 * The whole point is to avoid an over the wire GETATTR
2101 2102 * call.
2102 2103 */
2103 2104 if (flags & ATTR_HINT) {
2104 2105 if (vap->va_mask ==
2105 2106 (vap->va_mask & (AT_SIZE | AT_FSID | AT_RDEV))) {
2106 2107 if (vap->va_mask | AT_SIZE)
2107 2108 vap->va_size = cp->c_size;
2108 2109 /*
2109 2110 * Return the FSID of the cachefs filesystem,
2110 2111 * not the back filesystem
2111 2112 */
2112 2113 if (vap->va_mask | AT_FSID)
2113 2114 vap->va_fsid = vp->v_vfsp->vfs_dev;
2114 2115 if (vap->va_mask | AT_RDEV)
2115 2116 vap->va_rdev = cp->c_attr.va_rdev;
2116 2117 goto out;
2117 2118 }
2118 2119 }
2119 2120
2120 2121 mutex_enter(&cp->c_statelock);
2121 2122 if ((cp->c_metadata.md_flags & MD_NEEDATTRS) &&
2122 2123 (fscp->fs_cdconnected != CFS_CD_CONNECTED)) {
2123 2124 mutex_exit(&cp->c_statelock);
2124 2125 connected = 1;
2125 2126 continue;
2126 2127 }
2127 2128
2128 2129 error = CFSOP_CHECK_COBJECT(fscp, cp, 0, cr);
2129 2130 if (CFS_TIMEOUT(fscp, error)) {
2130 2131 mutex_exit(&cp->c_statelock);
2131 2132 cachefs_cd_release(fscp);
2132 2133 held = 0;
2133 2134 cachefs_cd_timedout(fscp);
2134 2135 continue;
2135 2136 }
2136 2137 if (error) {
2137 2138 mutex_exit(&cp->c_statelock);
2138 2139 break;
2139 2140 }
2140 2141
2141 2142 /* check for fileno conflict */
2142 2143 if ((fscp->fs_inum_size > 0) &&
2143 2144 ((cp->c_metadata.md_flags & MD_LOCALFILENO) == 0)) {
2144 2145 ino64_t fakenum;
2145 2146
2146 2147 mutex_exit(&cp->c_statelock);
2147 2148 mutex_enter(&fscp->fs_fslock);
2148 2149 fakenum = cachefs_inum_real2fake(fscp,
2149 2150 cp->c_attr.va_nodeid);
2150 2151 if (fakenum == 0) {
2151 2152 fakenum = cachefs_fileno_conflict(fscp,
2152 2153 cp->c_attr.va_nodeid);
2153 2154 }
2154 2155 mutex_exit(&fscp->fs_fslock);
2155 2156
2156 2157 mutex_enter(&cp->c_statelock);
2157 2158 cp->c_metadata.md_flags |= MD_LOCALFILENO;
2158 2159 cp->c_metadata.md_localfileno = fakenum;
2159 2160 cp->c_flags |= CN_UPDATED;
2160 2161 }
2161 2162
2162 2163 /* copy out the attributes */
2163 2164 *vap = cp->c_attr;
2164 2165
2165 2166 /*
2166 2167 * return the FSID of the cachefs filesystem,
2167 2168 * not the back filesystem
2168 2169 */
2169 2170 vap->va_fsid = vp->v_vfsp->vfs_dev;
2170 2171
2171 2172 /* return our idea of the size */
2172 2173 if (cp->c_size > vap->va_size)
2173 2174 vap->va_size = cp->c_size;
2174 2175
2175 2176 /* overwrite with our version of fileno and timestamps */
2176 2177 vap->va_nodeid = cp->c_metadata.md_localfileno;
2177 2178 vap->va_mtime = cp->c_metadata.md_localmtime;
2178 2179 vap->va_ctime = cp->c_metadata.md_localctime;
2179 2180
2180 2181 mutex_exit(&cp->c_statelock);
2181 2182 break;
2182 2183 }
2183 2184 out:
2184 2185 if (held)
2185 2186 cachefs_cd_release(fscp);
2186 2187 #ifdef CFS_CD_DEBUG
2187 2188 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
2188 2189 #endif
2189 2190
2190 2191 #ifdef CFSDEBUG
2191 2192 CFS_DEBUG(CFSDEBUG_VOPS)
2192 2193 printf("cachefs_getattr: EXIT error = %d\n", error);
2193 2194 #endif
2194 2195 return (error);
2195 2196 }
2196 2197
2197 2198 /*
2198 2199 * cachefs_getattr_backfs_nfsv4
2199 2200 *
2200 2201 * Call NFSv4 back filesystem to handle the getattr (cachefs
2201 2202 * pass-through support for NFSv4).
2202 2203 */
2203 2204 static int
2204 2205 cachefs_getattr_backfs_nfsv4(vnode_t *vp, vattr_t *vap,
2205 2206 int flags, cred_t *cr, caller_context_t *ct)
2206 2207 {
2207 2208 cnode_t *cp = VTOC(vp);
2208 2209 fscache_t *fscp = C_TO_FSCACHE(cp);
2209 2210 vnode_t *backvp;
2210 2211 int error;
2211 2212
2212 2213 /*
2213 2214 * For NFSv4 pass-through to work, only connected operation
2214 2215 * is supported, the cnode backvp must exist, and cachefs
2215 2216 * optional (eg., disconnectable) flags are turned off. Assert
2216 2217 * these conditions for the getattr operation.
2217 2218 */
2218 2219 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
2219 2220 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
2220 2221
2221 2222 /* Call backfs vnode op after extracting backvp */
2222 2223 mutex_enter(&cp->c_statelock);
2223 2224 backvp = cp->c_backvp;
2224 2225 mutex_exit(&cp->c_statelock);
2225 2226
2226 2227 CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_getattr_backfs_nfsv4: cnode %p,"
2227 2228 " backvp %p\n", cp, backvp));
2228 2229 error = VOP_GETATTR(backvp, vap, flags, cr, ct);
2229 2230
2230 2231 /* Update attributes */
2231 2232 cp->c_attr = *vap;
2232 2233
2233 2234 /*
2234 2235 * return the FSID of the cachefs filesystem,
2235 2236 * not the back filesystem
2236 2237 */
2237 2238 vap->va_fsid = vp->v_vfsp->vfs_dev;
2238 2239
2239 2240 return (error);
2240 2241 }
2241 2242
2242 2243 /*ARGSUSED4*/
2243 2244 static int
2244 2245 cachefs_setattr(
2245 2246 vnode_t *vp,
2246 2247 vattr_t *vap,
2247 2248 int flags,
2248 2249 cred_t *cr,
2249 2250 caller_context_t *ct)
2250 2251 {
2251 2252 cnode_t *cp = VTOC(vp);
2252 2253 fscache_t *fscp = C_TO_FSCACHE(cp);
2253 2254 int error;
2254 2255 int connected;
2255 2256 int held = 0;
2256 2257
2257 2258 if (getzoneid() != GLOBAL_ZONEID)
2258 2259 return (EPERM);
2259 2260
2260 2261 /*
2261 2262 * Cachefs only provides pass-through support for NFSv4,
2262 2263 * and all vnode operations are passed through to the
2263 2264 * back file system. For NFSv4 pass-through to work, only
2264 2265 * connected operation is supported, the cnode backvp must
2265 2266 * exist, and cachefs optional (eg., disconnectable) flags
2266 2267 * are turned off. Assert these conditions to ensure that
2267 2268 * the backfilesystem is called for the setattr operation.
2268 2269 */
2269 2270 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
2270 2271 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
2271 2272
2272 2273 connected = 0;
2273 2274 for (;;) {
2274 2275 /* drop hold on file system */
2275 2276 if (held) {
2276 2277 /* Won't loop with NFSv4 connected behavior */
2277 2278 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
2278 2279 cachefs_cd_release(fscp);
2279 2280 held = 0;
2280 2281 }
2281 2282
2282 2283 /* acquire access to the file system */
2283 2284 error = cachefs_cd_access(fscp, connected, 1);
2284 2285 if (error)
2285 2286 break;
2286 2287 held = 1;
2287 2288
2288 2289 /* perform the setattr */
2289 2290 error = cachefs_setattr_common(vp, vap, flags, cr, ct);
2290 2291 if (error) {
2291 2292 /* if connected */
2292 2293 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
2293 2294 if (CFS_TIMEOUT(fscp, error)) {
2294 2295 cachefs_cd_release(fscp);
2295 2296 held = 0;
2296 2297 cachefs_cd_timedout(fscp);
2297 2298 connected = 0;
2298 2299 continue;
2299 2300 }
2300 2301 }
2301 2302
2302 2303 /* else must be disconnected */
2303 2304 else {
2304 2305 if (CFS_TIMEOUT(fscp, error)) {
2305 2306 connected = 1;
2306 2307 continue;
2307 2308 }
2308 2309 }
2309 2310 }
2310 2311 break;
2311 2312 }
2312 2313
2313 2314 if (held) {
2314 2315 cachefs_cd_release(fscp);
2315 2316 }
2316 2317 #ifdef CFS_CD_DEBUG
2317 2318 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
2318 2319 #endif
2319 2320 return (error);
2320 2321 }
2321 2322
2322 2323 static int
2323 2324 cachefs_setattr_common(
2324 2325 vnode_t *vp,
2325 2326 vattr_t *vap,
2326 2327 int flags,
2327 2328 cred_t *cr,
2328 2329 caller_context_t *ct)
2329 2330 {
2330 2331 cnode_t *cp = VTOC(vp);
2331 2332 fscache_t *fscp = C_TO_FSCACHE(cp);
2332 2333 cachefscache_t *cachep = fscp->fs_cache;
2333 2334 uint_t mask = vap->va_mask;
2334 2335 int error = 0;
2335 2336 uint_t bcnt;
2336 2337
2337 2338 /* Cannot set these attributes. */
2338 2339 if (mask & AT_NOSET)
2339 2340 return (EINVAL);
2340 2341
2341 2342 /*
2342 2343 * Truncate file. Must have write permission and not be a directory.
2343 2344 */
2344 2345 if (mask & AT_SIZE) {
2345 2346 if (vp->v_type == VDIR) {
2346 2347 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_TRUNCATE))
2347 2348 cachefs_log_truncate(cachep, EISDIR,
2348 2349 fscp->fs_cfsvfsp,
2349 2350 &cp->c_metadata.md_cookie,
2350 2351 cp->c_id.cid_fileno,
2351 2352 crgetuid(cr), vap->va_size);
2352 2353 return (EISDIR);
2353 2354 }
2354 2355 }
2355 2356
2356 2357 /*
2357 2358 * Gotta deal with one special case here, where we're setting the
2358 2359 * size of the file. First, we zero out part of the page after the
2359 2360 * new size of the file. Then we toss (not write) all pages after
2360 2361 * page in which the new offset occurs. Note that the NULL passed
2361 2362 * in instead of a putapage() fn parameter is correct, since
2362 2363 * no dirty pages will be found (B_TRUNC | B_INVAL).
2363 2364 */
2364 2365
2365 2366 rw_enter(&cp->c_rwlock, RW_WRITER);
2366 2367
2367 2368 /* sync dirty pages */
2368 2369 if (!CFS_ISFS_BACKFS_NFSV4(fscp)) {
2369 2370 error = cachefs_putpage_common(vp, (offset_t)0, 0, 0, cr);
2370 2371 if (error == EINTR)
2371 2372 goto out;
2372 2373 }
2373 2374 error = 0;
2374 2375
2375 2376 /* if connected */
2376 2377 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
2377 2378 error = cachefs_setattr_connected(vp, vap, flags, cr, ct);
2378 2379 }
2379 2380 /* else must be disconnected */
2380 2381 else {
2381 2382 error = cachefs_setattr_disconnected(vp, vap, flags, cr, ct);
2382 2383 }
2383 2384 if (error)
2384 2385 goto out;
2385 2386
2386 2387 /*
2387 2388 * If the file size has been changed then
2388 2389 * toss whole pages beyond the end of the file and zero
2389 2390 * the portion of the last page that is beyond the end of the file.
2390 2391 */
2391 2392 if (mask & AT_SIZE && !CFS_ISFS_BACKFS_NFSV4(fscp)) {
2392 2393 bcnt = (uint_t)(cp->c_size & PAGEOFFSET);
2393 2394 if (bcnt)
2394 2395 pvn_vpzero(vp, cp->c_size, PAGESIZE - bcnt);
2395 2396 (void) pvn_vplist_dirty(vp, cp->c_size, cachefs_push,
2396 2397 B_TRUNC | B_INVAL, cr);
2397 2398 }
2398 2399
2399 2400 out:
2400 2401 rw_exit(&cp->c_rwlock);
2401 2402
2402 2403 if ((mask & AT_SIZE) &&
2403 2404 (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_TRUNCATE)))
2404 2405 cachefs_log_truncate(cachep, error, fscp->fs_cfsvfsp,
2405 2406 &cp->c_metadata.md_cookie, cp->c_id.cid_fileno,
2406 2407 crgetuid(cr), vap->va_size);
2407 2408
2408 2409 return (error);
2409 2410 }
2410 2411
2411 2412 static int
2412 2413 cachefs_setattr_connected(
2413 2414 vnode_t *vp,
2414 2415 vattr_t *vap,
2415 2416 int flags,
2416 2417 cred_t *cr,
2417 2418 caller_context_t *ct)
2418 2419 {
2419 2420 cnode_t *cp = VTOC(vp);
2420 2421 fscache_t *fscp = C_TO_FSCACHE(cp);
2421 2422 uint_t mask = vap->va_mask;
2422 2423 int error = 0;
2423 2424 int setsize;
2424 2425
2425 2426 mutex_enter(&cp->c_statelock);
2426 2427
2427 2428 if (cp->c_backvp == NULL) {
2428 2429 error = cachefs_getbackvp(fscp, cp);
2429 2430 if (error)
2430 2431 goto out;
2431 2432 }
2432 2433
2433 2434 error = CFSOP_CHECK_COBJECT(fscp, cp, 0, cr);
2434 2435 if (error)
2435 2436 goto out;
2436 2437
2437 2438 CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_setattr (nfsv4): cnode %p, "
2438 2439 "backvp %p\n", cp, cp->c_backvp));
2439 2440 error = VOP_SETATTR(cp->c_backvp, vap, flags, cr, ct);
2440 2441 if (error) {
2441 2442 goto out;
2442 2443 }
2443 2444
2444 2445 /* if the size of the file is being changed */
2445 2446 if (mask & AT_SIZE) {
2446 2447 cp->c_size = vap->va_size;
2447 2448 error = 0;
2448 2449 setsize = 0;
2449 2450
2450 2451 /* see if okay to try to set the file size */
2451 2452 if (((cp->c_flags & CN_NOCACHE) == 0) &&
2452 2453 CFS_ISFS_NONSHARED(fscp)) {
2453 2454 /* okay to set size if file is populated */
2454 2455 if (cp->c_metadata.md_flags & MD_POPULATED)
2455 2456 setsize = 1;
2456 2457
2457 2458 /*
2458 2459 * Okay to set size if front file exists and setting
2459 2460 * file size to zero.
2460 2461 */
2461 2462 if ((cp->c_metadata.md_flags & MD_FILE) &&
2462 2463 (vap->va_size == 0))
2463 2464 setsize = 1;
2464 2465 }
2465 2466
2466 2467 /* if okay to try to set the file size */
2467 2468 if (setsize) {
2468 2469 error = 0;
2469 2470 if (cp->c_frontvp == NULL)
2470 2471 error = cachefs_getfrontfile(cp);
2471 2472 if (error == 0)
2472 2473 error = cachefs_frontfile_size(cp, cp->c_size);
2473 2474 } else if (cp->c_metadata.md_flags & MD_FILE) {
2474 2475 /* make sure file gets nocached */
2475 2476 error = EEXIST;
2476 2477 }
2477 2478
2478 2479 /* if we have to nocache the file */
2479 2480 if (error) {
2480 2481 if ((cp->c_flags & CN_NOCACHE) == 0 &&
2481 2482 !CFS_ISFS_BACKFS_NFSV4(fscp))
2482 2483 cachefs_nocache(cp);
2483 2484 error = 0;
2484 2485 }
2485 2486 }
2486 2487
2487 2488 cp->c_flags |= CN_UPDATED;
2488 2489
2489 2490 /* XXX bob: given what modify_cobject does this seems unnecessary */
2490 2491 cp->c_attr.va_mask = AT_ALL;
2491 2492 error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr, ct);
2492 2493 if (error)
2493 2494 goto out;
2494 2495
2495 2496 cp->c_attr.va_size = MAX(cp->c_attr.va_size, cp->c_size);
2496 2497 cp->c_size = cp->c_attr.va_size;
2497 2498
2498 2499 CFSOP_MODIFY_COBJECT(fscp, cp, cr);
2499 2500 out:
2500 2501 mutex_exit(&cp->c_statelock);
2501 2502 return (error);
2502 2503 }
2503 2504
2504 2505 /*
2505 2506 * perform the setattr on the local file system
2506 2507 */
2507 2508 /*ARGSUSED4*/
2508 2509 static int
2509 2510 cachefs_setattr_disconnected(
2510 2511 vnode_t *vp,
2511 2512 vattr_t *vap,
2512 2513 int flags,
2513 2514 cred_t *cr,
2514 2515 caller_context_t *ct)
2515 2516 {
2516 2517 cnode_t *cp = VTOC(vp);
2517 2518 fscache_t *fscp = C_TO_FSCACHE(cp);
2518 2519 int mask;
2519 2520 int error;
2520 2521 int newfile;
2521 2522 off_t commit = 0;
2522 2523
2523 2524 if (CFS_ISFS_WRITE_AROUND(fscp))
2524 2525 return (ETIMEDOUT);
2525 2526
2526 2527 /* if we do not have good attributes */
2527 2528 if (cp->c_metadata.md_flags & MD_NEEDATTRS)
2528 2529 return (ETIMEDOUT);
2529 2530
2530 2531 /* primary concern is to keep this routine as much like ufs_setattr */
2531 2532
2532 2533 mutex_enter(&cp->c_statelock);
2533 2534
2534 2535 error = secpolicy_vnode_setattr(cr, vp, vap, &cp->c_attr, flags,
2535 2536 cachefs_access_local, cp);
2536 2537
2537 2538 if (error)
2538 2539 goto out;
2539 2540
2540 2541 mask = vap->va_mask;
2541 2542
2542 2543 /* if changing the size of the file */
2543 2544 if (mask & AT_SIZE) {
2544 2545 if (vp->v_type == VDIR) {
2545 2546 error = EISDIR;
2546 2547 goto out;
2547 2548 }
2548 2549
2549 2550 if (vp->v_type == VFIFO) {
2550 2551 error = 0;
2551 2552 goto out;
2552 2553 }
2553 2554
2554 2555 if ((vp->v_type != VREG) &&
2555 2556 !((vp->v_type == VLNK) && (vap->va_size == 0))) {
2556 2557 error = EINVAL;
2557 2558 goto out;
2558 2559 }
2559 2560
2560 2561 if (vap->va_size > fscp->fs_offmax) {
2561 2562 error = EFBIG;
2562 2563 goto out;
2563 2564 }
2564 2565
2565 2566 /* if the file is not populated and we are not truncating it */
2566 2567 if (((cp->c_metadata.md_flags & MD_POPULATED) == 0) &&
2567 2568 (vap->va_size != 0)) {
2568 2569 error = ETIMEDOUT;
2569 2570 goto out;
2570 2571 }
2571 2572
2572 2573 if ((cp->c_metadata.md_flags & MD_MAPPING) == 0) {
2573 2574 error = cachefs_dlog_cidmap(fscp);
2574 2575 if (error) {
2575 2576 error = ENOSPC;
2576 2577 goto out;
2577 2578 }
2578 2579 cp->c_metadata.md_flags |= MD_MAPPING;
2579 2580 }
2580 2581
2581 2582 /* log the operation */
2582 2583 commit = cachefs_dlog_setattr(fscp, vap, flags, cp, cr);
2583 2584 if (commit == 0) {
2584 2585 error = ENOSPC;
2585 2586 goto out;
2586 2587 }
2587 2588 cp->c_flags &= ~CN_NOCACHE;
2588 2589
2589 2590 /* special case truncating fast sym links */
2590 2591 if ((vp->v_type == VLNK) &&
2591 2592 (cp->c_metadata.md_flags & MD_FASTSYMLNK)) {
2592 2593 /* XXX how can we get here */
2593 2594 /* XXX should update mtime */
2594 2595 cp->c_size = 0;
2595 2596 error = 0;
2596 2597 goto out;
2597 2598 }
2598 2599
2599 2600 /* get the front file, this may create one */
2600 2601 newfile = (cp->c_metadata.md_flags & MD_FILE) ? 0 : 1;
2601 2602 if (cp->c_frontvp == NULL) {
2602 2603 error = cachefs_getfrontfile(cp);
2603 2604 if (error)
2604 2605 goto out;
2605 2606 }
2606 2607 ASSERT(cp->c_frontvp);
2607 2608 if (newfile && (cp->c_flags & CN_UPDATED)) {
2608 2609 /* allocate space for the metadata */
2609 2610 ASSERT((cp->c_flags & CN_ALLOC_PENDING) == 0);
2610 2611 ASSERT((cp->c_filegrp->fg_flags & CFS_FG_ALLOC_ATTR)
2611 2612 == 0);
2612 2613 error = filegrp_write_metadata(cp->c_filegrp,
2613 2614 &cp->c_id, &cp->c_metadata);
2614 2615 if (error)
2615 2616 goto out;
2616 2617 }
2617 2618
2618 2619 /* change the size of the front file */
2619 2620 error = cachefs_frontfile_size(cp, vap->va_size);
2620 2621 if (error)
2621 2622 goto out;
2622 2623 cp->c_attr.va_size = cp->c_size = vap->va_size;
2623 2624 gethrestime(&cp->c_metadata.md_localmtime);
2624 2625 cp->c_metadata.md_flags |= MD_POPULATED | MD_LOCALMTIME;
2625 2626 cachefs_modified(cp);
2626 2627 cp->c_flags |= CN_UPDATED;
2627 2628 }
2628 2629
2629 2630 if (mask & AT_MODE) {
2630 2631 /* mark as modified */
2631 2632 if (cachefs_modified_alloc(cp)) {
2632 2633 error = ENOSPC;
2633 2634 goto out;
2634 2635 }
2635 2636
2636 2637 if ((cp->c_metadata.md_flags & MD_MAPPING) == 0) {
2637 2638 error = cachefs_dlog_cidmap(fscp);
2638 2639 if (error) {
2639 2640 error = ENOSPC;
2640 2641 goto out;
2641 2642 }
2642 2643 cp->c_metadata.md_flags |= MD_MAPPING;
2643 2644 }
2644 2645
2645 2646 /* log the operation if not already logged */
2646 2647 if (commit == 0) {
2647 2648 commit = cachefs_dlog_setattr(fscp, vap, flags, cp, cr);
2648 2649 if (commit == 0) {
2649 2650 error = ENOSPC;
2650 2651 goto out;
2651 2652 }
2652 2653 }
2653 2654
2654 2655 cp->c_attr.va_mode &= S_IFMT;
2655 2656 cp->c_attr.va_mode |= vap->va_mode & ~S_IFMT;
2656 2657 gethrestime(&cp->c_metadata.md_localctime);
2657 2658 cp->c_metadata.md_flags |= MD_LOCALCTIME;
2658 2659 cp->c_flags |= CN_UPDATED;
2659 2660 }
2660 2661
2661 2662 if (mask & (AT_UID|AT_GID)) {
2662 2663
2663 2664 /* mark as modified */
2664 2665 if (cachefs_modified_alloc(cp)) {
2665 2666 error = ENOSPC;
2666 2667 goto out;
2667 2668 }
2668 2669
2669 2670 if ((cp->c_metadata.md_flags & MD_MAPPING) == 0) {
2670 2671 error = cachefs_dlog_cidmap(fscp);
2671 2672 if (error) {
2672 2673 error = ENOSPC;
2673 2674 goto out;
2674 2675 }
2675 2676 cp->c_metadata.md_flags |= MD_MAPPING;
2676 2677 }
2677 2678
2678 2679 /* log the operation if not already logged */
2679 2680 if (commit == 0) {
2680 2681 commit = cachefs_dlog_setattr(fscp, vap, flags, cp, cr);
2681 2682 if (commit == 0) {
2682 2683 error = ENOSPC;
2683 2684 goto out;
2684 2685 }
2685 2686 }
2686 2687
2687 2688 if (mask & AT_UID)
2688 2689 cp->c_attr.va_uid = vap->va_uid;
2689 2690
2690 2691 if (mask & AT_GID)
2691 2692 cp->c_attr.va_gid = vap->va_gid;
2692 2693 gethrestime(&cp->c_metadata.md_localctime);
2693 2694 cp->c_metadata.md_flags |= MD_LOCALCTIME;
2694 2695 cp->c_flags |= CN_UPDATED;
2695 2696 }
2696 2697
2697 2698
2698 2699 if (mask & (AT_MTIME|AT_ATIME)) {
2699 2700 /* mark as modified */
2700 2701 if (cachefs_modified_alloc(cp)) {
2701 2702 error = ENOSPC;
2702 2703 goto out;
2703 2704 }
2704 2705
2705 2706 if ((cp->c_metadata.md_flags & MD_MAPPING) == 0) {
2706 2707 error = cachefs_dlog_cidmap(fscp);
2707 2708 if (error) {
2708 2709 error = ENOSPC;
2709 2710 goto out;
2710 2711 }
2711 2712 cp->c_metadata.md_flags |= MD_MAPPING;
2712 2713 }
2713 2714
2714 2715 /* log the operation if not already logged */
2715 2716 if (commit == 0) {
2716 2717 commit = cachefs_dlog_setattr(fscp, vap, flags, cp, cr);
2717 2718 if (commit == 0) {
2718 2719 error = ENOSPC;
2719 2720 goto out;
2720 2721 }
2721 2722 }
2722 2723
2723 2724 if (mask & AT_MTIME) {
2724 2725 cp->c_metadata.md_localmtime = vap->va_mtime;
2725 2726 cp->c_metadata.md_flags |= MD_LOCALMTIME;
2726 2727 }
2727 2728 if (mask & AT_ATIME)
2728 2729 cp->c_attr.va_atime = vap->va_atime;
2729 2730 gethrestime(&cp->c_metadata.md_localctime);
2730 2731 cp->c_metadata.md_flags |= MD_LOCALCTIME;
2731 2732 cp->c_flags |= CN_UPDATED;
2732 2733 }
2733 2734
2734 2735 out:
2735 2736 mutex_exit(&cp->c_statelock);
2736 2737
2737 2738 /* commit the log entry */
2738 2739 if (commit) {
2739 2740 if (cachefs_dlog_commit(fscp, commit, error)) {
2740 2741 /*EMPTY*/
2741 2742 /* XXX bob: fix on panic */
2742 2743 }
2743 2744 }
2744 2745 return (error);
2745 2746 }
2746 2747
2747 2748 /* ARGSUSED */
2748 2749 static int
2749 2750 cachefs_access(vnode_t *vp, int mode, int flags, cred_t *cr,
2750 2751 caller_context_t *ct)
2751 2752 {
2752 2753 cnode_t *cp = VTOC(vp);
2753 2754 fscache_t *fscp = C_TO_FSCACHE(cp);
2754 2755 int error;
2755 2756 int held = 0;
2756 2757 int connected = 0;
2757 2758
2758 2759 #ifdef CFSDEBUG
2759 2760 CFS_DEBUG(CFSDEBUG_VOPS)
2760 2761 printf("cachefs_access: ENTER vp %p\n", (void *)vp);
2761 2762 #endif
2762 2763 if (getzoneid() != GLOBAL_ZONEID) {
2763 2764 error = EPERM;
2764 2765 goto out;
2765 2766 }
2766 2767
2767 2768 /*
2768 2769 * Cachefs only provides pass-through support for NFSv4,
2769 2770 * and all vnode operations are passed through to the
2770 2771 * back file system. For NFSv4 pass-through to work, only
2771 2772 * connected operation is supported, the cnode backvp must
2772 2773 * exist, and cachefs optional (eg., disconnectable) flags
2773 2774 * are turned off. Assert these conditions to ensure that
2774 2775 * the backfilesystem is called for the access operation.
2775 2776 */
2776 2777 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
2777 2778 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
2778 2779
2779 2780 for (;;) {
2780 2781 /* get (or renew) access to the file system */
2781 2782 if (held) {
2782 2783 /* Won't loop with NFSv4 connected behavior */
2783 2784 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
2784 2785 cachefs_cd_release(fscp);
2785 2786 held = 0;
2786 2787 }
2787 2788 error = cachefs_cd_access(fscp, connected, 0);
2788 2789 if (error)
2789 2790 break;
2790 2791 held = 1;
2791 2792
2792 2793 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
2793 2794 error = cachefs_access_connected(vp, mode, flags,
2794 2795 cr);
2795 2796 if (CFS_TIMEOUT(fscp, error)) {
2796 2797 cachefs_cd_release(fscp);
2797 2798 held = 0;
2798 2799 cachefs_cd_timedout(fscp);
2799 2800 connected = 0;
2800 2801 continue;
2801 2802 }
2802 2803 } else {
2803 2804 mutex_enter(&cp->c_statelock);
2804 2805 error = cachefs_access_local(cp, mode, cr);
2805 2806 mutex_exit(&cp->c_statelock);
2806 2807 if (CFS_TIMEOUT(fscp, error)) {
2807 2808 if (cachefs_cd_access_miss(fscp)) {
2808 2809 mutex_enter(&cp->c_statelock);
2809 2810 if (cp->c_backvp == NULL) {
2810 2811 (void) cachefs_getbackvp(fscp,
2811 2812 cp);
2812 2813 }
2813 2814 mutex_exit(&cp->c_statelock);
2814 2815 error = cachefs_access_connected(vp,
2815 2816 mode, flags, cr);
2816 2817 if (!CFS_TIMEOUT(fscp, error))
2817 2818 break;
2818 2819 delay(5*hz);
2819 2820 connected = 0;
2820 2821 continue;
2821 2822 }
2822 2823 connected = 1;
2823 2824 continue;
2824 2825 }
2825 2826 }
2826 2827 break;
2827 2828 }
2828 2829 if (held)
2829 2830 cachefs_cd_release(fscp);
2830 2831 #ifdef CFS_CD_DEBUG
2831 2832 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
2832 2833 #endif
2833 2834 out:
2834 2835 #ifdef CFSDEBUG
2835 2836 CFS_DEBUG(CFSDEBUG_VOPS)
2836 2837 printf("cachefs_access: EXIT error = %d\n", error);
2837 2838 #endif
2838 2839 return (error);
2839 2840 }
2840 2841
2841 2842 static int
2842 2843 cachefs_access_connected(struct vnode *vp, int mode, int flags, cred_t *cr)
2843 2844 {
2844 2845 cnode_t *cp = VTOC(vp);
2845 2846 fscache_t *fscp = C_TO_FSCACHE(cp);
2846 2847 int error = 0;
2847 2848
2848 2849 mutex_enter(&cp->c_statelock);
2849 2850
2850 2851 /* Make sure the cnode attrs are valid first. */
2851 2852 error = CFSOP_CHECK_COBJECT(fscp, cp, 0, cr);
2852 2853 if (error)
2853 2854 goto out;
2854 2855
2855 2856 /* see if can do a local file system check */
2856 2857 if ((fscp->fs_info.fi_mntflags & CFS_ACCESS_BACKFS) == 0 &&
2857 2858 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
2858 2859 error = cachefs_access_local(cp, mode, cr);
2859 2860 goto out;
2860 2861 }
2861 2862
2862 2863 /* else do a remote file system check */
2863 2864 else {
2864 2865 if (cp->c_backvp == NULL) {
2865 2866 error = cachefs_getbackvp(fscp, cp);
2866 2867 if (error)
2867 2868 goto out;
2868 2869 }
2869 2870
2870 2871 CFS_DPRINT_BACKFS_NFSV4(fscp,
2871 2872 ("cachefs_access (nfsv4): cnode %p, backvp %p\n",
2872 2873 cp, cp->c_backvp));
2873 2874 error = VOP_ACCESS(cp->c_backvp, mode, flags, cr, NULL);
2874 2875
2875 2876 /*
2876 2877 * even though we don't `need' the ACL to do access
2877 2878 * via the backvp, we should cache it here to make our
2878 2879 * behavior more reasonable if we go disconnected.
2879 2880 */
2880 2881
2881 2882 if (((fscp->fs_info.fi_mntflags & CFS_NOACL) == 0) &&
2882 2883 (cachefs_vtype_aclok(vp)) &&
2883 2884 ((cp->c_flags & CN_NOCACHE) == 0) &&
2884 2885 (!CFS_ISFS_BACKFS_NFSV4(fscp)) &&
2885 2886 ((cp->c_metadata.md_flags & MD_ACL) == 0))
2886 2887 (void) cachefs_cacheacl(cp, NULL);
2887 2888 }
2888 2889 out:
2889 2890 /*
2890 2891 * If NFS returned ESTALE, mark this cnode as stale, so that
2891 2892 * the vn_open retry will read the file anew from backfs
2892 2893 */
2893 2894 if (error == ESTALE)
2894 2895 cachefs_cnode_stale(cp);
2895 2896
2896 2897 mutex_exit(&cp->c_statelock);
2897 2898 return (error);
2898 2899 }
2899 2900
2900 2901 /*
2901 2902 * CFS has a fastsymlink scheme. If the size of the link is < C_FSL_SIZE, then
2902 2903 * the link is placed in the metadata itself (no front file is allocated).
2903 2904 */
2904 2905 /*ARGSUSED*/
2905 2906 static int
2906 2907 cachefs_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
2907 2908 {
2908 2909 int error = 0;
2909 2910 cnode_t *cp = VTOC(vp);
2910 2911 fscache_t *fscp = C_TO_FSCACHE(cp);
2911 2912 cachefscache_t *cachep = fscp->fs_cache;
2912 2913 int held = 0;
2913 2914 int connected = 0;
2914 2915
2915 2916 if (getzoneid() != GLOBAL_ZONEID)
2916 2917 return (EPERM);
2917 2918
2918 2919 if (vp->v_type != VLNK)
2919 2920 return (EINVAL);
2920 2921
2921 2922 /*
2922 2923 * Cachefs only provides pass-through support for NFSv4,
2923 2924 * and all vnode operations are passed through to the
2924 2925 * back file system. For NFSv4 pass-through to work, only
2925 2926 * connected operation is supported, the cnode backvp must
2926 2927 * exist, and cachefs optional (eg., disconnectable) flags
2927 2928 * are turned off. Assert these conditions to ensure that
2928 2929 * the backfilesystem is called for the readlink operation.
2929 2930 */
2930 2931 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
2931 2932 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
2932 2933
2933 2934 for (;;) {
2934 2935 /* get (or renew) access to the file system */
2935 2936 if (held) {
2936 2937 /* Won't loop with NFSv4 connected behavior */
2937 2938 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
2938 2939 cachefs_cd_release(fscp);
2939 2940 held = 0;
2940 2941 }
2941 2942 error = cachefs_cd_access(fscp, connected, 0);
2942 2943 if (error)
2943 2944 break;
2944 2945 held = 1;
2945 2946
2946 2947 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
2947 2948 /*
2948 2949 * since readlink_connected will call stuffsymlink
2949 2950 * on success, have to serialize access
2950 2951 */
2951 2952 if (!rw_tryenter(&cp->c_rwlock, RW_WRITER)) {
2952 2953 cachefs_cd_release(fscp);
2953 2954 rw_enter(&cp->c_rwlock, RW_WRITER);
2954 2955 error = cachefs_cd_access(fscp, connected, 0);
2955 2956 if (error) {
2956 2957 held = 0;
2957 2958 rw_exit(&cp->c_rwlock);
2958 2959 break;
2959 2960 }
2960 2961 }
2961 2962 error = cachefs_readlink_connected(vp, uiop, cr);
2962 2963 rw_exit(&cp->c_rwlock);
2963 2964 if (CFS_TIMEOUT(fscp, error)) {
2964 2965 cachefs_cd_release(fscp);
2965 2966 held = 0;
2966 2967 cachefs_cd_timedout(fscp);
2967 2968 connected = 0;
2968 2969 continue;
2969 2970 }
2970 2971 } else {
2971 2972 error = cachefs_readlink_disconnected(vp, uiop);
2972 2973 if (CFS_TIMEOUT(fscp, error)) {
2973 2974 if (cachefs_cd_access_miss(fscp)) {
2974 2975 /* as above */
2975 2976 if (!rw_tryenter(&cp->c_rwlock,
2976 2977 RW_WRITER)) {
2977 2978 cachefs_cd_release(fscp);
2978 2979 rw_enter(&cp->c_rwlock,
2979 2980 RW_WRITER);
2980 2981 error = cachefs_cd_access(fscp,
2981 2982 connected, 0);
2982 2983 if (error) {
2983 2984 held = 0;
2984 2985 rw_exit(&cp->c_rwlock);
2985 2986 break;
2986 2987 }
2987 2988 }
2988 2989 error = cachefs_readlink_connected(vp,
2989 2990 uiop, cr);
2990 2991 rw_exit(&cp->c_rwlock);
2991 2992 if (!CFS_TIMEOUT(fscp, error))
2992 2993 break;
2993 2994 delay(5*hz);
2994 2995 connected = 0;
2995 2996 continue;
2996 2997 }
2997 2998 connected = 1;
2998 2999 continue;
2999 3000 }
3000 3001 }
3001 3002 break;
3002 3003 }
3003 3004 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_READLINK))
3004 3005 cachefs_log_readlink(cachep, error, fscp->fs_cfsvfsp,
3005 3006 &cp->c_metadata.md_cookie, cp->c_id.cid_fileno,
3006 3007 crgetuid(cr), cp->c_size);
3007 3008
3008 3009 if (held)
3009 3010 cachefs_cd_release(fscp);
3010 3011 #ifdef CFS_CD_DEBUG
3011 3012 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
3012 3013 #endif
3013 3014
3014 3015 /*
3015 3016 * The over the wire error for attempting to readlink something
3016 3017 * other than a symbolic link is ENXIO. However, we need to
3017 3018 * return EINVAL instead of ENXIO, so we map it here.
3018 3019 */
3019 3020 return (error == ENXIO ? EINVAL : error);
3020 3021 }
3021 3022
3022 3023 static int
3023 3024 cachefs_readlink_connected(vnode_t *vp, uio_t *uiop, cred_t *cr)
3024 3025 {
3025 3026 int error;
3026 3027 cnode_t *cp = VTOC(vp);
3027 3028 fscache_t *fscp = C_TO_FSCACHE(cp);
3028 3029 caddr_t buf;
3029 3030 int buflen;
3030 3031 int readcache = 0;
3031 3032
3032 3033 mutex_enter(&cp->c_statelock);
3033 3034
3034 3035 error = CFSOP_CHECK_COBJECT(fscp, cp, 0, cr);
3035 3036 if (error)
3036 3037 goto out;
3037 3038
3038 3039 /* if the sym link is cached as a fast sym link */
3039 3040 if (cp->c_metadata.md_flags & MD_FASTSYMLNK) {
3040 3041 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
3041 3042 error = uiomove(cp->c_metadata.md_allocinfo,
3042 3043 MIN(cp->c_size, uiop->uio_resid), UIO_READ, uiop);
3043 3044 #ifdef CFSDEBUG
3044 3045 readcache = 1;
3045 3046 goto out;
3046 3047 #else /* CFSDEBUG */
3047 3048 /* XXX KLUDGE! correct for insidious 0-len symlink */
3048 3049 if (cp->c_size != 0) {
3049 3050 readcache = 1;
3050 3051 goto out;
3051 3052 }
3052 3053 #endif /* CFSDEBUG */
3053 3054 }
3054 3055
3055 3056 /* if the sym link is cached in a front file */
3056 3057 if (cp->c_metadata.md_flags & MD_POPULATED) {
3057 3058 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
3058 3059 ASSERT(cp->c_metadata.md_flags & MD_FILE);
3059 3060 if (cp->c_frontvp == NULL) {
3060 3061 (void) cachefs_getfrontfile(cp);
3061 3062 }
3062 3063 if (cp->c_metadata.md_flags & MD_POPULATED) {
3063 3064 /* read symlink data from frontfile */
3064 3065 uiop->uio_offset = 0;
3065 3066 (void) VOP_RWLOCK(cp->c_frontvp,
3066 3067 V_WRITELOCK_FALSE, NULL);
3067 3068 error = VOP_READ(cp->c_frontvp, uiop, 0, kcred, NULL);
3068 3069 VOP_RWUNLOCK(cp->c_frontvp, V_WRITELOCK_FALSE, NULL);
3069 3070
3070 3071 /* XXX KLUDGE! correct for insidious 0-len symlink */
3071 3072 if (cp->c_size != 0) {
3072 3073 readcache = 1;
3073 3074 goto out;
3074 3075 }
3075 3076 }
3076 3077 }
3077 3078
3078 3079 /* get the sym link contents from the back fs */
3079 3080 error = cachefs_readlink_back(cp, cr, &buf, &buflen);
3080 3081 if (error)
3081 3082 goto out;
3082 3083
3083 3084 /* copy the contents out to the user */
3084 3085 error = uiomove(buf, MIN(buflen, uiop->uio_resid), UIO_READ, uiop);
3085 3086
3086 3087 /*
3087 3088 * try to cache the sym link, note that its a noop if NOCACHE is set
3088 3089 * or if NFSv4 pass-through is enabled.
3089 3090 */
3090 3091 if (cachefs_stuffsymlink(cp, buf, buflen)) {
3091 3092 cachefs_nocache(cp);
3092 3093 }
3093 3094
3094 3095 cachefs_kmem_free(buf, MAXPATHLEN);
3095 3096
3096 3097 out:
3097 3098 mutex_exit(&cp->c_statelock);
3098 3099 if (error == 0) {
3099 3100 if (readcache)
3100 3101 fscp->fs_stats.st_hits++;
3101 3102 else
3102 3103 fscp->fs_stats.st_misses++;
3103 3104 }
3104 3105 return (error);
3105 3106 }
3106 3107
3107 3108 static int
3108 3109 cachefs_readlink_disconnected(vnode_t *vp, uio_t *uiop)
3109 3110 {
3110 3111 int error;
3111 3112 cnode_t *cp = VTOC(vp);
3112 3113 fscache_t *fscp = C_TO_FSCACHE(cp);
3113 3114 int readcache = 0;
3114 3115
3115 3116 mutex_enter(&cp->c_statelock);
3116 3117
3117 3118 /* if the sym link is cached as a fast sym link */
3118 3119 if (cp->c_metadata.md_flags & MD_FASTSYMLNK) {
3119 3120 error = uiomove(cp->c_metadata.md_allocinfo,
3120 3121 MIN(cp->c_size, uiop->uio_resid), UIO_READ, uiop);
3121 3122 readcache = 1;
3122 3123 goto out;
3123 3124 }
3124 3125
3125 3126 /* if the sym link is cached in a front file */
3126 3127 if (cp->c_metadata.md_flags & MD_POPULATED) {
3127 3128 ASSERT(cp->c_metadata.md_flags & MD_FILE);
3128 3129 if (cp->c_frontvp == NULL) {
3129 3130 (void) cachefs_getfrontfile(cp);
3130 3131 }
3131 3132 if (cp->c_metadata.md_flags & MD_POPULATED) {
3132 3133 /* read symlink data from frontfile */
3133 3134 uiop->uio_offset = 0;
3134 3135 (void) VOP_RWLOCK(cp->c_frontvp,
3135 3136 V_WRITELOCK_FALSE, NULL);
3136 3137 error = VOP_READ(cp->c_frontvp, uiop, 0, kcred, NULL);
3137 3138 VOP_RWUNLOCK(cp->c_frontvp, V_WRITELOCK_FALSE, NULL);
3138 3139 readcache = 1;
3139 3140 goto out;
3140 3141 }
3141 3142 }
3142 3143 error = ETIMEDOUT;
3143 3144
3144 3145 out:
3145 3146 mutex_exit(&cp->c_statelock);
3146 3147 if (error == 0) {
3147 3148 if (readcache)
3148 3149 fscp->fs_stats.st_hits++;
3149 3150 else
3150 3151 fscp->fs_stats.st_misses++;
3151 3152 }
3152 3153 return (error);
3153 3154 }
3154 3155
3155 3156 /*ARGSUSED*/
3156 3157 static int
3157 3158 cachefs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct)
3158 3159 {
3159 3160 cnode_t *cp = VTOC(vp);
3160 3161 int error = 0;
3161 3162 fscache_t *fscp = C_TO_FSCACHE(cp);
3162 3163 int held = 0;
3163 3164 int connected = 0;
3164 3165
3165 3166 #ifdef CFSDEBUG
3166 3167 CFS_DEBUG(CFSDEBUG_VOPS)
3167 3168 printf("cachefs_fsync: ENTER vp %p\n", (void *)vp);
3168 3169 #endif
3169 3170
3170 3171 if (getzoneid() != GLOBAL_ZONEID) {
3171 3172 error = EPERM;
3172 3173 goto out;
3173 3174 }
3174 3175
3175 3176 if (fscp->fs_backvfsp && fscp->fs_backvfsp->vfs_flag & VFS_RDONLY)
3176 3177 goto out;
3177 3178
3178 3179 /*
3179 3180 * Cachefs only provides pass-through support for NFSv4,
3180 3181 * and all vnode operations are passed through to the
3181 3182 * back file system. For NFSv4 pass-through to work, only
3182 3183 * connected operation is supported, the cnode backvp must
3183 3184 * exist, and cachefs optional (eg., disconnectable) flags
3184 3185 * are turned off. Assert these conditions to ensure that
3185 3186 * the backfilesystem is called for the fsync operation.
3186 3187 */
3187 3188 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
3188 3189 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
3189 3190
3190 3191 for (;;) {
3191 3192 /* get (or renew) access to the file system */
3192 3193 if (held) {
3193 3194 /* Won't loop with NFSv4 connected behavior */
3194 3195 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
3195 3196 cachefs_cd_release(fscp);
3196 3197 held = 0;
3197 3198 }
3198 3199 error = cachefs_cd_access(fscp, connected, 1);
3199 3200 if (error)
3200 3201 break;
3201 3202 held = 1;
3202 3203 connected = 0;
3203 3204
3204 3205 /* if a regular file, write out the pages */
3205 3206 if ((vp->v_type == VREG) && vn_has_cached_data(vp) &&
3206 3207 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
3207 3208 error = cachefs_putpage_common(vp, (offset_t)0,
3208 3209 0, 0, cr);
3209 3210 if (CFS_TIMEOUT(fscp, error)) {
3210 3211 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
3211 3212 cachefs_cd_release(fscp);
3212 3213 held = 0;
3213 3214 cachefs_cd_timedout(fscp);
3214 3215 continue;
3215 3216 } else {
3216 3217 connected = 1;
3217 3218 continue;
3218 3219 }
3219 3220 }
3220 3221
3221 3222 /* if no space left in cache, wait until connected */
3222 3223 if ((error == ENOSPC) &&
3223 3224 (fscp->fs_cdconnected != CFS_CD_CONNECTED)) {
3224 3225 connected = 1;
3225 3226 continue;
3226 3227 }
3227 3228
3228 3229 /* clear the cnode error if putpage worked */
3229 3230 if ((error == 0) && cp->c_error) {
3230 3231 mutex_enter(&cp->c_statelock);
3231 3232 cp->c_error = 0;
3232 3233 mutex_exit(&cp->c_statelock);
3233 3234 }
3234 3235
3235 3236 if (error)
3236 3237 break;
3237 3238 }
3238 3239
3239 3240 /* if connected, sync the backvp */
3240 3241 if ((fscp->fs_cdconnected == CFS_CD_CONNECTED) &&
3241 3242 cp->c_backvp) {
3242 3243 mutex_enter(&cp->c_statelock);
3243 3244 if (cp->c_backvp) {
3244 3245 CFS_DPRINT_BACKFS_NFSV4(fscp,
3245 3246 ("cachefs_fsync (nfsv4): cnode %p, "
3246 3247 "backvp %p\n", cp, cp->c_backvp));
3247 3248 error = VOP_FSYNC(cp->c_backvp, syncflag, cr,
3248 3249 ct);
3249 3250 if (CFS_TIMEOUT(fscp, error)) {
3250 3251 mutex_exit(&cp->c_statelock);
3251 3252 cachefs_cd_release(fscp);
3252 3253 held = 0;
3253 3254 cachefs_cd_timedout(fscp);
3254 3255 continue;
3255 3256 } else if (error && (error != EINTR))
3256 3257 cp->c_error = error;
3257 3258 }
3258 3259 mutex_exit(&cp->c_statelock);
3259 3260 }
3260 3261
3261 3262 /* sync the metadata and the front file to the front fs */
3262 3263 if (!CFS_ISFS_BACKFS_NFSV4(fscp)) {
3263 3264 error = cachefs_sync_metadata(cp);
3264 3265 if (error &&
3265 3266 (fscp->fs_cdconnected == CFS_CD_CONNECTED))
3266 3267 error = 0;
3267 3268 }
3268 3269 break;
3269 3270 }
3270 3271
3271 3272 if (error == 0)
3272 3273 error = cp->c_error;
3273 3274
3274 3275 if (held)
3275 3276 cachefs_cd_release(fscp);
3276 3277
3277 3278 out:
3278 3279 #ifdef CFS_CD_DEBUG
3279 3280 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
3280 3281 #endif
3281 3282
3282 3283 #ifdef CFSDEBUG
3283 3284 CFS_DEBUG(CFSDEBUG_VOPS)
3284 3285 printf("cachefs_fsync: EXIT vp %p\n", (void *)vp);
3285 3286 #endif
3286 3287 return (error);
3287 3288 }
3288 3289
3289 3290 /*
3290 3291 * Called from cachefs_inactive(), to make sure all the data goes out to disk.
3291 3292 */
3292 3293 int
3293 3294 cachefs_sync_metadata(cnode_t *cp)
3294 3295 {
3295 3296 int error = 0;
3296 3297 struct filegrp *fgp;
3297 3298 struct vattr va;
3298 3299 fscache_t *fscp = C_TO_FSCACHE(cp);
3299 3300
3300 3301 #ifdef CFSDEBUG
3301 3302 CFS_DEBUG(CFSDEBUG_VOPS)
3302 3303 printf("c_sync_metadata: ENTER cp %p cflag %x\n",
3303 3304 (void *)cp, cp->c_flags);
3304 3305 #endif
3305 3306
3306 3307 mutex_enter(&cp->c_statelock);
3307 3308 if ((cp->c_flags & CN_UPDATED) == 0)
3308 3309 goto out;
3309 3310 if (cp->c_flags & (CN_STALE | CN_DESTROY))
3310 3311 goto out;
3311 3312 fgp = cp->c_filegrp;
3312 3313 if ((fgp->fg_flags & CFS_FG_WRITE) == 0)
3313 3314 goto out;
3314 3315 if (CFS_ISFS_BACKFS_NFSV4(fscp))
3315 3316 goto out;
3316 3317
3317 3318 if (fgp->fg_flags & CFS_FG_ALLOC_ATTR) {
3318 3319 mutex_exit(&cp->c_statelock);
3319 3320 error = filegrp_allocattr(fgp);
3320 3321 mutex_enter(&cp->c_statelock);
3321 3322 if (error) {
3322 3323 error = 0;
3323 3324 goto out;
3324 3325 }
3325 3326 }
3326 3327
3327 3328 if (cp->c_flags & CN_ALLOC_PENDING) {
3328 3329 error = filegrp_create_metadata(fgp, &cp->c_metadata,
3329 3330 &cp->c_id);
3330 3331 if (error)
3331 3332 goto out;
3332 3333 cp->c_flags &= ~CN_ALLOC_PENDING;
3333 3334 }
3334 3335
3335 3336 if (cp->c_flags & CN_NEED_FRONT_SYNC) {
3336 3337 if (cp->c_frontvp != NULL) {
3337 3338 error = VOP_FSYNC(cp->c_frontvp, FSYNC, kcred, NULL);
3338 3339 if (error) {
3339 3340 cp->c_metadata.md_timestamp.tv_sec = 0;
3340 3341 } else {
3341 3342 va.va_mask = AT_MTIME;
3342 3343 error = VOP_GETATTR(cp->c_frontvp, &va, 0,
3343 3344 kcred, NULL);
3344 3345 if (error)
3345 3346 goto out;
3346 3347 cp->c_metadata.md_timestamp = va.va_mtime;
3347 3348 cp->c_flags &=
3348 3349 ~(CN_NEED_FRONT_SYNC |
3349 3350 CN_POPULATION_PENDING);
3350 3351 }
3351 3352 } else {
3352 3353 cp->c_flags &=
3353 3354 ~(CN_NEED_FRONT_SYNC | CN_POPULATION_PENDING);
3354 3355 }
3355 3356 }
3356 3357
3357 3358 /*
3358 3359 * XXX tony: How can CN_ALLOC_PENDING still be set??
3359 3360 * XXX tony: How can CN_UPDATED not be set?????
3360 3361 */
3361 3362 if ((cp->c_flags & CN_ALLOC_PENDING) == 0 &&
3362 3363 (cp->c_flags & CN_UPDATED)) {
3363 3364 error = filegrp_write_metadata(fgp, &cp->c_id,
3364 3365 &cp->c_metadata);
3365 3366 if (error)
3366 3367 goto out;
3367 3368 }
3368 3369 out:
3369 3370 if (error) {
3370 3371 /* XXX modified files? */
3371 3372 if (cp->c_metadata.md_rlno) {
3372 3373 cachefs_removefrontfile(&cp->c_metadata,
3373 3374 &cp->c_id, fgp);
3374 3375 cachefs_rlent_moveto(C_TO_FSCACHE(cp)->fs_cache,
3375 3376 CACHEFS_RL_FREE, cp->c_metadata.md_rlno, 0);
3376 3377 cp->c_metadata.md_rlno = 0;
3377 3378 cp->c_metadata.md_rltype = CACHEFS_RL_NONE;
3378 3379 if (cp->c_frontvp) {
3379 3380 VN_RELE(cp->c_frontvp);
3380 3381 cp->c_frontvp = NULL;
3381 3382 }
3382 3383 }
3383 3384 if ((cp->c_flags & CN_ALLOC_PENDING) == 0)
3384 3385 (void) filegrp_destroy_metadata(fgp, &cp->c_id);
3385 3386 cp->c_flags |= CN_ALLOC_PENDING;
3386 3387 cachefs_nocache(cp);
3387 3388 }
3388 3389 /*
3389 3390 * we clear the updated bit even on errors because a retry
3390 3391 * will probably fail also.
3391 3392 */
3392 3393 cp->c_flags &= ~CN_UPDATED;
3393 3394 mutex_exit(&cp->c_statelock);
3394 3395
3395 3396 #ifdef CFSDEBUG
3396 3397 CFS_DEBUG(CFSDEBUG_VOPS)
3397 3398 printf("c_sync_metadata: EXIT cp %p cflag %x\n",
3398 3399 (void *)cp, cp->c_flags);
3399 3400 #endif
3400 3401
3401 3402 return (error);
3402 3403 }
3403 3404
3404 3405 /*
3405 3406 * This is the vop entry point for inactivating a vnode.
3406 3407 * It just queues the request for the async thread which
3407 3408 * calls cachefs_inactive.
3408 3409 * Because of the dnlc, it is not safe to grab most locks here.
3409 3410 */
3410 3411 /*ARGSUSED*/
3411 3412 static void
3412 3413 cachefs_inactive(struct vnode *vp, cred_t *cr, caller_context_t *ct)
3413 3414 {
3414 3415 cnode_t *cp;
3415 3416 struct cachefs_req *rp;
3416 3417 fscache_t *fscp;
3417 3418
3418 3419 #ifdef CFSDEBUG
3419 3420 CFS_DEBUG(CFSDEBUG_VOPS)
3420 3421 printf("cachefs_inactive: ENTER vp %p\n", (void *)vp);
3421 3422 #endif
3422 3423
3423 3424 cp = VTOC(vp);
3424 3425 fscp = C_TO_FSCACHE(cp);
3425 3426
3426 3427 ASSERT((cp->c_flags & CN_IDLE) == 0);
3427 3428
3428 3429 /*
3429 3430 * Cachefs only provides pass-through support for NFSv4,
3430 3431 * and all vnode operations are passed through to the
3431 3432 * back file system. For NFSv4 pass-through to work, only
3432 3433 * connected operation is supported, the cnode backvp must
3433 3434 * exist, and cachefs optional (eg., disconnectable) flags
3434 3435 * are turned off. Assert these conditions to ensure that
3435 3436 * the backfilesystem is called for the inactive operation.
3436 3437 */
3437 3438 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
3438 3439 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
3439 3440
3440 3441 /* vn_rele() set the v_count == 1 */
3441 3442
3442 3443 cp->c_ipending = 1;
3443 3444
3444 3445 rp = kmem_cache_alloc(cachefs_req_cache, KM_SLEEP);
3445 3446 rp->cfs_cmd = CFS_IDLE;
3446 3447 rp->cfs_cr = cr;
3447 3448 crhold(rp->cfs_cr);
3448 3449 rp->cfs_req_u.cu_idle.ci_vp = vp;
3449 3450 cachefs_addqueue(rp, &(C_TO_FSCACHE(cp)->fs_workq));
3450 3451
3451 3452 #ifdef CFSDEBUG
3452 3453 CFS_DEBUG(CFSDEBUG_VOPS)
3453 3454 printf("cachefs_inactive: EXIT vp %p\n", (void *)vp);
3454 3455 #endif
3455 3456 }
3456 3457
3457 3458 /* ARGSUSED */
3458 3459 static int
3459 3460 cachefs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp,
3460 3461 struct pathname *pnp, int flags, vnode_t *rdir, cred_t *cr,
3461 3462 caller_context_t *ct, int *direntflags, pathname_t *realpnp)
3462 3463
3463 3464 {
3464 3465 int error = 0;
3465 3466 cnode_t *dcp = VTOC(dvp);
3466 3467 fscache_t *fscp = C_TO_FSCACHE(dcp);
3467 3468 int held = 0;
3468 3469 int connected = 0;
3469 3470
3470 3471 #ifdef CFSDEBUG
3471 3472 CFS_DEBUG(CFSDEBUG_VOPS)
3472 3473 printf("cachefs_lookup: ENTER dvp %p nm %s\n", (void *)dvp, nm);
3473 3474 #endif
3474 3475
3475 3476 if (getzoneid() != GLOBAL_ZONEID) {
3476 3477 error = EPERM;
3477 3478 goto out;
3478 3479 }
3479 3480
3480 3481 /*
3481 3482 * Cachefs only provides pass-through support for NFSv4,
3482 3483 * and all vnode operations are passed through to the
3483 3484 * back file system. For NFSv4 pass-through to work, only
3484 3485 * connected operation is supported, the cnode backvp must
3485 3486 * exist, and cachefs optional (eg., disconnectable) flags
3486 3487 * are turned off. Assert these conditions to ensure that
3487 3488 * the backfilesystem is called for the lookup operation.
3488 3489 */
3489 3490 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
3490 3491 CFS_BACKFS_NFSV4_ASSERT_CNODE(dcp);
3491 3492
3492 3493 for (;;) {
3493 3494 /* get (or renew) access to the file system */
3494 3495 if (held) {
3495 3496 /* Won't loop with NFSv4 connected behavior */
3496 3497 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
3497 3498 cachefs_cd_release(fscp);
3498 3499 held = 0;
3499 3500 }
3500 3501 error = cachefs_cd_access(fscp, connected, 0);
3501 3502 if (error)
3502 3503 break;
3503 3504 held = 1;
3504 3505
3505 3506 error = cachefs_lookup_common(dvp, nm, vpp, pnp,
3506 3507 flags, rdir, cr);
3507 3508 if (CFS_TIMEOUT(fscp, error)) {
3508 3509 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
3509 3510 cachefs_cd_release(fscp);
3510 3511 held = 0;
3511 3512 cachefs_cd_timedout(fscp);
3512 3513 connected = 0;
3513 3514 continue;
3514 3515 } else {
3515 3516 if (cachefs_cd_access_miss(fscp)) {
3516 3517 rw_enter(&dcp->c_rwlock, RW_READER);
3517 3518 error = cachefs_lookup_back(dvp, nm,
3518 3519 vpp, cr);
3519 3520 rw_exit(&dcp->c_rwlock);
3520 3521 if (!CFS_TIMEOUT(fscp, error))
3521 3522 break;
3522 3523 delay(5*hz);
3523 3524 connected = 0;
3524 3525 continue;
3525 3526 }
3526 3527 connected = 1;
3527 3528 continue;
3528 3529 }
3529 3530 }
3530 3531 break;
3531 3532 }
3532 3533 if (held)
3533 3534 cachefs_cd_release(fscp);
3534 3535
3535 3536 if (error == 0 && IS_DEVVP(*vpp)) {
3536 3537 struct vnode *newvp;
3537 3538 newvp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
3538 3539 VN_RELE(*vpp);
3539 3540 if (newvp == NULL) {
3540 3541 error = ENOSYS;
3541 3542 } else {
3542 3543 *vpp = newvp;
3543 3544 }
3544 3545 }
3545 3546
3546 3547 #ifdef CFS_CD_DEBUG
3547 3548 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
3548 3549 #endif
3549 3550 out:
3550 3551 #ifdef CFSDEBUG
3551 3552 CFS_DEBUG(CFSDEBUG_VOPS)
3552 3553 printf("cachefs_lookup: EXIT error = %d\n", error);
3553 3554 #endif
3554 3555
3555 3556 return (error);
3556 3557 }
3557 3558
3558 3559 /* ARGSUSED */
3559 3560 int
3560 3561 cachefs_lookup_common(vnode_t *dvp, char *nm, vnode_t **vpp,
3561 3562 struct pathname *pnp, int flags, vnode_t *rdir, cred_t *cr)
3562 3563 {
3563 3564 int error = 0;
3564 3565 cnode_t *cp, *dcp = VTOC(dvp);
3565 3566 fscache_t *fscp = C_TO_FSCACHE(dcp);
3566 3567 struct fid cookie;
3567 3568 u_offset_t d_offset;
3568 3569 struct cachefs_req *rp;
3569 3570 cfs_cid_t cid, dircid;
3570 3571 uint_t flag;
3571 3572 uint_t uncached = 0;
3572 3573
3573 3574 *vpp = NULL;
3574 3575
3575 3576 /*
3576 3577 * If lookup is for "", just return dvp. Don't need
3577 3578 * to send it over the wire, look it up in the dnlc,
3578 3579 * or perform any access checks.
3579 3580 */
3580 3581 if (*nm == '\0') {
3581 3582 VN_HOLD(dvp);
3582 3583 *vpp = dvp;
3583 3584 return (0);
3584 3585 }
3585 3586
3586 3587 /* can't do lookups in non-directories */
3587 3588 if (dvp->v_type != VDIR)
3588 3589 return (ENOTDIR);
3589 3590
3590 3591 /* perform access check, also does consistency check if connected */
3591 3592 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
3592 3593 error = cachefs_access_connected(dvp, VEXEC, 0, cr);
3593 3594 } else {
3594 3595 mutex_enter(&dcp->c_statelock);
3595 3596 error = cachefs_access_local(dcp, VEXEC, cr);
3596 3597 mutex_exit(&dcp->c_statelock);
3597 3598 }
3598 3599 if (error)
3599 3600 return (error);
3600 3601
3601 3602 /*
3602 3603 * If lookup is for ".", just return dvp. Don't need
3603 3604 * to send it over the wire or look it up in the dnlc,
3604 3605 * just need to check access.
3605 3606 */
3606 3607 if (strcmp(nm, ".") == 0) {
3607 3608 VN_HOLD(dvp);
3608 3609 *vpp = dvp;
3609 3610 return (0);
3610 3611 }
3611 3612
3612 3613 /* check the dnlc */
3613 3614 *vpp = (vnode_t *)dnlc_lookup(dvp, nm);
3614 3615 if (*vpp)
3615 3616 return (0);
3616 3617
3617 3618 /* read lock the dir before starting the search */
3618 3619 rw_enter(&dcp->c_rwlock, RW_READER);
3619 3620
3620 3621 mutex_enter(&dcp->c_statelock);
3621 3622 dircid = dcp->c_id;
3622 3623
3623 3624 dcp->c_usage++;
3624 3625
3625 3626 /* if front file is not usable, lookup on the back fs */
3626 3627 if ((dcp->c_flags & (CN_NOCACHE | CN_ASYNC_POPULATE)) ||
3627 3628 CFS_ISFS_BACKFS_NFSV4(fscp) ||
3628 3629 ((dcp->c_filegrp->fg_flags & CFS_FG_READ) == 0)) {
3629 3630 mutex_exit(&dcp->c_statelock);
3630 3631 if (fscp->fs_cdconnected == CFS_CD_CONNECTED)
3631 3632 error = cachefs_lookup_back(dvp, nm, vpp, cr);
3632 3633 else
3633 3634 error = ETIMEDOUT;
3634 3635 goto out;
3635 3636 }
3636 3637
3637 3638 /* if the front file is not populated, try to populate it */
3638 3639 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
3639 3640 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
3640 3641 error = ETIMEDOUT;
3641 3642 mutex_exit(&dcp->c_statelock);
3642 3643 goto out;
3643 3644 }
3644 3645
3645 3646 if (cachefs_async_okay()) {
3646 3647 /* cannot populate if cache is not writable */
3647 3648 ASSERT((dcp->c_flags &
3648 3649 (CN_ASYNC_POPULATE | CN_NOCACHE)) == 0);
3649 3650 dcp->c_flags |= CN_ASYNC_POPULATE;
3650 3651
3651 3652 rp = kmem_cache_alloc(cachefs_req_cache, KM_SLEEP);
3652 3653 rp->cfs_cmd = CFS_POPULATE;
3653 3654 rp->cfs_req_u.cu_populate.cpop_vp = dvp;
3654 3655 rp->cfs_cr = cr;
3655 3656
3656 3657 crhold(cr);
3657 3658 VN_HOLD(dvp);
3658 3659
3659 3660 cachefs_addqueue(rp, &fscp->fs_workq);
3660 3661 } else if (fscp->fs_info.fi_mntflags & CFS_NOACL) {
3661 3662 error = cachefs_dir_fill(dcp, cr);
3662 3663 if (error != 0) {
3663 3664 mutex_exit(&dcp->c_statelock);
3664 3665 goto out;
3665 3666 }
3666 3667 }
3667 3668 /* no populate if too many asyncs and we have to cache ACLs */
3668 3669
3669 3670 mutex_exit(&dcp->c_statelock);
3670 3671
3671 3672 if (fscp->fs_cdconnected == CFS_CD_CONNECTED)
3672 3673 error = cachefs_lookup_back(dvp, nm, vpp, cr);
3673 3674 else
3674 3675 error = ETIMEDOUT;
3675 3676 goto out;
3676 3677 }
3677 3678
3678 3679 /* by now we have a valid cached front file that we can search */
3679 3680
3680 3681 ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
3681 3682 error = cachefs_dir_look(dcp, nm, &cookie, &flag,
3682 3683 &d_offset, &cid);
3683 3684 mutex_exit(&dcp->c_statelock);
3684 3685
3685 3686 if (error) {
3686 3687 /* if the entry does not have the fid, go get it */
3687 3688 if (error == EINVAL) {
3688 3689 if (fscp->fs_cdconnected == CFS_CD_CONNECTED)
3689 3690 error = cachefs_lookup_back(dvp, nm, vpp, cr);
3690 3691 else
3691 3692 error = ETIMEDOUT;
3692 3693 }
3693 3694
3694 3695 /* errors other than does not exist */
3695 3696 else if (error != ENOENT) {
3696 3697 if (fscp->fs_cdconnected == CFS_CD_CONNECTED)
3697 3698 error = cachefs_lookup_back(dvp, nm, vpp, cr);
3698 3699 else
3699 3700 error = ETIMEDOUT;
3700 3701 }
3701 3702 goto out;
3702 3703 }
3703 3704
3704 3705 /*
3705 3706 * Else we found the entry in the cached directory.
3706 3707 * Make a cnode for it.
3707 3708 */
3708 3709 error = cachefs_cnode_make(&cid, fscp, &cookie, NULL, NULL,
3709 3710 cr, 0, &cp);
3710 3711 if (error == ESTALE) {
3711 3712 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
3712 3713 mutex_enter(&dcp->c_statelock);
3713 3714 cachefs_nocache(dcp);
3714 3715 mutex_exit(&dcp->c_statelock);
3715 3716 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
3716 3717 error = cachefs_lookup_back(dvp, nm, vpp, cr);
3717 3718 uncached = 1;
3718 3719 } else
3719 3720 error = ETIMEDOUT;
3720 3721 } else if (error == 0) {
3721 3722 *vpp = CTOV(cp);
3722 3723 }
3723 3724
3724 3725 out:
3725 3726 if (error == 0) {
3726 3727 /* put the entry in the dnlc */
3727 3728 if (cachefs_dnlc)
3728 3729 dnlc_enter(dvp, nm, *vpp);
3729 3730
3730 3731 /* save the cid of the parent so can find the name */
3731 3732 cp = VTOC(*vpp);
3732 3733 if (bcmp(&cp->c_metadata.md_parent, &dircid,
3733 3734 sizeof (cfs_cid_t)) != 0) {
3734 3735 mutex_enter(&cp->c_statelock);
3735 3736 cp->c_metadata.md_parent = dircid;
3736 3737 cp->c_flags |= CN_UPDATED;
3737 3738 mutex_exit(&cp->c_statelock);
3738 3739 }
3739 3740 }
3740 3741
3741 3742 rw_exit(&dcp->c_rwlock);
3742 3743 if (uncached && dcp->c_metadata.md_flags & MD_PACKED)
3743 3744 (void) cachefs_pack_common(dvp, cr);
3744 3745 return (error);
3745 3746 }
3746 3747
3747 3748 /*
3748 3749 * Called from cachefs_lookup_common when the back file system needs to be
3749 3750 * examined to perform the lookup.
3750 3751 */
3751 3752 static int
3752 3753 cachefs_lookup_back(vnode_t *dvp, char *nm, vnode_t **vpp,
3753 3754 cred_t *cr)
3754 3755 {
3755 3756 int error = 0;
3756 3757 cnode_t *cp, *dcp = VTOC(dvp);
3757 3758 fscache_t *fscp = C_TO_FSCACHE(dcp);
3758 3759 vnode_t *backvp = NULL;
3759 3760 struct vattr va;
3760 3761 struct fid cookie;
3761 3762 cfs_cid_t cid;
3762 3763 uint32_t valid_fid;
3763 3764
3764 3765 mutex_enter(&dcp->c_statelock);
3765 3766
3766 3767 /* do a lookup on the back FS to get the back vnode */
3767 3768 if (dcp->c_backvp == NULL) {
3768 3769 error = cachefs_getbackvp(fscp, dcp);
3769 3770 if (error)
3770 3771 goto out;
3771 3772 }
3772 3773
3773 3774 CFS_DPRINT_BACKFS_NFSV4(fscp,
3774 3775 ("cachefs_lookup (nfsv4): dcp %p, dbackvp %p, name %s\n",
3775 3776 dcp, dcp->c_backvp, nm));
3776 3777 error = VOP_LOOKUP(dcp->c_backvp, nm, &backvp, (struct pathname *)NULL,
3777 3778 0, (vnode_t *)NULL, cr, NULL, NULL, NULL);
3778 3779 if (error)
3779 3780 goto out;
3780 3781 if (IS_DEVVP(backvp)) {
3781 3782 struct vnode *devvp = backvp;
3782 3783
3783 3784 if (VOP_REALVP(devvp, &backvp, NULL) == 0) {
3784 3785 VN_HOLD(backvp);
3785 3786 VN_RELE(devvp);
3786 3787 }
3787 3788 }
3788 3789
3789 3790 /* get the fid and attrs from the back fs */
3790 3791 valid_fid = (CFS_ISFS_BACKFS_NFSV4(fscp) ? FALSE : TRUE);
3791 3792 error = cachefs_getcookie(backvp, &cookie, &va, cr, valid_fid);
3792 3793 if (error)
3793 3794 goto out;
3794 3795
3795 3796 cid.cid_fileno = va.va_nodeid;
3796 3797 cid.cid_flags = 0;
3797 3798
3798 3799 #if 0
3799 3800 /* XXX bob: this is probably no longer necessary */
3800 3801 /* if the directory entry was incomplete, we can complete it now */
3801 3802 if ((dcp->c_metadata.md_flags & MD_POPULATED) &&
3802 3803 ((dcp->c_flags & CN_ASYNC_POPULATE) == 0) &&
3803 3804 (dcp->c_filegrp->fg_flags & CFS_FG_WRITE)) {
3804 3805 cachefs_dir_modentry(dcp, d_offset, &cookie, &cid);
3805 3806 }
3806 3807 #endif
3807 3808
3808 3809 out:
3809 3810 mutex_exit(&dcp->c_statelock);
3810 3811
3811 3812 /* create the cnode */
3812 3813 if (error == 0) {
3813 3814 error = cachefs_cnode_make(&cid, fscp,
3814 3815 (valid_fid ? &cookie : NULL),
3815 3816 &va, backvp, cr, 0, &cp);
3816 3817 if (error == 0) {
3817 3818 *vpp = CTOV(cp);
3818 3819 }
3819 3820 }
3820 3821
3821 3822 if (backvp)
3822 3823 VN_RELE(backvp);
3823 3824
3824 3825 return (error);
3825 3826 }
3826 3827
3827 3828 /*ARGSUSED7*/
3828 3829 static int
3829 3830 cachefs_create(vnode_t *dvp, char *nm, vattr_t *vap,
3830 3831 vcexcl_t exclusive, int mode, vnode_t **vpp, cred_t *cr, int flag,
3831 3832 caller_context_t *ct, vsecattr_t *vsecp)
3832 3833
3833 3834 {
3834 3835 cnode_t *dcp = VTOC(dvp);
3835 3836 fscache_t *fscp = C_TO_FSCACHE(dcp);
3836 3837 cachefscache_t *cachep = fscp->fs_cache;
3837 3838 int error;
3838 3839 int connected = 0;
3839 3840 int held = 0;
3840 3841
3841 3842 #ifdef CFSDEBUG
3842 3843 CFS_DEBUG(CFSDEBUG_VOPS)
3843 3844 printf("cachefs_create: ENTER dvp %p, nm %s\n",
3844 3845 (void *)dvp, nm);
3845 3846 #endif
3846 3847 if (getzoneid() != GLOBAL_ZONEID) {
3847 3848 error = EPERM;
3848 3849 goto out;
3849 3850 }
3850 3851
3851 3852 /*
3852 3853 * Cachefs only provides pass-through support for NFSv4,
3853 3854 * and all vnode operations are passed through to the
3854 3855 * back file system. For NFSv4 pass-through to work, only
3855 3856 * connected operation is supported, the cnode backvp must
3856 3857 * exist, and cachefs optional (eg., disconnectable) flags
3857 3858 * are turned off. Assert these conditions to ensure that
3858 3859 * the backfilesystem is called for the create operation.
3859 3860 */
3860 3861 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
3861 3862 CFS_BACKFS_NFSV4_ASSERT_CNODE(dcp);
3862 3863
3863 3864 for (;;) {
3864 3865 /* get (or renew) access to the file system */
3865 3866 if (held) {
3866 3867 /* Won't loop with NFSv4 connected behavior */
3867 3868 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
3868 3869 cachefs_cd_release(fscp);
3869 3870 held = 0;
3870 3871 }
3871 3872 error = cachefs_cd_access(fscp, connected, 1);
3872 3873 if (error)
3873 3874 break;
3874 3875 held = 1;
3875 3876
3876 3877 /*
3877 3878 * if we are connected, perform the remote portion of the
3878 3879 * create.
3879 3880 */
3880 3881 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
3881 3882 error = cachefs_create_connected(dvp, nm, vap,
3882 3883 exclusive, mode, vpp, cr);
3883 3884 if (CFS_TIMEOUT(fscp, error)) {
3884 3885 cachefs_cd_release(fscp);
3885 3886 held = 0;
3886 3887 cachefs_cd_timedout(fscp);
3887 3888 connected = 0;
3888 3889 continue;
3889 3890 } else if (error) {
3890 3891 break;
3891 3892 }
3892 3893 }
3893 3894
3894 3895 /* else we must be disconnected */
3895 3896 else {
3896 3897 error = cachefs_create_disconnected(dvp, nm, vap,
3897 3898 exclusive, mode, vpp, cr);
3898 3899 if (CFS_TIMEOUT(fscp, error)) {
3899 3900 connected = 1;
3900 3901 continue;
3901 3902 } else if (error) {
3902 3903 break;
3903 3904 }
3904 3905 }
3905 3906 break;
3906 3907 }
3907 3908
3908 3909 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_CREATE)) {
3909 3910 fid_t *fidp = NULL;
3910 3911 ino64_t fileno = 0;
3911 3912 cnode_t *cp = NULL;
3912 3913 if (error == 0)
3913 3914 cp = VTOC(*vpp);
3914 3915
3915 3916 if (cp != NULL) {
3916 3917 fidp = &cp->c_metadata.md_cookie;
3917 3918 fileno = cp->c_id.cid_fileno;
3918 3919 }
3919 3920 cachefs_log_create(cachep, error, fscp->fs_cfsvfsp,
3920 3921 fidp, fileno, crgetuid(cr));
3921 3922 }
3922 3923
3923 3924 if (held)
3924 3925 cachefs_cd_release(fscp);
3925 3926
3926 3927 if (error == 0 && CFS_ISFS_NONSHARED(fscp))
3927 3928 (void) cachefs_pack(dvp, nm, cr);
3928 3929 if (error == 0 && IS_DEVVP(*vpp)) {
3929 3930 struct vnode *spcvp;
3930 3931
3931 3932 spcvp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
3932 3933 VN_RELE(*vpp);
3933 3934 if (spcvp == NULL) {
3934 3935 error = ENOSYS;
3935 3936 } else {
3936 3937 *vpp = spcvp;
3937 3938 }
3938 3939 }
3939 3940
3940 3941 #ifdef CFS_CD_DEBUG
3941 3942 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
3942 3943 #endif
3943 3944 out:
3944 3945 #ifdef CFSDEBUG
3945 3946 CFS_DEBUG(CFSDEBUG_VOPS)
3946 3947 printf("cachefs_create: EXIT error %d\n", error);
3947 3948 #endif
3948 3949 return (error);
3949 3950 }
3950 3951
3951 3952
3952 3953 static int
3953 3954 cachefs_create_connected(vnode_t *dvp, char *nm, vattr_t *vap,
3954 3955 enum vcexcl exclusive, int mode, vnode_t **vpp, cred_t *cr)
3955 3956 {
3956 3957 cnode_t *dcp = VTOC(dvp);
3957 3958 fscache_t *fscp = C_TO_FSCACHE(dcp);
3958 3959 int error;
3959 3960 vnode_t *tvp = NULL;
3960 3961 vnode_t *devvp;
3961 3962 fid_t cookie;
3962 3963 vattr_t va;
3963 3964 cnode_t *ncp;
3964 3965 cfs_cid_t cid;
3965 3966 vnode_t *vp;
3966 3967 uint32_t valid_fid;
3967 3968
3968 3969 /* special case if file already exists */
3969 3970 error = cachefs_lookup_common(dvp, nm, &vp, NULL, 0, NULL, cr);
3970 3971 if (CFS_TIMEOUT(fscp, error))
3971 3972 return (error);
3972 3973 if (error == 0) {
3973 3974 if (exclusive == EXCL)
3974 3975 error = EEXIST;
3975 3976 else if (vp->v_type == VDIR && (mode & VWRITE))
3976 3977 error = EISDIR;
3977 3978 else if ((error =
3978 3979 cachefs_access_connected(vp, mode, 0, cr)) == 0) {
3979 3980 if ((vap->va_mask & AT_SIZE) && (vp->v_type == VREG)) {
3980 3981 vap->va_mask = AT_SIZE;
3981 3982 error = cachefs_setattr_common(vp, vap, 0,
3982 3983 cr, NULL);
3983 3984 }
3984 3985 }
3985 3986 if (error) {
3986 3987 VN_RELE(vp);
3987 3988 } else
3988 3989 *vpp = vp;
3989 3990 return (error);
3990 3991 }
3991 3992
3992 3993 rw_enter(&dcp->c_rwlock, RW_WRITER);
3993 3994 mutex_enter(&dcp->c_statelock);
3994 3995
3995 3996 /* consistency check the directory */
3996 3997 error = CFSOP_CHECK_COBJECT(fscp, dcp, 0, cr);
3997 3998 if (error) {
3998 3999 mutex_exit(&dcp->c_statelock);
3999 4000 goto out;
4000 4001 }
4001 4002
4002 4003 /* get the backvp if necessary */
4003 4004 if (dcp->c_backvp == NULL) {
4004 4005 error = cachefs_getbackvp(fscp, dcp);
4005 4006 if (error) {
4006 4007 mutex_exit(&dcp->c_statelock);
4007 4008 goto out;
4008 4009 }
4009 4010 }
4010 4011
4011 4012 /* create the file on the back fs */
4012 4013 CFS_DPRINT_BACKFS_NFSV4(fscp,
4013 4014 ("cachefs_create (nfsv4): dcp %p, dbackvp %p,"
4014 4015 "name %s\n", dcp, dcp->c_backvp, nm));
4015 4016 error = VOP_CREATE(dcp->c_backvp, nm, vap, exclusive, mode,
4016 4017 &devvp, cr, 0, NULL, NULL);
4017 4018 mutex_exit(&dcp->c_statelock);
4018 4019 if (error)
4019 4020 goto out;
4020 4021 if (VOP_REALVP(devvp, &tvp, NULL) == 0) {
4021 4022 VN_HOLD(tvp);
4022 4023 VN_RELE(devvp);
4023 4024 } else {
4024 4025 tvp = devvp;
4025 4026 }
4026 4027
4027 4028 /* get the fid and attrs from the back fs */
4028 4029 valid_fid = (CFS_ISFS_BACKFS_NFSV4(fscp) ? FALSE : TRUE);
4029 4030 error = cachefs_getcookie(tvp, &cookie, &va, cr, valid_fid);
4030 4031 if (error)
4031 4032 goto out;
4032 4033
4033 4034 /* make the cnode */
4034 4035 cid.cid_fileno = va.va_nodeid;
4035 4036 cid.cid_flags = 0;
4036 4037 error = cachefs_cnode_make(&cid, fscp, (valid_fid ? &cookie : NULL),
4037 4038 &va, tvp, cr, 0, &ncp);
4038 4039 if (error)
4039 4040 goto out;
4040 4041
4041 4042 *vpp = CTOV(ncp);
4042 4043
4043 4044 /* enter it in the parent directory */
4044 4045 mutex_enter(&dcp->c_statelock);
4045 4046 if (CFS_ISFS_NONSHARED(fscp) &&
4046 4047 (dcp->c_metadata.md_flags & MD_POPULATED)) {
4047 4048 /* see if entry already exists */
4048 4049 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
4049 4050 error = cachefs_dir_look(dcp, nm, NULL, NULL, NULL, NULL);
4050 4051 if (error == ENOENT) {
4051 4052 /* entry, does not exist, add the new file */
4052 4053 error = cachefs_dir_enter(dcp, nm, &ncp->c_cookie,
4053 4054 &ncp->c_id, SM_ASYNC);
4054 4055 if (error) {
4055 4056 cachefs_nocache(dcp);
4056 4057 error = 0;
4057 4058 }
4058 4059 /* XXX should this be done elsewhere, too? */
4059 4060 dnlc_enter(dvp, nm, *vpp);
4060 4061 } else {
4061 4062 /* entry exists or some other problem */
4062 4063 cachefs_nocache(dcp);
4063 4064 error = 0;
4064 4065 }
4065 4066 }
4066 4067 CFSOP_MODIFY_COBJECT(fscp, dcp, cr);
4067 4068 mutex_exit(&dcp->c_statelock);
4068 4069
4069 4070 out:
4070 4071 rw_exit(&dcp->c_rwlock);
4071 4072 if (tvp)
4072 4073 VN_RELE(tvp);
4073 4074
4074 4075 return (error);
4075 4076 }
4076 4077
4077 4078 static int
4078 4079 cachefs_create_disconnected(vnode_t *dvp, char *nm, vattr_t *vap,
4079 4080 enum vcexcl exclusive, int mode, vnode_t **vpp, cred_t *cr)
4080 4081 {
4081 4082 cnode_t *dcp = VTOC(dvp);
4082 4083 cnode_t *cp;
4083 4084 cnode_t *ncp = NULL;
4084 4085 vnode_t *vp;
4085 4086 fscache_t *fscp = C_TO_FSCACHE(dcp);
4086 4087 int error = 0;
4087 4088 struct vattr va;
4088 4089 timestruc_t current_time;
4089 4090 off_t commit = 0;
4090 4091 fid_t cookie;
4091 4092 cfs_cid_t cid;
4092 4093
4093 4094 rw_enter(&dcp->c_rwlock, RW_WRITER);
4094 4095 mutex_enter(&dcp->c_statelock);
4095 4096
4096 4097 /* give up if the directory is not populated */
4097 4098 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
4098 4099 mutex_exit(&dcp->c_statelock);
4099 4100 rw_exit(&dcp->c_rwlock);
4100 4101 return (ETIMEDOUT);
4101 4102 }
4102 4103
4103 4104 /* special case if file already exists */
4104 4105 error = cachefs_dir_look(dcp, nm, &cookie, NULL, NULL, &cid);
4105 4106 if (error == EINVAL) {
4106 4107 mutex_exit(&dcp->c_statelock);
4107 4108 rw_exit(&dcp->c_rwlock);
4108 4109 return (ETIMEDOUT);
4109 4110 }
4110 4111 if (error == 0) {
4111 4112 mutex_exit(&dcp->c_statelock);
4112 4113 rw_exit(&dcp->c_rwlock);
4113 4114 error = cachefs_cnode_make(&cid, fscp, &cookie, NULL, NULL,
4114 4115 cr, 0, &cp);
4115 4116 if (error) {
4116 4117 return (error);
4117 4118 }
4118 4119 vp = CTOV(cp);
4119 4120
4120 4121 if (cp->c_metadata.md_flags & MD_NEEDATTRS)
4121 4122 error = ETIMEDOUT;
4122 4123 else if (exclusive == EXCL)
4123 4124 error = EEXIST;
4124 4125 else if (vp->v_type == VDIR && (mode & VWRITE))
4125 4126 error = EISDIR;
4126 4127 else {
4127 4128 mutex_enter(&cp->c_statelock);
4128 4129 error = cachefs_access_local(cp, mode, cr);
4129 4130 mutex_exit(&cp->c_statelock);
4130 4131 if (!error) {
4131 4132 if ((vap->va_mask & AT_SIZE) &&
4132 4133 (vp->v_type == VREG)) {
4133 4134 vap->va_mask = AT_SIZE;
4134 4135 error = cachefs_setattr_common(vp,
4135 4136 vap, 0, cr, NULL);
4136 4137 }
4137 4138 }
4138 4139 }
4139 4140 if (error) {
4140 4141 VN_RELE(vp);
4141 4142 } else
4142 4143 *vpp = vp;
4143 4144 return (error);
4144 4145 }
4145 4146
4146 4147 /* give up if cannot modify the cache */
4147 4148 if (CFS_ISFS_WRITE_AROUND(fscp)) {
4148 4149 mutex_exit(&dcp->c_statelock);
4149 4150 error = ETIMEDOUT;
4150 4151 goto out;
4151 4152 }
4152 4153
4153 4154 /* check access */
4154 4155 if (error = cachefs_access_local(dcp, VWRITE, cr)) {
4155 4156 mutex_exit(&dcp->c_statelock);
4156 4157 goto out;
4157 4158 }
4158 4159
4159 4160 /* mark dir as modified */
4160 4161 cachefs_modified(dcp);
4161 4162 mutex_exit(&dcp->c_statelock);
4162 4163
4163 4164 /* must be privileged to set sticky bit */
4164 4165 if ((vap->va_mode & VSVTX) && secpolicy_vnode_stky_modify(cr) != 0)
4165 4166 vap->va_mode &= ~VSVTX;
4166 4167
4167 4168 /* make up a reasonable set of attributes */
4168 4169 cachefs_attr_setup(vap, &va, dcp, cr);
4169 4170
4170 4171 /* create the cnode */
4171 4172 error = cachefs_cnode_create(fscp, &va, 0, &ncp);
4172 4173 if (error)
4173 4174 goto out;
4174 4175
4175 4176 mutex_enter(&ncp->c_statelock);
4176 4177
4177 4178 /* get the front file now instead of later */
4178 4179 if (vap->va_type == VREG) {
4179 4180 error = cachefs_getfrontfile(ncp);
4180 4181 if (error) {
4181 4182 mutex_exit(&ncp->c_statelock);
4182 4183 goto out;
4183 4184 }
4184 4185 ASSERT(ncp->c_frontvp != NULL);
4185 4186 ASSERT((ncp->c_flags & CN_ALLOC_PENDING) == 0);
4186 4187 ncp->c_metadata.md_flags |= MD_POPULATED;
4187 4188 } else {
4188 4189 ASSERT(ncp->c_flags & CN_ALLOC_PENDING);
4189 4190 if (ncp->c_filegrp->fg_flags & CFS_FG_ALLOC_ATTR) {
4190 4191 (void) filegrp_allocattr(ncp->c_filegrp);
4191 4192 }
4192 4193 error = filegrp_create_metadata(ncp->c_filegrp,
4193 4194 &ncp->c_metadata, &ncp->c_id);
4194 4195 if (error) {
4195 4196 mutex_exit(&ncp->c_statelock);
4196 4197 goto out;
4197 4198 }
4198 4199 ncp->c_flags &= ~CN_ALLOC_PENDING;
4199 4200 }
4200 4201 mutex_enter(&dcp->c_statelock);
4201 4202 cachefs_creategid(dcp, ncp, vap, cr);
4202 4203 cachefs_createacl(dcp, ncp);
4203 4204 mutex_exit(&dcp->c_statelock);
4204 4205
4205 4206 /* set times on the file */
4206 4207 gethrestime(¤t_time);
4207 4208 ncp->c_metadata.md_vattr.va_atime = current_time;
4208 4209 ncp->c_metadata.md_localctime = current_time;
4209 4210 ncp->c_metadata.md_localmtime = current_time;
4210 4211 ncp->c_metadata.md_flags |= MD_LOCALMTIME | MD_LOCALCTIME;
4211 4212
4212 4213 /* reserve space for the daemon cid mapping */
4213 4214 error = cachefs_dlog_cidmap(fscp);
4214 4215 if (error) {
4215 4216 mutex_exit(&ncp->c_statelock);
4216 4217 goto out;
4217 4218 }
4218 4219 ncp->c_metadata.md_flags |= MD_MAPPING;
4219 4220
4220 4221 /* mark the new file as modified */
4221 4222 if (cachefs_modified_alloc(ncp)) {
4222 4223 mutex_exit(&ncp->c_statelock);
4223 4224 error = ENOSPC;
4224 4225 goto out;
4225 4226 }
4226 4227 ncp->c_flags |= CN_UPDATED;
4227 4228
4228 4229 /*
4229 4230 * write the metadata now rather than waiting until
4230 4231 * inactive so that if there's no space we can let
4231 4232 * the caller know.
4232 4233 */
4233 4234 ASSERT((ncp->c_flags & CN_ALLOC_PENDING) == 0);
4234 4235 ASSERT((ncp->c_filegrp->fg_flags & CFS_FG_ALLOC_ATTR) == 0);
4235 4236 error = filegrp_write_metadata(ncp->c_filegrp,
4236 4237 &ncp->c_id, &ncp->c_metadata);
4237 4238 if (error) {
4238 4239 mutex_exit(&ncp->c_statelock);
4239 4240 goto out;
4240 4241 }
4241 4242
4242 4243 /* log the operation */
4243 4244 commit = cachefs_dlog_create(fscp, dcp, nm, vap, exclusive,
4244 4245 mode, ncp, 0, cr);
4245 4246 if (commit == 0) {
4246 4247 mutex_exit(&ncp->c_statelock);
4247 4248 error = ENOSPC;
4248 4249 goto out;
4249 4250 }
4250 4251
4251 4252 mutex_exit(&ncp->c_statelock);
4252 4253
4253 4254 mutex_enter(&dcp->c_statelock);
4254 4255
4255 4256 /* update parent dir times */
4256 4257 dcp->c_metadata.md_localmtime = current_time;
4257 4258 dcp->c_metadata.md_flags |= MD_LOCALMTIME;
4258 4259 dcp->c_flags |= CN_UPDATED;
4259 4260
4260 4261 /* enter new file name in the parent directory */
4261 4262 if (dcp->c_metadata.md_flags & MD_POPULATED) {
4262 4263 error = cachefs_dir_enter(dcp, nm, &ncp->c_cookie,
4263 4264 &ncp->c_id, 0);
4264 4265 if (error) {
4265 4266 cachefs_nocache(dcp);
4266 4267 mutex_exit(&dcp->c_statelock);
4267 4268 error = ETIMEDOUT;
4268 4269 goto out;
4269 4270 }
4270 4271 dnlc_enter(dvp, nm, CTOV(ncp));
4271 4272 } else {
4272 4273 mutex_exit(&dcp->c_statelock);
4273 4274 error = ETIMEDOUT;
4274 4275 goto out;
4275 4276 }
4276 4277 mutex_exit(&dcp->c_statelock);
4277 4278
4278 4279 out:
4279 4280 rw_exit(&dcp->c_rwlock);
4280 4281
4281 4282 if (commit) {
4282 4283 if (cachefs_dlog_commit(fscp, commit, error)) {
4283 4284 /*EMPTY*/
4284 4285 /* XXX bob: fix on panic */
4285 4286 }
4286 4287 }
4287 4288 if (error) {
4288 4289 /* destroy the cnode we created */
4289 4290 if (ncp) {
4290 4291 mutex_enter(&ncp->c_statelock);
4291 4292 ncp->c_flags |= CN_DESTROY;
4292 4293 mutex_exit(&ncp->c_statelock);
4293 4294 VN_RELE(CTOV(ncp));
4294 4295 }
4295 4296 } else {
4296 4297 *vpp = CTOV(ncp);
4297 4298 }
4298 4299 return (error);
4299 4300 }
4300 4301
4301 4302 /*ARGSUSED*/
4302 4303 static int
4303 4304 cachefs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
4304 4305 int flags)
4305 4306 {
4306 4307 cnode_t *dcp = VTOC(dvp);
4307 4308 fscache_t *fscp = C_TO_FSCACHE(dcp);
4308 4309 cachefscache_t *cachep = fscp->fs_cache;
4309 4310 int error = 0;
4310 4311 int held = 0;
4311 4312 int connected = 0;
4312 4313 size_t namlen;
4313 4314 vnode_t *vp = NULL;
4314 4315 int vfslock = 0;
4315 4316
4316 4317 #ifdef CFSDEBUG
4317 4318 CFS_DEBUG(CFSDEBUG_VOPS)
4318 4319 printf("cachefs_remove: ENTER dvp %p name %s\n",
4319 4320 (void *)dvp, nm);
4320 4321 #endif
4321 4322 if (getzoneid() != GLOBAL_ZONEID) {
4322 4323 error = EPERM;
4323 4324 goto out;
4324 4325 }
4325 4326
4326 4327 if (fscp->fs_cache->c_flags & (CACHE_NOFILL | CACHE_NOCACHE))
4327 4328 ASSERT(dcp->c_flags & CN_NOCACHE);
4328 4329
4329 4330 /*
4330 4331 * Cachefs only provides pass-through support for NFSv4,
4331 4332 * and all vnode operations are passed through to the
4332 4333 * back file system. For NFSv4 pass-through to work, only
4333 4334 * connected operation is supported, the cnode backvp must
4334 4335 * exist, and cachefs optional (eg., disconnectable) flags
4335 4336 * are turned off. Assert these conditions to ensure that
4336 4337 * the backfilesystem is called for the remove operation.
4337 4338 */
4338 4339 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
4339 4340 CFS_BACKFS_NFSV4_ASSERT_CNODE(dcp);
4340 4341
4341 4342 for (;;) {
4342 4343 if (vfslock) {
4343 4344 vn_vfsunlock(vp);
4344 4345 vfslock = 0;
4345 4346 }
4346 4347 if (vp) {
4347 4348 VN_RELE(vp);
4348 4349 vp = NULL;
4349 4350 }
4350 4351
4351 4352 /* get (or renew) access to the file system */
4352 4353 if (held) {
4353 4354 /* Won't loop with NFSv4 connected behavior */
4354 4355 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
4355 4356 cachefs_cd_release(fscp);
4356 4357 held = 0;
4357 4358 }
4358 4359 error = cachefs_cd_access(fscp, connected, 1);
4359 4360 if (error)
4360 4361 break;
4361 4362 held = 1;
4362 4363
4363 4364 /* if disconnected, do some extra error checking */
4364 4365 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
4365 4366 /* check permissions */
4366 4367 mutex_enter(&dcp->c_statelock);
4367 4368 error = cachefs_access_local(dcp, (VEXEC|VWRITE), cr);
4368 4369 mutex_exit(&dcp->c_statelock);
4369 4370 if (CFS_TIMEOUT(fscp, error)) {
4370 4371 connected = 1;
4371 4372 continue;
4372 4373 }
4373 4374 if (error)
4374 4375 break;
4375 4376
4376 4377 namlen = strlen(nm);
4377 4378 if (namlen == 0) {
4378 4379 error = EINVAL;
4379 4380 break;
4380 4381 }
4381 4382
4382 4383 /* cannot remove . and .. */
4383 4384 if (nm[0] == '.') {
4384 4385 if (namlen == 1) {
4385 4386 error = EINVAL;
4386 4387 break;
4387 4388 } else if (namlen == 2 && nm[1] == '.') {
4388 4389 error = EEXIST;
4389 4390 break;
4390 4391 }
4391 4392 }
4392 4393
4393 4394 }
4394 4395
4395 4396 /* get the cnode of the file to delete */
4396 4397 error = cachefs_lookup_common(dvp, nm, &vp, NULL, 0, NULL, cr);
4397 4398 if (error) {
4398 4399 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
4399 4400 if (CFS_TIMEOUT(fscp, error)) {
4400 4401 cachefs_cd_release(fscp);
4401 4402 held = 0;
4402 4403 cachefs_cd_timedout(fscp);
4403 4404 connected = 0;
4404 4405 continue;
4405 4406 }
4406 4407 } else {
4407 4408 if (CFS_TIMEOUT(fscp, error)) {
4408 4409 connected = 1;
4409 4410 continue;
4410 4411 }
4411 4412 }
4412 4413 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_REMOVE)) {
4413 4414 struct fid foo;
4414 4415
4415 4416 bzero(&foo, sizeof (foo));
4416 4417 cachefs_log_remove(cachep, error,
4417 4418 fscp->fs_cfsvfsp, &foo, 0, crgetuid(cr));
4418 4419 }
4419 4420 break;
4420 4421 }
4421 4422
4422 4423 if (vp->v_type == VDIR) {
4423 4424 /* must be privileged to remove dirs with unlink() */
4424 4425 if ((error = secpolicy_fs_linkdir(cr, vp->v_vfsp)) != 0)
4425 4426 break;
4426 4427
4427 4428 /* see ufs_dirremove for why this is done, mount race */
4428 4429 if (vn_vfswlock(vp)) {
4429 4430 error = EBUSY;
4430 4431 break;
4431 4432 }
4432 4433 vfslock = 1;
4433 4434 if (vn_mountedvfs(vp) != NULL) {
4434 4435 error = EBUSY;
4435 4436 break;
4436 4437 }
4437 4438 }
4438 4439
4439 4440 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
4440 4441 error = cachefs_remove_connected(dvp, nm, cr, vp);
4441 4442 if (CFS_TIMEOUT(fscp, error)) {
4442 4443 cachefs_cd_release(fscp);
4443 4444 held = 0;
4444 4445 cachefs_cd_timedout(fscp);
4445 4446 connected = 0;
4446 4447 continue;
4447 4448 }
4448 4449 } else {
4449 4450 error = cachefs_remove_disconnected(dvp, nm, cr,
4450 4451 vp);
4451 4452 if (CFS_TIMEOUT(fscp, error)) {
4452 4453 connected = 1;
4453 4454 continue;
4454 4455 }
4455 4456 }
4456 4457 break;
4457 4458 }
4458 4459
4459 4460 #if 0
4460 4461 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_REMOVE))
4461 4462 cachefs_log_remove(cachep, error, fscp->fs_cfsvfsp,
4462 4463 &cp->c_metadata.md_cookie, cp->c_id.cid_fileno,
4463 4464 crgetuid(cr));
4464 4465 #endif
4465 4466
4466 4467 if (held)
4467 4468 cachefs_cd_release(fscp);
4468 4469
4469 4470 if (vfslock)
4470 4471 vn_vfsunlock(vp);
4471 4472
4472 4473 if (vp)
4473 4474 VN_RELE(vp);
4474 4475
4475 4476 #ifdef CFS_CD_DEBUG
4476 4477 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
4477 4478 #endif
4478 4479 out:
4479 4480 #ifdef CFSDEBUG
4480 4481 CFS_DEBUG(CFSDEBUG_VOPS)
4481 4482 printf("cachefs_remove: EXIT dvp %p\n", (void *)dvp);
4482 4483 #endif
4483 4484
4484 4485 return (error);
4485 4486 }
4486 4487
4487 4488 int
4488 4489 cachefs_remove_connected(vnode_t *dvp, char *nm, cred_t *cr, vnode_t *vp)
4489 4490 {
4490 4491 cnode_t *dcp = VTOC(dvp);
4491 4492 cnode_t *cp = VTOC(vp);
4492 4493 fscache_t *fscp = C_TO_FSCACHE(dcp);
4493 4494 int error = 0;
4494 4495
4495 4496 /*
4496 4497 * Acquire the rwlock (WRITER) on the directory to prevent other
4497 4498 * activity on the directory.
4498 4499 */
4499 4500 rw_enter(&dcp->c_rwlock, RW_WRITER);
4500 4501
4501 4502 /* purge dnlc of this entry so can get accurate vnode count */
4502 4503 dnlc_purge_vp(vp);
4503 4504
4504 4505 /*
4505 4506 * If the cnode is active, make a link to the file
4506 4507 * so operations on the file will continue.
4507 4508 */
4508 4509 if ((vp->v_type != VDIR) &&
4509 4510 !((vp->v_count == 1) || ((vp->v_count == 2) && cp->c_ipending))) {
4510 4511 error = cachefs_remove_dolink(dvp, vp, nm, cr);
4511 4512 if (error)
4512 4513 goto out;
4513 4514 }
4514 4515
4515 4516 /* else call backfs NFSv4 handler if NFSv4 */
4516 4517 else if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
4517 4518 error = cachefs_remove_backfs_nfsv4(dvp, nm, cr, vp);
4518 4519 goto out;
4519 4520 }
4520 4521
4521 4522 /* else drop the backvp so nfs does not do rename */
4522 4523 else if (cp->c_backvp) {
4523 4524 mutex_enter(&cp->c_statelock);
4524 4525 if (cp->c_backvp) {
4525 4526 VN_RELE(cp->c_backvp);
4526 4527 cp->c_backvp = NULL;
4527 4528 }
4528 4529 mutex_exit(&cp->c_statelock);
4529 4530 }
4530 4531
4531 4532 mutex_enter(&dcp->c_statelock);
4532 4533
4533 4534 /* get the backvp */
4534 4535 if (dcp->c_backvp == NULL) {
4535 4536 error = cachefs_getbackvp(fscp, dcp);
4536 4537 if (error) {
4537 4538 mutex_exit(&dcp->c_statelock);
4538 4539 goto out;
4539 4540 }
4540 4541 }
4541 4542
4542 4543 /* check directory consistency */
4543 4544 error = CFSOP_CHECK_COBJECT(fscp, dcp, 0, cr);
4544 4545 if (error) {
4545 4546 mutex_exit(&dcp->c_statelock);
4546 4547 goto out;
4547 4548 }
4548 4549
4549 4550 /* perform the remove on the back fs */
4550 4551 error = VOP_REMOVE(dcp->c_backvp, nm, cr, NULL, 0);
4551 4552 if (error) {
4552 4553 mutex_exit(&dcp->c_statelock);
4553 4554 goto out;
4554 4555 }
4555 4556
4556 4557 /* the dir has been modified */
4557 4558 CFSOP_MODIFY_COBJECT(fscp, dcp, cr);
4558 4559
4559 4560 /* remove the entry from the populated directory */
4560 4561 if (CFS_ISFS_NONSHARED(fscp) &&
4561 4562 (dcp->c_metadata.md_flags & MD_POPULATED)) {
4562 4563 error = cachefs_dir_rmentry(dcp, nm);
4563 4564 if (error) {
4564 4565 cachefs_nocache(dcp);
4565 4566 error = 0;
4566 4567 }
4567 4568 }
4568 4569 mutex_exit(&dcp->c_statelock);
4569 4570
4570 4571 /* fix up the file we deleted */
4571 4572 mutex_enter(&cp->c_statelock);
4572 4573 if (cp->c_attr.va_nlink == 1)
4573 4574 cp->c_flags |= CN_DESTROY;
4574 4575 else
4575 4576 cp->c_flags |= CN_UPDATED;
4576 4577
4577 4578 cp->c_attr.va_nlink--;
4578 4579 CFSOP_MODIFY_COBJECT(fscp, cp, cr);
4579 4580 mutex_exit(&cp->c_statelock);
4580 4581
4581 4582 out:
4582 4583 rw_exit(&dcp->c_rwlock);
4583 4584 return (error);
4584 4585 }
4585 4586
4586 4587 /*
4587 4588 * cachefs_remove_backfs_nfsv4
4588 4589 *
4589 4590 * Call NFSv4 back filesystem to handle the remove (cachefs
4590 4591 * pass-through support for NFSv4).
4591 4592 */
4592 4593 int
4593 4594 cachefs_remove_backfs_nfsv4(vnode_t *dvp, char *nm, cred_t *cr, vnode_t *vp)
4594 4595 {
4595 4596 cnode_t *dcp = VTOC(dvp);
4596 4597 cnode_t *cp = VTOC(vp);
4597 4598 vnode_t *dbackvp;
4598 4599 fscache_t *fscp = C_TO_FSCACHE(dcp);
4599 4600 int error = 0;
4600 4601
4601 4602 /*
4602 4603 * For NFSv4 pass-through to work, only connected operation
4603 4604 * is supported, the cnode backvp must exist, and cachefs
4604 4605 * optional (eg., disconnectable) flags are turned off. Assert
4605 4606 * these conditions for the getattr operation.
4606 4607 */
4607 4608 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
4608 4609 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
4609 4610
4610 4611 /* Should hold the directory readwrite lock to update directory */
4611 4612 ASSERT(RW_WRITE_HELD(&dcp->c_rwlock));
4612 4613
4613 4614 /*
4614 4615 * Update attributes for directory. Note that
4615 4616 * CFSOP_CHECK_COBJECT asserts for c_statelock being
4616 4617 * held, so grab it before calling the routine.
4617 4618 */
4618 4619 mutex_enter(&dcp->c_statelock);
4619 4620 error = CFSOP_CHECK_COBJECT(fscp, dcp, 0, cr);
4620 4621 mutex_exit(&dcp->c_statelock);
4621 4622 if (error)
4622 4623 goto out;
4623 4624
4624 4625 /*
4625 4626 * Update attributes for cp. Note that CFSOP_CHECK_COBJECT
4626 4627 * asserts for c_statelock being held, so grab it before
4627 4628 * calling the routine.
4628 4629 */
4629 4630 mutex_enter(&cp->c_statelock);
4630 4631 error = CFSOP_CHECK_COBJECT(fscp, cp, 0, cr);
4631 4632 if (error) {
4632 4633 mutex_exit(&cp->c_statelock);
4633 4634 goto out;
4634 4635 }
4635 4636
4636 4637 /*
4637 4638 * Drop the backvp so nfs if the link count is 1 so that
4638 4639 * nfs does not do rename. Ensure that we will destroy the cnode
4639 4640 * since this cnode no longer contains the backvp. Note that we
4640 4641 * maintain lock on this cnode to prevent change till the remove
4641 4642 * completes, otherwise other operations will encounter an ESTALE
4642 4643 * if they try to use the cnode with CN_DESTROY set (see
4643 4644 * cachefs_get_backvp()), or change the state of the cnode
4644 4645 * while we're removing it.
4645 4646 */
4646 4647 if (cp->c_attr.va_nlink == 1) {
4647 4648 /*
4648 4649 * The unldvp information is created for the case
4649 4650 * when there is more than one reference on the
4650 4651 * vnode when a remove operation is called. If the
4651 4652 * remove itself was holding a reference to the
4652 4653 * vnode, then a subsequent remove will remove the
4653 4654 * backvp, so we need to get rid of the unldvp
4654 4655 * before removing the backvp. An alternate would
4655 4656 * be to simply ignore the remove and let the
4656 4657 * inactivation routine do the deletion of the
4657 4658 * unldvp.
4658 4659 */
4659 4660 if (cp->c_unldvp) {
4660 4661 VN_RELE(cp->c_unldvp);
4661 4662 cachefs_kmem_free(cp->c_unlname, MAXNAMELEN);
4662 4663 crfree(cp->c_unlcred);
4663 4664 cp->c_unldvp = NULL;
4664 4665 cp->c_unlcred = NULL;
4665 4666 }
4666 4667 cp->c_flags |= CN_DESTROY;
4667 4668 cp->c_attr.va_nlink = 0;
4668 4669 VN_RELE(cp->c_backvp);
4669 4670 cp->c_backvp = NULL;
4670 4671 }
4671 4672
4672 4673 /* perform the remove on back fs after extracting directory backvp */
4673 4674 mutex_enter(&dcp->c_statelock);
4674 4675 dbackvp = dcp->c_backvp;
4675 4676 mutex_exit(&dcp->c_statelock);
4676 4677
4677 4678 CFS_DPRINT_BACKFS_NFSV4(fscp,
4678 4679 ("cachefs_remove (nfsv4): dcp %p, dbackvp %p, name %s\n",
4679 4680 dcp, dbackvp, nm));
4680 4681 error = VOP_REMOVE(dbackvp, nm, cr, NULL, 0);
4681 4682 if (error) {
4682 4683 mutex_exit(&cp->c_statelock);
4683 4684 goto out;
4684 4685 }
4685 4686
4686 4687 /* fix up the file we deleted, if not destroying the cnode */
4687 4688 if ((cp->c_flags & CN_DESTROY) == 0) {
4688 4689 cp->c_attr.va_nlink--;
4689 4690 cp->c_flags |= CN_UPDATED;
4690 4691 }
4691 4692
4692 4693 mutex_exit(&cp->c_statelock);
4693 4694
4694 4695 out:
4695 4696 return (error);
4696 4697 }
4697 4698
4698 4699 int
4699 4700 cachefs_remove_disconnected(vnode_t *dvp, char *nm, cred_t *cr,
4700 4701 vnode_t *vp)
4701 4702 {
4702 4703 cnode_t *dcp = VTOC(dvp);
4703 4704 cnode_t *cp = VTOC(vp);
4704 4705 fscache_t *fscp = C_TO_FSCACHE(dcp);
4705 4706 int error = 0;
4706 4707 off_t commit = 0;
4707 4708 timestruc_t current_time;
4708 4709
4709 4710 if (CFS_ISFS_WRITE_AROUND(fscp))
4710 4711 return (ETIMEDOUT);
4711 4712
4712 4713 if (cp->c_metadata.md_flags & MD_NEEDATTRS)
4713 4714 return (ETIMEDOUT);
4714 4715
4715 4716 /*
4716 4717 * Acquire the rwlock (WRITER) on the directory to prevent other
4717 4718 * activity on the directory.
4718 4719 */
4719 4720 rw_enter(&dcp->c_rwlock, RW_WRITER);
4720 4721
4721 4722 /* dir must be populated */
4722 4723 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
4723 4724 error = ETIMEDOUT;
4724 4725 goto out;
4725 4726 }
4726 4727
4727 4728 mutex_enter(&dcp->c_statelock);
4728 4729 mutex_enter(&cp->c_statelock);
4729 4730
4730 4731 error = cachefs_stickyrmchk(dcp, cp, cr);
4731 4732
4732 4733 mutex_exit(&cp->c_statelock);
4733 4734 mutex_exit(&dcp->c_statelock);
4734 4735 if (error)
4735 4736 goto out;
4736 4737
4737 4738 /* purge dnlc of this entry so can get accurate vnode count */
4738 4739 dnlc_purge_vp(vp);
4739 4740
4740 4741 /*
4741 4742 * If the cnode is active, make a link to the file
4742 4743 * so operations on the file will continue.
4743 4744 */
4744 4745 if ((vp->v_type != VDIR) &&
4745 4746 !((vp->v_count == 1) || ((vp->v_count == 2) && cp->c_ipending))) {
4746 4747 error = cachefs_remove_dolink(dvp, vp, nm, cr);
4747 4748 if (error)
4748 4749 goto out;
4749 4750 }
4750 4751
4751 4752 if (cp->c_attr.va_nlink > 1) {
4752 4753 mutex_enter(&cp->c_statelock);
4753 4754 if (cachefs_modified_alloc(cp)) {
4754 4755 mutex_exit(&cp->c_statelock);
4755 4756 error = ENOSPC;
4756 4757 goto out;
4757 4758 }
4758 4759 if ((cp->c_metadata.md_flags & MD_MAPPING) == 0) {
4759 4760 error = cachefs_dlog_cidmap(fscp);
4760 4761 if (error) {
4761 4762 mutex_exit(&cp->c_statelock);
4762 4763 error = ENOSPC;
4763 4764 goto out;
4764 4765 }
4765 4766 cp->c_metadata.md_flags |= MD_MAPPING;
4766 4767 cp->c_flags |= CN_UPDATED;
4767 4768 }
4768 4769 mutex_exit(&cp->c_statelock);
4769 4770 }
4770 4771
4771 4772 /* log the remove */
4772 4773 commit = cachefs_dlog_remove(fscp, dcp, nm, cp, cr);
4773 4774 if (commit == 0) {
4774 4775 error = ENOSPC;
4775 4776 goto out;
4776 4777 }
4777 4778
4778 4779 /* remove the file from the dir */
4779 4780 mutex_enter(&dcp->c_statelock);
4780 4781 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
4781 4782 mutex_exit(&dcp->c_statelock);
4782 4783 error = ETIMEDOUT;
4783 4784 goto out;
4784 4785
4785 4786 }
4786 4787 cachefs_modified(dcp);
4787 4788 error = cachefs_dir_rmentry(dcp, nm);
4788 4789 if (error) {
4789 4790 mutex_exit(&dcp->c_statelock);
4790 4791 if (error == ENOTDIR)
4791 4792 error = ETIMEDOUT;
4792 4793 goto out;
4793 4794 }
4794 4795
4795 4796 /* update parent dir times */
4796 4797 gethrestime(¤t_time);
4797 4798 dcp->c_metadata.md_localctime = current_time;
4798 4799 dcp->c_metadata.md_localmtime = current_time;
4799 4800 dcp->c_metadata.md_flags |= MD_LOCALCTIME | MD_LOCALMTIME;
4800 4801 dcp->c_flags |= CN_UPDATED;
4801 4802 mutex_exit(&dcp->c_statelock);
4802 4803
4803 4804 /* adjust file we are deleting */
4804 4805 mutex_enter(&cp->c_statelock);
4805 4806 cp->c_attr.va_nlink--;
4806 4807 cp->c_metadata.md_localctime = current_time;
4807 4808 cp->c_metadata.md_flags |= MD_LOCALCTIME;
4808 4809 if (cp->c_attr.va_nlink == 0) {
4809 4810 cp->c_flags |= CN_DESTROY;
4810 4811 } else {
4811 4812 cp->c_flags |= CN_UPDATED;
4812 4813 }
4813 4814 mutex_exit(&cp->c_statelock);
4814 4815
4815 4816 out:
4816 4817 if (commit) {
4817 4818 /* commit the log entry */
4818 4819 if (cachefs_dlog_commit(fscp, commit, error)) {
4819 4820 /*EMPTY*/
4820 4821 /* XXX bob: fix on panic */
4821 4822 }
4822 4823 }
4823 4824
4824 4825 rw_exit(&dcp->c_rwlock);
4825 4826 return (error);
4826 4827 }
4827 4828
4828 4829 /*ARGSUSED*/
4829 4830 static int
4830 4831 cachefs_link(vnode_t *tdvp, vnode_t *fvp, char *tnm, cred_t *cr,
4831 4832 caller_context_t *ct, int flags)
4832 4833 {
4833 4834 fscache_t *fscp = VFS_TO_FSCACHE(tdvp->v_vfsp);
4834 4835 cnode_t *tdcp = VTOC(tdvp);
4835 4836 struct vnode *realvp;
4836 4837 int error = 0;
4837 4838 int held = 0;
4838 4839 int connected = 0;
4839 4840
4840 4841 #ifdef CFSDEBUG
4841 4842 CFS_DEBUG(CFSDEBUG_VOPS)
4842 4843 printf("cachefs_link: ENTER fvp %p tdvp %p tnm %s\n",
4843 4844 (void *)fvp, (void *)tdvp, tnm);
4844 4845 #endif
4845 4846
4846 4847 if (getzoneid() != GLOBAL_ZONEID) {
4847 4848 error = EPERM;
4848 4849 goto out;
4849 4850 }
4850 4851
4851 4852 if (fscp->fs_cache->c_flags & (CACHE_NOFILL | CACHE_NOCACHE))
4852 4853 ASSERT(tdcp->c_flags & CN_NOCACHE);
4853 4854
4854 4855 if (VOP_REALVP(fvp, &realvp, ct) == 0) {
4855 4856 fvp = realvp;
4856 4857 }
4857 4858
4858 4859 /*
4859 4860 * Cachefs only provides pass-through support for NFSv4,
4860 4861 * and all vnode operations are passed through to the
4861 4862 * back file system. For NFSv4 pass-through to work, only
4862 4863 * connected operation is supported, the cnode backvp must
4863 4864 * exist, and cachefs optional (eg., disconnectable) flags
4864 4865 * are turned off. Assert these conditions to ensure that
4865 4866 * the backfilesystem is called for the link operation.
4866 4867 */
4867 4868
4868 4869 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
4869 4870 CFS_BACKFS_NFSV4_ASSERT_CNODE(tdcp);
4870 4871
4871 4872 for (;;) {
4872 4873 /* get (or renew) access to the file system */
4873 4874 if (held) {
4874 4875 /* Won't loop with NFSv4 connected behavior */
4875 4876 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
4876 4877 rw_exit(&tdcp->c_rwlock);
4877 4878 cachefs_cd_release(fscp);
4878 4879 held = 0;
4879 4880 }
4880 4881 error = cachefs_cd_access(fscp, connected, 1);
4881 4882 if (error)
4882 4883 break;
4883 4884 rw_enter(&tdcp->c_rwlock, RW_WRITER);
4884 4885 held = 1;
4885 4886
4886 4887 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
4887 4888 error = cachefs_link_connected(tdvp, fvp, tnm, cr);
4888 4889 if (CFS_TIMEOUT(fscp, error)) {
4889 4890 rw_exit(&tdcp->c_rwlock);
4890 4891 cachefs_cd_release(fscp);
4891 4892 held = 0;
4892 4893 cachefs_cd_timedout(fscp);
4893 4894 connected = 0;
4894 4895 continue;
4895 4896 }
4896 4897 } else {
4897 4898 error = cachefs_link_disconnected(tdvp, fvp, tnm,
4898 4899 cr);
4899 4900 if (CFS_TIMEOUT(fscp, error)) {
4900 4901 connected = 1;
4901 4902 continue;
4902 4903 }
4903 4904 }
4904 4905 break;
4905 4906 }
4906 4907
4907 4908 if (held) {
4908 4909 rw_exit(&tdcp->c_rwlock);
4909 4910 cachefs_cd_release(fscp);
4910 4911 }
4911 4912
4912 4913 #ifdef CFS_CD_DEBUG
4913 4914 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
4914 4915 #endif
4915 4916 out:
4916 4917 #ifdef CFSDEBUG
4917 4918 CFS_DEBUG(CFSDEBUG_VOPS)
4918 4919 printf("cachefs_link: EXIT fvp %p tdvp %p tnm %s\n",
4919 4920 (void *)fvp, (void *)tdvp, tnm);
4920 4921 #endif
4921 4922 return (error);
4922 4923 }
4923 4924
4924 4925 static int
4925 4926 cachefs_link_connected(vnode_t *tdvp, vnode_t *fvp, char *tnm, cred_t *cr)
4926 4927 {
4927 4928 cnode_t *tdcp = VTOC(tdvp);
4928 4929 cnode_t *fcp = VTOC(fvp);
4929 4930 fscache_t *fscp = VFS_TO_FSCACHE(tdvp->v_vfsp);
4930 4931 int error = 0;
4931 4932 vnode_t *backvp = NULL;
4932 4933
4933 4934 if (tdcp != fcp) {
4934 4935 mutex_enter(&fcp->c_statelock);
4935 4936
4936 4937 if (fcp->c_backvp == NULL) {
4937 4938 error = cachefs_getbackvp(fscp, fcp);
4938 4939 if (error) {
4939 4940 mutex_exit(&fcp->c_statelock);
4940 4941 goto out;
4941 4942 }
4942 4943 }
4943 4944
4944 4945 error = CFSOP_CHECK_COBJECT(fscp, fcp, 0, cr);
4945 4946 if (error) {
4946 4947 mutex_exit(&fcp->c_statelock);
4947 4948 goto out;
4948 4949 }
4949 4950 backvp = fcp->c_backvp;
4950 4951 VN_HOLD(backvp);
4951 4952 mutex_exit(&fcp->c_statelock);
4952 4953 }
4953 4954
4954 4955 mutex_enter(&tdcp->c_statelock);
4955 4956
4956 4957 /* get backvp of target directory */
4957 4958 if (tdcp->c_backvp == NULL) {
4958 4959 error = cachefs_getbackvp(fscp, tdcp);
4959 4960 if (error) {
4960 4961 mutex_exit(&tdcp->c_statelock);
4961 4962 goto out;
4962 4963 }
4963 4964 }
4964 4965
4965 4966 /* consistency check target directory */
4966 4967 error = CFSOP_CHECK_COBJECT(fscp, tdcp, 0, cr);
4967 4968 if (error) {
4968 4969 mutex_exit(&tdcp->c_statelock);
4969 4970 goto out;
4970 4971 }
4971 4972 if (backvp == NULL) {
4972 4973 backvp = tdcp->c_backvp;
4973 4974 VN_HOLD(backvp);
4974 4975 }
4975 4976
4976 4977 /* perform the link on the back fs */
4977 4978 CFS_DPRINT_BACKFS_NFSV4(fscp,
4978 4979 ("cachefs_link (nfsv4): tdcp %p, tdbackvp %p, "
4979 4980 "name %s\n", tdcp, tdcp->c_backvp, tnm));
4980 4981 error = VOP_LINK(tdcp->c_backvp, backvp, tnm, cr, NULL, 0);
4981 4982 if (error) {
4982 4983 mutex_exit(&tdcp->c_statelock);
4983 4984 goto out;
4984 4985 }
4985 4986
4986 4987 CFSOP_MODIFY_COBJECT(fscp, tdcp, cr);
4987 4988
4988 4989 /* if the dir is populated, add the new link */
4989 4990 if (CFS_ISFS_NONSHARED(fscp) &&
4990 4991 (tdcp->c_metadata.md_flags & MD_POPULATED)) {
4991 4992 error = cachefs_dir_enter(tdcp, tnm, &fcp->c_cookie,
4992 4993 &fcp->c_id, SM_ASYNC);
4993 4994 if (error) {
4994 4995 cachefs_nocache(tdcp);
4995 4996 error = 0;
4996 4997 }
4997 4998 }
4998 4999 mutex_exit(&tdcp->c_statelock);
4999 5000
5000 5001 /* get the new link count on the file */
5001 5002 mutex_enter(&fcp->c_statelock);
5002 5003 fcp->c_flags |= CN_UPDATED;
5003 5004 CFSOP_MODIFY_COBJECT(fscp, fcp, cr);
5004 5005 if (fcp->c_backvp == NULL) {
5005 5006 error = cachefs_getbackvp(fscp, fcp);
5006 5007 if (error) {
5007 5008 mutex_exit(&fcp->c_statelock);
5008 5009 goto out;
5009 5010 }
5010 5011 }
5011 5012
5012 5013 /* XXX bob: given what modify_cobject does this seems unnecessary */
5013 5014 fcp->c_attr.va_mask = AT_ALL;
5014 5015 error = VOP_GETATTR(fcp->c_backvp, &fcp->c_attr, 0, cr, NULL);
5015 5016 mutex_exit(&fcp->c_statelock);
5016 5017 out:
5017 5018 if (backvp)
5018 5019 VN_RELE(backvp);
5019 5020
5020 5021 return (error);
5021 5022 }
5022 5023
5023 5024 static int
5024 5025 cachefs_link_disconnected(vnode_t *tdvp, vnode_t *fvp, char *tnm,
5025 5026 cred_t *cr)
5026 5027 {
5027 5028 cnode_t *tdcp = VTOC(tdvp);
5028 5029 cnode_t *fcp = VTOC(fvp);
5029 5030 fscache_t *fscp = VFS_TO_FSCACHE(tdvp->v_vfsp);
5030 5031 int error = 0;
5031 5032 timestruc_t current_time;
5032 5033 off_t commit = 0;
5033 5034
5034 5035 if (fvp->v_type == VDIR && secpolicy_fs_linkdir(cr, fvp->v_vfsp) != 0 ||
5035 5036 fcp->c_attr.va_uid != crgetuid(cr) && secpolicy_basic_link(cr) != 0)
5036 5037 return (EPERM);
5037 5038
5038 5039 if (CFS_ISFS_WRITE_AROUND(fscp))
5039 5040 return (ETIMEDOUT);
5040 5041
5041 5042 if (fcp->c_metadata.md_flags & MD_NEEDATTRS)
5042 5043 return (ETIMEDOUT);
5043 5044
5044 5045 mutex_enter(&tdcp->c_statelock);
5045 5046
5046 5047 /* check permissions */
5047 5048 if (error = cachefs_access_local(tdcp, (VEXEC|VWRITE), cr)) {
5048 5049 mutex_exit(&tdcp->c_statelock);
5049 5050 goto out;
5050 5051 }
5051 5052
5052 5053 /* the directory front file must be populated */
5053 5054 if ((tdcp->c_metadata.md_flags & MD_POPULATED) == 0) {
5054 5055 error = ETIMEDOUT;
5055 5056 mutex_exit(&tdcp->c_statelock);
5056 5057 goto out;
5057 5058 }
5058 5059
5059 5060 /* make sure tnm does not already exist in the directory */
5060 5061 error = cachefs_dir_look(tdcp, tnm, NULL, NULL, NULL, NULL);
5061 5062 if (error == ENOTDIR) {
5062 5063 error = ETIMEDOUT;
5063 5064 mutex_exit(&tdcp->c_statelock);
5064 5065 goto out;
5065 5066 }
5066 5067 if (error != ENOENT) {
5067 5068 error = EEXIST;
5068 5069 mutex_exit(&tdcp->c_statelock);
5069 5070 goto out;
5070 5071 }
5071 5072
5072 5073 mutex_enter(&fcp->c_statelock);
5073 5074
5074 5075 /* create a mapping for the file if necessary */
5075 5076 if ((fcp->c_metadata.md_flags & MD_MAPPING) == 0) {
5076 5077 error = cachefs_dlog_cidmap(fscp);
5077 5078 if (error) {
5078 5079 mutex_exit(&fcp->c_statelock);
5079 5080 mutex_exit(&tdcp->c_statelock);
5080 5081 error = ENOSPC;
5081 5082 goto out;
5082 5083 }
5083 5084 fcp->c_metadata.md_flags |= MD_MAPPING;
5084 5085 fcp->c_flags |= CN_UPDATED;
5085 5086 }
5086 5087
5087 5088 /* mark file as modified */
5088 5089 if (cachefs_modified_alloc(fcp)) {
5089 5090 mutex_exit(&fcp->c_statelock);
5090 5091 mutex_exit(&tdcp->c_statelock);
5091 5092 error = ENOSPC;
5092 5093 goto out;
5093 5094 }
5094 5095 mutex_exit(&fcp->c_statelock);
5095 5096
5096 5097 /* log the operation */
5097 5098 commit = cachefs_dlog_link(fscp, tdcp, tnm, fcp, cr);
5098 5099 if (commit == 0) {
5099 5100 mutex_exit(&tdcp->c_statelock);
5100 5101 error = ENOSPC;
5101 5102 goto out;
5102 5103 }
5103 5104
5104 5105 gethrestime(¤t_time);
5105 5106
5106 5107 /* make the new link */
5107 5108 cachefs_modified(tdcp);
5108 5109 error = cachefs_dir_enter(tdcp, tnm, &fcp->c_cookie,
5109 5110 &fcp->c_id, SM_ASYNC);
5110 5111 if (error) {
5111 5112 error = 0;
5112 5113 mutex_exit(&tdcp->c_statelock);
5113 5114 goto out;
5114 5115 }
5115 5116
5116 5117 /* Update mtime/ctime of parent dir */
5117 5118 tdcp->c_metadata.md_localmtime = current_time;
5118 5119 tdcp->c_metadata.md_localctime = current_time;
5119 5120 tdcp->c_metadata.md_flags |= MD_LOCALCTIME | MD_LOCALMTIME;
5120 5121 tdcp->c_flags |= CN_UPDATED;
5121 5122 mutex_exit(&tdcp->c_statelock);
5122 5123
5123 5124 /* update the file we linked to */
5124 5125 mutex_enter(&fcp->c_statelock);
5125 5126 fcp->c_attr.va_nlink++;
5126 5127 fcp->c_metadata.md_localctime = current_time;
5127 5128 fcp->c_metadata.md_flags |= MD_LOCALCTIME;
5128 5129 fcp->c_flags |= CN_UPDATED;
5129 5130 mutex_exit(&fcp->c_statelock);
5130 5131
5131 5132 out:
5132 5133 if (commit) {
5133 5134 /* commit the log entry */
5134 5135 if (cachefs_dlog_commit(fscp, commit, error)) {
5135 5136 /*EMPTY*/
5136 5137 /* XXX bob: fix on panic */
5137 5138 }
5138 5139 }
5139 5140
5140 5141 return (error);
5141 5142 }
5142 5143
5143 5144 /*
5144 5145 * Serialize all renames in CFS, to avoid deadlocks - We have to hold two
5145 5146 * cnodes atomically.
5146 5147 */
5147 5148 kmutex_t cachefs_rename_lock;
5148 5149
5149 5150 /*ARGSUSED*/
5150 5151 static int
5151 5152 cachefs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp,
5152 5153 char *nnm, cred_t *cr, caller_context_t *ct, int flags)
5153 5154 {
5154 5155 fscache_t *fscp = C_TO_FSCACHE(VTOC(odvp));
5155 5156 cachefscache_t *cachep = fscp->fs_cache;
5156 5157 int error = 0;
5157 5158 int held = 0;
5158 5159 int connected = 0;
5159 5160 vnode_t *delvp = NULL;
5160 5161 vnode_t *tvp = NULL;
5161 5162 int vfslock = 0;
5162 5163 struct vnode *realvp;
5163 5164
5164 5165 if (getzoneid() != GLOBAL_ZONEID)
5165 5166 return (EPERM);
5166 5167
5167 5168 if (VOP_REALVP(ndvp, &realvp, ct) == 0)
5168 5169 ndvp = realvp;
5169 5170
5170 5171 /*
5171 5172 * if the fs NOFILL or NOCACHE flags are on, then the old and new
5172 5173 * directory cnodes better indicate NOCACHE mode as well.
5173 5174 */
5174 5175 ASSERT(
5175 5176 (fscp->fs_cache->c_flags & (CACHE_NOFILL | CACHE_NOCACHE)) == 0 ||
5176 5177 ((VTOC(odvp)->c_flags & CN_NOCACHE) &&
5177 5178 (VTOC(ndvp)->c_flags & CN_NOCACHE)));
5178 5179
5179 5180 /*
5180 5181 * Cachefs only provides pass-through support for NFSv4,
5181 5182 * and all vnode operations are passed through to the
5182 5183 * back file system. For NFSv4 pass-through to work, only
5183 5184 * connected operation is supported, the cnode backvp must
5184 5185 * exist, and cachefs optional (eg., disconnectable) flags
5185 5186 * are turned off. Assert these conditions to ensure that
5186 5187 * the backfilesystem is called for the rename operation.
5187 5188 */
5188 5189 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
5189 5190 CFS_BACKFS_NFSV4_ASSERT_CNODE(VTOC(odvp));
5190 5191 CFS_BACKFS_NFSV4_ASSERT_CNODE(VTOC(ndvp));
5191 5192
5192 5193 for (;;) {
5193 5194 if (vfslock) {
5194 5195 vn_vfsunlock(delvp);
5195 5196 vfslock = 0;
5196 5197 }
5197 5198 if (delvp) {
5198 5199 VN_RELE(delvp);
5199 5200 delvp = NULL;
5200 5201 }
5201 5202
5202 5203 /* get (or renew) access to the file system */
5203 5204 if (held) {
5204 5205 /* Won't loop for NFSv4 connected support */
5205 5206 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
5206 5207 cachefs_cd_release(fscp);
5207 5208 held = 0;
5208 5209 }
5209 5210 error = cachefs_cd_access(fscp, connected, 1);
5210 5211 if (error)
5211 5212 break;
5212 5213 held = 1;
5213 5214
5214 5215 /* sanity check */
5215 5216 if ((odvp->v_type != VDIR) || (ndvp->v_type != VDIR)) {
5216 5217 error = EINVAL;
5217 5218 break;
5218 5219 }
5219 5220
5220 5221 /* cannot rename from or to . or .. */
5221 5222 if (strcmp(onm, ".") == 0 || strcmp(onm, "..") == 0 ||
5222 5223 strcmp(nnm, ".") == 0 || strcmp(nnm, "..") == 0) {
5223 5224 error = EINVAL;
5224 5225 break;
5225 5226 }
5226 5227
5227 5228 if (odvp != ndvp) {
5228 5229 /*
5229 5230 * if moving a directory, its notion
5230 5231 * of ".." will change
5231 5232 */
5232 5233 error = cachefs_lookup_common(odvp, onm, &tvp,
5233 5234 NULL, 0, NULL, cr);
5234 5235 if (error == 0) {
5235 5236 ASSERT(tvp != NULL);
5236 5237 if (tvp->v_type == VDIR) {
5237 5238 cnode_t *cp = VTOC(tvp);
5238 5239
5239 5240 dnlc_remove(tvp, "..");
5240 5241
5241 5242 mutex_enter(&cp->c_statelock);
5242 5243 CFSOP_MODIFY_COBJECT(fscp, cp, cr);
5243 5244 mutex_exit(&cp->c_statelock);
5244 5245 }
5245 5246 } else {
5246 5247 tvp = NULL;
5247 5248 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
5248 5249 if (CFS_TIMEOUT(fscp, error)) {
5249 5250 cachefs_cd_release(fscp);
5250 5251 held = 0;
5251 5252 cachefs_cd_timedout(fscp);
5252 5253 connected = 0;
5253 5254 continue;
5254 5255 }
5255 5256 } else {
5256 5257 if (CFS_TIMEOUT(fscp, error)) {
5257 5258 connected = 1;
5258 5259 continue;
5259 5260 }
5260 5261 }
5261 5262 break;
5262 5263 }
5263 5264 }
5264 5265
5265 5266 /* get the cnode if file being deleted */
5266 5267 error = cachefs_lookup_common(ndvp, nnm, &delvp, NULL, 0,
5267 5268 NULL, cr);
5268 5269 if (error) {
5269 5270 delvp = NULL;
5270 5271 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
5271 5272 if (CFS_TIMEOUT(fscp, error)) {
5272 5273 cachefs_cd_release(fscp);
5273 5274 held = 0;
5274 5275 cachefs_cd_timedout(fscp);
5275 5276 connected = 0;
5276 5277 continue;
5277 5278 }
5278 5279 } else {
5279 5280 if (CFS_TIMEOUT(fscp, error)) {
5280 5281 connected = 1;
5281 5282 continue;
5282 5283 }
5283 5284 }
5284 5285 if (error != ENOENT)
5285 5286 break;
5286 5287 }
5287 5288
5288 5289 if (delvp && delvp->v_type == VDIR) {
5289 5290 /* see ufs_dirremove for why this is done, mount race */
5290 5291 if (vn_vfswlock(delvp)) {
5291 5292 error = EBUSY;
5292 5293 break;
5293 5294 }
5294 5295 vfslock = 1;
5295 5296 if (vn_mountedvfs(delvp) != NULL) {
5296 5297 error = EBUSY;
5297 5298 break;
5298 5299 }
5299 5300 }
5300 5301
5301 5302 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
5302 5303 error = cachefs_rename_connected(odvp, onm,
5303 5304 ndvp, nnm, cr, delvp);
5304 5305 if (CFS_TIMEOUT(fscp, error)) {
5305 5306 cachefs_cd_release(fscp);
5306 5307 held = 0;
5307 5308 cachefs_cd_timedout(fscp);
5308 5309 connected = 0;
5309 5310 continue;
5310 5311 }
5311 5312 } else {
5312 5313 error = cachefs_rename_disconnected(odvp, onm,
5313 5314 ndvp, nnm, cr, delvp);
5314 5315 if (CFS_TIMEOUT(fscp, error)) {
5315 5316 connected = 1;
5316 5317 continue;
5317 5318 }
5318 5319 }
5319 5320 break;
5320 5321 }
5321 5322
5322 5323 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RENAME)) {
5323 5324 struct fid gone;
5324 5325
5325 5326 bzero(&gone, sizeof (gone));
5326 5327 gone.fid_len = MAXFIDSZ;
5327 5328 if (delvp != NULL)
5328 5329 (void) VOP_FID(delvp, &gone, ct);
5329 5330
5330 5331 cachefs_log_rename(cachep, error, fscp->fs_cfsvfsp,
5331 5332 &gone, 0, (delvp != NULL), crgetuid(cr));
5332 5333 }
5333 5334
5334 5335 if (held)
5335 5336 cachefs_cd_release(fscp);
5336 5337
5337 5338 if (vfslock)
5338 5339 vn_vfsunlock(delvp);
5339 5340
5340 5341 if (delvp)
5341 5342 VN_RELE(delvp);
5342 5343 if (tvp)
5343 5344 VN_RELE(tvp);
5344 5345
5345 5346 #ifdef CFS_CD_DEBUG
5346 5347 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
5347 5348 #endif
5348 5349 return (error);
5349 5350 }
5350 5351
5351 5352 static int
5352 5353 cachefs_rename_connected(vnode_t *odvp, char *onm, vnode_t *ndvp,
5353 5354 char *nnm, cred_t *cr, vnode_t *delvp)
5354 5355 {
5355 5356 cnode_t *odcp = VTOC(odvp);
5356 5357 cnode_t *ndcp = VTOC(ndvp);
5357 5358 vnode_t *revp = NULL;
5358 5359 cnode_t *recp;
5359 5360 cnode_t *delcp;
5360 5361 fscache_t *fscp = C_TO_FSCACHE(odcp);
5361 5362 int error = 0;
5362 5363 struct fid cookie;
5363 5364 struct fid *cookiep;
5364 5365 cfs_cid_t cid;
5365 5366 int gotdirent;
5366 5367
5367 5368 /* find the file we are renaming */
5368 5369 error = cachefs_lookup_common(odvp, onm, &revp, NULL, 0, NULL, cr);
5369 5370 if (error)
5370 5371 return (error);
5371 5372 recp = VTOC(revp);
5372 5373
5373 5374 /*
5374 5375 * To avoid deadlock, we acquire this global rename lock before
5375 5376 * we try to get the locks for the source and target directories.
5376 5377 */
5377 5378 mutex_enter(&cachefs_rename_lock);
5378 5379 rw_enter(&odcp->c_rwlock, RW_WRITER);
5379 5380 if (odcp != ndcp) {
5380 5381 rw_enter(&ndcp->c_rwlock, RW_WRITER);
5381 5382 }
5382 5383 mutex_exit(&cachefs_rename_lock);
5383 5384
5384 5385 ASSERT((odcp->c_flags & CN_ASYNC_POP_WORKING) == 0);
5385 5386 ASSERT((ndcp->c_flags & CN_ASYNC_POP_WORKING) == 0);
5386 5387
5387 5388 mutex_enter(&odcp->c_statelock);
5388 5389 if (odcp->c_backvp == NULL) {
5389 5390 error = cachefs_getbackvp(fscp, odcp);
5390 5391 if (error) {
5391 5392 mutex_exit(&odcp->c_statelock);
5392 5393 goto out;
5393 5394 }
5394 5395 }
5395 5396
5396 5397 error = CFSOP_CHECK_COBJECT(fscp, odcp, 0, cr);
5397 5398 if (error) {
5398 5399 mutex_exit(&odcp->c_statelock);
5399 5400 goto out;
5400 5401 }
5401 5402 mutex_exit(&odcp->c_statelock);
5402 5403
5403 5404 if (odcp != ndcp) {
5404 5405 mutex_enter(&ndcp->c_statelock);
5405 5406 if (ndcp->c_backvp == NULL) {
5406 5407 error = cachefs_getbackvp(fscp, ndcp);
5407 5408 if (error) {
5408 5409 mutex_exit(&ndcp->c_statelock);
5409 5410 goto out;
5410 5411 }
5411 5412 }
5412 5413
5413 5414 error = CFSOP_CHECK_COBJECT(fscp, ndcp, 0, cr);
5414 5415 if (error) {
5415 5416 mutex_exit(&ndcp->c_statelock);
5416 5417 goto out;
5417 5418 }
5418 5419 mutex_exit(&ndcp->c_statelock);
5419 5420 }
5420 5421
5421 5422 /* if a file is being deleted because of this rename */
5422 5423 if (delvp) {
5423 5424 /* if src and dest file are same */
5424 5425 if (delvp == revp) {
5425 5426 error = 0;
5426 5427 goto out;
5427 5428 }
5428 5429
5429 5430 /*
5430 5431 * If the cnode is active, make a link to the file
5431 5432 * so operations on the file will continue.
5432 5433 */
5433 5434 dnlc_purge_vp(delvp);
5434 5435 delcp = VTOC(delvp);
5435 5436 if ((delvp->v_type != VDIR) &&
5436 5437 !((delvp->v_count == 1) ||
5437 5438 ((delvp->v_count == 2) && delcp->c_ipending))) {
5438 5439 error = cachefs_remove_dolink(ndvp, delvp, nnm, cr);
5439 5440 if (error)
5440 5441 goto out;
5441 5442 }
5442 5443 }
5443 5444
5444 5445 /* do the rename on the back fs */
5445 5446 CFS_DPRINT_BACKFS_NFSV4(fscp,
5446 5447 ("cachefs_rename (nfsv4): odcp %p, odbackvp %p, "
5447 5448 " ndcp %p, ndbackvp %p, onm %s, nnm %s\n",
5448 5449 odcp, odcp->c_backvp, ndcp, ndcp->c_backvp, onm, nnm));
5449 5450 error = VOP_RENAME(odcp->c_backvp, onm, ndcp->c_backvp, nnm, cr, NULL,
5450 5451 0);
5451 5452 if (error)
5452 5453 goto out;
5453 5454
5454 5455 /* purge mappings to file in the old directory */
5455 5456 dnlc_purge_vp(odvp);
5456 5457
5457 5458 /* purge mappings in the new dir if we deleted a file */
5458 5459 if (delvp && (odvp != ndvp))
5459 5460 dnlc_purge_vp(ndvp);
5460 5461
5461 5462 /* update the file we just deleted */
5462 5463 if (delvp) {
5463 5464 mutex_enter(&delcp->c_statelock);
5464 5465 if (delcp->c_attr.va_nlink == 1) {
5465 5466 delcp->c_flags |= CN_DESTROY;
5466 5467 } else {
5467 5468 delcp->c_flags |= CN_UPDATED;
5468 5469 }
5469 5470 delcp->c_attr.va_nlink--;
5470 5471 CFSOP_MODIFY_COBJECT(fscp, delcp, cr);
5471 5472 mutex_exit(&delcp->c_statelock);
5472 5473 }
5473 5474
5474 5475 /* find the entry in the old directory */
5475 5476 mutex_enter(&odcp->c_statelock);
5476 5477 gotdirent = 0;
5477 5478 cookiep = NULL;
5478 5479 if (CFS_ISFS_NONSHARED(fscp) &&
5479 5480 (odcp->c_metadata.md_flags & MD_POPULATED)) {
5480 5481 error = cachefs_dir_look(odcp, onm, &cookie,
5481 5482 NULL, NULL, &cid);
5482 5483 if (error == 0 || error == EINVAL) {
5483 5484 gotdirent = 1;
5484 5485 if (error == 0)
5485 5486 cookiep = &cookie;
5486 5487 } else {
5487 5488 cachefs_inval_object(odcp);
5488 5489 }
5489 5490 }
5490 5491 error = 0;
5491 5492
5492 5493 /* remove the directory entry from the old directory */
5493 5494 if (gotdirent) {
5494 5495 error = cachefs_dir_rmentry(odcp, onm);
5495 5496 if (error) {
5496 5497 cachefs_nocache(odcp);
5497 5498 error = 0;
5498 5499 }
5499 5500 }
5500 5501 CFSOP_MODIFY_COBJECT(fscp, odcp, cr);
5501 5502 mutex_exit(&odcp->c_statelock);
5502 5503
5503 5504 /* install the directory entry in the new directory */
5504 5505 mutex_enter(&ndcp->c_statelock);
5505 5506 if (CFS_ISFS_NONSHARED(fscp) &&
5506 5507 (ndcp->c_metadata.md_flags & MD_POPULATED)) {
5507 5508 error = 1;
5508 5509 if (gotdirent) {
5509 5510 ASSERT(cid.cid_fileno != 0);
5510 5511 error = 0;
5511 5512 if (delvp) {
5512 5513 error = cachefs_dir_rmentry(ndcp, nnm);
5513 5514 }
5514 5515 if (error == 0) {
5515 5516 error = cachefs_dir_enter(ndcp, nnm, cookiep,
5516 5517 &cid, SM_ASYNC);
5517 5518 }
5518 5519 }
5519 5520 if (error) {
5520 5521 cachefs_nocache(ndcp);
5521 5522 error = 0;
5522 5523 }
5523 5524 }
5524 5525 if (odcp != ndcp)
5525 5526 CFSOP_MODIFY_COBJECT(fscp, ndcp, cr);
5526 5527 mutex_exit(&ndcp->c_statelock);
5527 5528
5528 5529 /* ctime of renamed file has changed */
5529 5530 mutex_enter(&recp->c_statelock);
5530 5531 CFSOP_MODIFY_COBJECT(fscp, recp, cr);
5531 5532 mutex_exit(&recp->c_statelock);
5532 5533
5533 5534 out:
5534 5535 if (odcp != ndcp)
5535 5536 rw_exit(&ndcp->c_rwlock);
5536 5537 rw_exit(&odcp->c_rwlock);
5537 5538
5538 5539 VN_RELE(revp);
5539 5540
5540 5541 return (error);
5541 5542 }
5542 5543
5543 5544 static int
5544 5545 cachefs_rename_disconnected(vnode_t *odvp, char *onm, vnode_t *ndvp,
5545 5546 char *nnm, cred_t *cr, vnode_t *delvp)
5546 5547 {
5547 5548 cnode_t *odcp = VTOC(odvp);
5548 5549 cnode_t *ndcp = VTOC(ndvp);
5549 5550 cnode_t *delcp = NULL;
5550 5551 vnode_t *revp = NULL;
5551 5552 cnode_t *recp;
5552 5553 fscache_t *fscp = C_TO_FSCACHE(odcp);
5553 5554 int error = 0;
5554 5555 struct fid cookie;
5555 5556 struct fid *cookiep;
5556 5557 cfs_cid_t cid;
5557 5558 off_t commit = 0;
5558 5559 timestruc_t current_time;
5559 5560
5560 5561 if (CFS_ISFS_WRITE_AROUND(fscp))
5561 5562 return (ETIMEDOUT);
5562 5563
5563 5564 /* find the file we are renaming */
5564 5565 error = cachefs_lookup_common(odvp, onm, &revp, NULL, 0, NULL, cr);
5565 5566 if (error)
5566 5567 return (error);
5567 5568 recp = VTOC(revp);
5568 5569
5569 5570 /*
5570 5571 * To avoid deadlock, we acquire this global rename lock before
5571 5572 * we try to get the locks for the source and target directories.
5572 5573 */
5573 5574 mutex_enter(&cachefs_rename_lock);
5574 5575 rw_enter(&odcp->c_rwlock, RW_WRITER);
5575 5576 if (odcp != ndcp) {
5576 5577 rw_enter(&ndcp->c_rwlock, RW_WRITER);
5577 5578 }
5578 5579 mutex_exit(&cachefs_rename_lock);
5579 5580
5580 5581 if (recp->c_metadata.md_flags & MD_NEEDATTRS) {
5581 5582 error = ETIMEDOUT;
5582 5583 goto out;
5583 5584 }
5584 5585
5585 5586 if ((recp->c_metadata.md_flags & MD_MAPPING) == 0) {
5586 5587 mutex_enter(&recp->c_statelock);
5587 5588 if ((recp->c_metadata.md_flags & MD_MAPPING) == 0) {
5588 5589 error = cachefs_dlog_cidmap(fscp);
5589 5590 if (error) {
5590 5591 mutex_exit(&recp->c_statelock);
5591 5592 error = ENOSPC;
5592 5593 goto out;
5593 5594 }
5594 5595 recp->c_metadata.md_flags |= MD_MAPPING;
5595 5596 recp->c_flags |= CN_UPDATED;
5596 5597 }
5597 5598 mutex_exit(&recp->c_statelock);
5598 5599 }
5599 5600
5600 5601 /* check permissions */
5601 5602 /* XXX clean up this mutex junk sometime */
5602 5603 mutex_enter(&odcp->c_statelock);
5603 5604 error = cachefs_access_local(odcp, (VEXEC|VWRITE), cr);
5604 5605 mutex_exit(&odcp->c_statelock);
5605 5606 if (error != 0)
5606 5607 goto out;
5607 5608 mutex_enter(&ndcp->c_statelock);
5608 5609 error = cachefs_access_local(ndcp, (VEXEC|VWRITE), cr);
5609 5610 mutex_exit(&ndcp->c_statelock);
5610 5611 if (error != 0)
5611 5612 goto out;
5612 5613 mutex_enter(&odcp->c_statelock);
5613 5614 error = cachefs_stickyrmchk(odcp, recp, cr);
5614 5615 mutex_exit(&odcp->c_statelock);
5615 5616 if (error != 0)
5616 5617 goto out;
5617 5618
5618 5619 /* dirs must be populated */
5619 5620 if (((odcp->c_metadata.md_flags & MD_POPULATED) == 0) ||
5620 5621 ((ndcp->c_metadata.md_flags & MD_POPULATED) == 0)) {
5621 5622 error = ETIMEDOUT;
5622 5623 goto out;
5623 5624 }
5624 5625
5625 5626 /* for now do not allow moving dirs because could cause cycles */
5626 5627 if ((((revp->v_type == VDIR) && (odvp != ndvp))) ||
5627 5628 (revp == odvp)) {
5628 5629 error = ETIMEDOUT;
5629 5630 goto out;
5630 5631 }
5631 5632
5632 5633 /* if a file is being deleted because of this rename */
5633 5634 if (delvp) {
5634 5635 delcp = VTOC(delvp);
5635 5636
5636 5637 /* if src and dest file are the same */
5637 5638 if (delvp == revp) {
5638 5639 error = 0;
5639 5640 goto out;
5640 5641 }
5641 5642
5642 5643 if (delcp->c_metadata.md_flags & MD_NEEDATTRS) {
5643 5644 error = ETIMEDOUT;
5644 5645 goto out;
5645 5646 }
5646 5647
5647 5648 /* if there are hard links to this file */
5648 5649 if (delcp->c_attr.va_nlink > 1) {
5649 5650 mutex_enter(&delcp->c_statelock);
5650 5651 if (cachefs_modified_alloc(delcp)) {
5651 5652 mutex_exit(&delcp->c_statelock);
5652 5653 error = ENOSPC;
5653 5654 goto out;
5654 5655 }
5655 5656
5656 5657 if ((delcp->c_metadata.md_flags & MD_MAPPING) == 0) {
5657 5658 error = cachefs_dlog_cidmap(fscp);
5658 5659 if (error) {
5659 5660 mutex_exit(&delcp->c_statelock);
5660 5661 error = ENOSPC;
5661 5662 goto out;
5662 5663 }
5663 5664 delcp->c_metadata.md_flags |= MD_MAPPING;
5664 5665 delcp->c_flags |= CN_UPDATED;
5665 5666 }
5666 5667 mutex_exit(&delcp->c_statelock);
5667 5668 }
5668 5669
5669 5670 /* make sure we can delete file */
5670 5671 mutex_enter(&ndcp->c_statelock);
5671 5672 error = cachefs_stickyrmchk(ndcp, delcp, cr);
5672 5673 mutex_exit(&ndcp->c_statelock);
5673 5674 if (error != 0)
5674 5675 goto out;
5675 5676
5676 5677 /*
5677 5678 * If the cnode is active, make a link to the file
5678 5679 * so operations on the file will continue.
5679 5680 */
5680 5681 dnlc_purge_vp(delvp);
5681 5682 if ((delvp->v_type != VDIR) &&
5682 5683 !((delvp->v_count == 1) ||
5683 5684 ((delvp->v_count == 2) && delcp->c_ipending))) {
5684 5685 error = cachefs_remove_dolink(ndvp, delvp, nnm, cr);
5685 5686 if (error)
5686 5687 goto out;
5687 5688 }
5688 5689 }
5689 5690
5690 5691 /* purge mappings to file in the old directory */
5691 5692 dnlc_purge_vp(odvp);
5692 5693
5693 5694 /* purge mappings in the new dir if we deleted a file */
5694 5695 if (delvp && (odvp != ndvp))
5695 5696 dnlc_purge_vp(ndvp);
5696 5697
5697 5698 /* find the entry in the old directory */
5698 5699 mutex_enter(&odcp->c_statelock);
5699 5700 if ((odcp->c_metadata.md_flags & MD_POPULATED) == 0) {
5700 5701 mutex_exit(&odcp->c_statelock);
5701 5702 error = ETIMEDOUT;
5702 5703 goto out;
5703 5704 }
5704 5705 cookiep = NULL;
5705 5706 error = cachefs_dir_look(odcp, onm, &cookie, NULL, NULL, &cid);
5706 5707 if (error == 0 || error == EINVAL) {
5707 5708 if (error == 0)
5708 5709 cookiep = &cookie;
5709 5710 } else {
5710 5711 mutex_exit(&odcp->c_statelock);
5711 5712 if (error == ENOTDIR)
5712 5713 error = ETIMEDOUT;
5713 5714 goto out;
5714 5715 }
5715 5716 error = 0;
5716 5717
5717 5718 /* write the log entry */
5718 5719 commit = cachefs_dlog_rename(fscp, odcp, onm, ndcp, nnm, cr,
5719 5720 recp, delcp);
5720 5721 if (commit == 0) {
5721 5722 mutex_exit(&odcp->c_statelock);
5722 5723 error = ENOSPC;
5723 5724 goto out;
5724 5725 }
5725 5726
5726 5727 /* remove the directory entry from the old directory */
5727 5728 cachefs_modified(odcp);
5728 5729 error = cachefs_dir_rmentry(odcp, onm);
5729 5730 if (error) {
5730 5731 mutex_exit(&odcp->c_statelock);
5731 5732 if (error == ENOTDIR)
5732 5733 error = ETIMEDOUT;
5733 5734 goto out;
5734 5735 }
5735 5736 mutex_exit(&odcp->c_statelock);
5736 5737
5737 5738 /* install the directory entry in the new directory */
5738 5739 mutex_enter(&ndcp->c_statelock);
5739 5740 error = ENOTDIR;
5740 5741 if (ndcp->c_metadata.md_flags & MD_POPULATED) {
5741 5742 ASSERT(cid.cid_fileno != 0);
5742 5743 cachefs_modified(ndcp);
5743 5744 error = 0;
5744 5745 if (delvp) {
5745 5746 error = cachefs_dir_rmentry(ndcp, nnm);
5746 5747 }
5747 5748 if (error == 0) {
5748 5749 error = cachefs_dir_enter(ndcp, nnm, cookiep,
5749 5750 &cid, SM_ASYNC);
5750 5751 }
5751 5752 }
5752 5753 if (error) {
5753 5754 cachefs_nocache(ndcp);
5754 5755 mutex_exit(&ndcp->c_statelock);
5755 5756 mutex_enter(&odcp->c_statelock);
5756 5757 cachefs_nocache(odcp);
5757 5758 mutex_exit(&odcp->c_statelock);
5758 5759 if (error == ENOTDIR)
5759 5760 error = ETIMEDOUT;
5760 5761 goto out;
5761 5762 }
5762 5763 mutex_exit(&ndcp->c_statelock);
5763 5764
5764 5765 gethrestime(¤t_time);
5765 5766
5766 5767 /* update the file we just deleted */
5767 5768 if (delvp) {
5768 5769 mutex_enter(&delcp->c_statelock);
5769 5770 delcp->c_attr.va_nlink--;
5770 5771 delcp->c_metadata.md_localctime = current_time;
5771 5772 delcp->c_metadata.md_flags |= MD_LOCALCTIME;
5772 5773 if (delcp->c_attr.va_nlink == 0) {
5773 5774 delcp->c_flags |= CN_DESTROY;
5774 5775 } else {
5775 5776 delcp->c_flags |= CN_UPDATED;
5776 5777 }
5777 5778 mutex_exit(&delcp->c_statelock);
5778 5779 }
5779 5780
5780 5781 /* update the file we renamed */
5781 5782 mutex_enter(&recp->c_statelock);
5782 5783 recp->c_metadata.md_localctime = current_time;
5783 5784 recp->c_metadata.md_flags |= MD_LOCALCTIME;
5784 5785 recp->c_flags |= CN_UPDATED;
5785 5786 mutex_exit(&recp->c_statelock);
5786 5787
5787 5788 /* update the source directory */
5788 5789 mutex_enter(&odcp->c_statelock);
5789 5790 odcp->c_metadata.md_localctime = current_time;
5790 5791 odcp->c_metadata.md_localmtime = current_time;
5791 5792 odcp->c_metadata.md_flags |= MD_LOCALCTIME | MD_LOCALMTIME;
5792 5793 odcp->c_flags |= CN_UPDATED;
5793 5794 mutex_exit(&odcp->c_statelock);
5794 5795
5795 5796 /* update the destination directory */
5796 5797 if (odcp != ndcp) {
5797 5798 mutex_enter(&ndcp->c_statelock);
5798 5799 ndcp->c_metadata.md_localctime = current_time;
5799 5800 ndcp->c_metadata.md_localmtime = current_time;
5800 5801 ndcp->c_metadata.md_flags |= MD_LOCALCTIME | MD_LOCALMTIME;
5801 5802 ndcp->c_flags |= CN_UPDATED;
5802 5803 mutex_exit(&ndcp->c_statelock);
5803 5804 }
5804 5805
5805 5806 out:
5806 5807 if (commit) {
5807 5808 /* commit the log entry */
5808 5809 if (cachefs_dlog_commit(fscp, commit, error)) {
5809 5810 /*EMPTY*/
5810 5811 /* XXX bob: fix on panic */
5811 5812 }
5812 5813 }
5813 5814
5814 5815 if (odcp != ndcp)
5815 5816 rw_exit(&ndcp->c_rwlock);
5816 5817 rw_exit(&odcp->c_rwlock);
5817 5818
5818 5819 VN_RELE(revp);
5819 5820
5820 5821 return (error);
5821 5822 }
5822 5823
5823 5824 /*ARGSUSED*/
5824 5825 static int
5825 5826 cachefs_mkdir(vnode_t *dvp, char *nm, vattr_t *vap, vnode_t **vpp,
5826 5827 cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
5827 5828 {
5828 5829 cnode_t *dcp = VTOC(dvp);
5829 5830 fscache_t *fscp = C_TO_FSCACHE(dcp);
5830 5831 cachefscache_t *cachep = fscp->fs_cache;
5831 5832 int error = 0;
5832 5833 int held = 0;
5833 5834 int connected = 0;
5834 5835
5835 5836 #ifdef CFSDEBUG
5836 5837 CFS_DEBUG(CFSDEBUG_VOPS)
5837 5838 printf("cachefs_mkdir: ENTER dvp %p\n", (void *)dvp);
5838 5839 #endif
5839 5840
5840 5841 if (getzoneid() != GLOBAL_ZONEID) {
5841 5842 error = EPERM;
5842 5843 goto out;
5843 5844 }
5844 5845
5845 5846 if (fscp->fs_cache->c_flags & (CACHE_NOFILL | CACHE_NOCACHE))
5846 5847 ASSERT(dcp->c_flags & CN_NOCACHE);
5847 5848
5848 5849 /*
5849 5850 * Cachefs only provides pass-through support for NFSv4,
5850 5851 * and all vnode operations are passed through to the
5851 5852 * back file system. For NFSv4 pass-through to work, only
5852 5853 * connected operation is supported, the cnode backvp must
5853 5854 * exist, and cachefs optional (eg., disconnectable) flags
5854 5855 * are turned off. Assert these conditions to ensure that
5855 5856 * the backfilesystem is called for the mkdir operation.
5856 5857 */
5857 5858 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
5858 5859 CFS_BACKFS_NFSV4_ASSERT_CNODE(dcp);
5859 5860
5860 5861 for (;;) {
5861 5862 /* get (or renew) access to the file system */
5862 5863 if (held) {
5863 5864 /* Won't loop with NFSv4 connected behavior */
5864 5865 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
5865 5866 rw_exit(&dcp->c_rwlock);
5866 5867 cachefs_cd_release(fscp);
5867 5868 held = 0;
5868 5869 }
5869 5870 error = cachefs_cd_access(fscp, connected, 1);
5870 5871 if (error)
5871 5872 break;
5872 5873 rw_enter(&dcp->c_rwlock, RW_WRITER);
5873 5874 held = 1;
5874 5875
5875 5876 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
5876 5877 error = cachefs_mkdir_connected(dvp, nm, vap,
5877 5878 vpp, cr);
5878 5879 if (CFS_TIMEOUT(fscp, error)) {
5879 5880 rw_exit(&dcp->c_rwlock);
5880 5881 cachefs_cd_release(fscp);
5881 5882 held = 0;
5882 5883 cachefs_cd_timedout(fscp);
5883 5884 connected = 0;
5884 5885 continue;
5885 5886 }
5886 5887 } else {
5887 5888 error = cachefs_mkdir_disconnected(dvp, nm, vap,
5888 5889 vpp, cr);
5889 5890 if (CFS_TIMEOUT(fscp, error)) {
5890 5891 connected = 1;
5891 5892 continue;
5892 5893 }
5893 5894 }
5894 5895 break;
5895 5896 }
5896 5897
5897 5898 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_MKDIR)) {
5898 5899 fid_t *fidp = NULL;
5899 5900 ino64_t fileno = 0;
5900 5901 cnode_t *cp = NULL;
5901 5902 if (error == 0)
5902 5903 cp = VTOC(*vpp);
5903 5904
5904 5905 if (cp != NULL) {
5905 5906 fidp = &cp->c_metadata.md_cookie;
5906 5907 fileno = cp->c_id.cid_fileno;
5907 5908 }
5908 5909
5909 5910 cachefs_log_mkdir(cachep, error, fscp->fs_cfsvfsp,
5910 5911 fidp, fileno, crgetuid(cr));
5911 5912 }
5912 5913
5913 5914 if (held) {
5914 5915 rw_exit(&dcp->c_rwlock);
5915 5916 cachefs_cd_release(fscp);
5916 5917 }
5917 5918 if (error == 0 && CFS_ISFS_NONSHARED(fscp))
5918 5919 (void) cachefs_pack(dvp, nm, cr);
5919 5920
5920 5921 #ifdef CFS_CD_DEBUG
5921 5922 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
5922 5923 #endif
5923 5924 out:
5924 5925 #ifdef CFSDEBUG
5925 5926 CFS_DEBUG(CFSDEBUG_VOPS)
5926 5927 printf("cachefs_mkdir: EXIT error = %d\n", error);
5927 5928 #endif
5928 5929 return (error);
5929 5930 }
5930 5931
5931 5932 static int
5932 5933 cachefs_mkdir_connected(vnode_t *dvp, char *nm, vattr_t *vap,
5933 5934 vnode_t **vpp, cred_t *cr)
5934 5935 {
5935 5936 cnode_t *newcp = NULL, *dcp = VTOC(dvp);
5936 5937 struct vnode *vp = NULL;
5937 5938 int error = 0;
5938 5939 fscache_t *fscp = C_TO_FSCACHE(dcp);
5939 5940 struct fid cookie;
5940 5941 struct vattr attr;
5941 5942 cfs_cid_t cid, dircid;
5942 5943 uint32_t valid_fid;
5943 5944
5944 5945 if (fscp->fs_cache->c_flags & (CACHE_NOFILL | CACHE_NOCACHE))
5945 5946 ASSERT(dcp->c_flags & CN_NOCACHE);
5946 5947
5947 5948 mutex_enter(&dcp->c_statelock);
5948 5949
5949 5950 /* get backvp of dir */
5950 5951 if (dcp->c_backvp == NULL) {
5951 5952 error = cachefs_getbackvp(fscp, dcp);
5952 5953 if (error) {
5953 5954 mutex_exit(&dcp->c_statelock);
5954 5955 goto out;
5955 5956 }
5956 5957 }
5957 5958
5958 5959 /* consistency check the directory */
5959 5960 error = CFSOP_CHECK_COBJECT(fscp, dcp, 0, cr);
5960 5961 if (error) {
5961 5962 mutex_exit(&dcp->c_statelock);
5962 5963 goto out;
5963 5964 }
5964 5965 dircid = dcp->c_id;
5965 5966
5966 5967 /* make the dir on the back fs */
5967 5968 CFS_DPRINT_BACKFS_NFSV4(fscp,
5968 5969 ("cachefs_mkdir (nfsv4): dcp %p, dbackvp %p, "
5969 5970 "name %s\n", dcp, dcp->c_backvp, nm));
5970 5971 error = VOP_MKDIR(dcp->c_backvp, nm, vap, &vp, cr, NULL, 0, NULL);
5971 5972 mutex_exit(&dcp->c_statelock);
5972 5973 if (error) {
5973 5974 goto out;
5974 5975 }
5975 5976
5976 5977 /* get the cookie and make the cnode */
5977 5978 attr.va_mask = AT_ALL;
5978 5979 valid_fid = (CFS_ISFS_BACKFS_NFSV4(fscp) ? FALSE : TRUE);
5979 5980 error = cachefs_getcookie(vp, &cookie, &attr, cr, valid_fid);
5980 5981 if (error) {
5981 5982 goto out;
5982 5983 }
5983 5984 cid.cid_flags = 0;
5984 5985 cid.cid_fileno = attr.va_nodeid;
5985 5986 error = cachefs_cnode_make(&cid, fscp, (valid_fid ? &cookie : NULL),
5986 5987 &attr, vp, cr, 0, &newcp);
5987 5988 if (error) {
5988 5989 goto out;
5989 5990 }
5990 5991 ASSERT(CTOV(newcp)->v_type == VDIR);
5991 5992 *vpp = CTOV(newcp);
5992 5993
5993 5994 /* if the dir is populated, add the new entry */
5994 5995 mutex_enter(&dcp->c_statelock);
5995 5996 if (CFS_ISFS_NONSHARED(fscp) &&
5996 5997 (dcp->c_metadata.md_flags & MD_POPULATED)) {
5997 5998 error = cachefs_dir_enter(dcp, nm, &cookie, &newcp->c_id,
5998 5999 SM_ASYNC);
5999 6000 if (error) {
6000 6001 cachefs_nocache(dcp);
6001 6002 error = 0;
6002 6003 }
6003 6004 }
6004 6005 dcp->c_attr.va_nlink++;
6005 6006 dcp->c_flags |= CN_UPDATED;
6006 6007 CFSOP_MODIFY_COBJECT(fscp, dcp, cr);
6007 6008 mutex_exit(&dcp->c_statelock);
6008 6009
6009 6010 /* XXX bob: should we do a filldir here? or just add . and .. */
6010 6011 /* maybe should kick off an async filldir so caller does not wait */
6011 6012
6012 6013 /* put the entry in the dnlc */
6013 6014 if (cachefs_dnlc)
6014 6015 dnlc_enter(dvp, nm, *vpp);
6015 6016
6016 6017 /* save the fileno of the parent so can find the name */
6017 6018 if (bcmp(&newcp->c_metadata.md_parent, &dircid,
6018 6019 sizeof (cfs_cid_t)) != 0) {
6019 6020 mutex_enter(&newcp->c_statelock);
6020 6021 newcp->c_metadata.md_parent = dircid;
6021 6022 newcp->c_flags |= CN_UPDATED;
6022 6023 mutex_exit(&newcp->c_statelock);
6023 6024 }
6024 6025 out:
6025 6026 if (vp)
6026 6027 VN_RELE(vp);
6027 6028
6028 6029 return (error);
6029 6030 }
6030 6031
6031 6032 static int
6032 6033 cachefs_mkdir_disconnected(vnode_t *dvp, char *nm, vattr_t *vap,
6033 6034 vnode_t **vpp, cred_t *cr)
6034 6035 {
6035 6036 cnode_t *dcp = VTOC(dvp);
6036 6037 fscache_t *fscp = C_TO_FSCACHE(dcp);
6037 6038 int error;
6038 6039 cnode_t *newcp = NULL;
6039 6040 struct vattr va;
6040 6041 timestruc_t current_time;
6041 6042 off_t commit = 0;
6042 6043 char *s;
6043 6044 int namlen;
6044 6045
6045 6046 /* don't allow '/' characters in pathname component */
6046 6047 for (s = nm, namlen = 0; *s; s++, namlen++)
6047 6048 if (*s == '/')
6048 6049 return (EACCES);
6049 6050 if (namlen == 0)
6050 6051 return (EINVAL);
6051 6052
6052 6053 if (CFS_ISFS_WRITE_AROUND(fscp))
6053 6054 return (ETIMEDOUT);
6054 6055
6055 6056 mutex_enter(&dcp->c_statelock);
6056 6057
6057 6058 /* check permissions */
6058 6059 if (error = cachefs_access_local(dcp, (VEXEC|VWRITE), cr)) {
6059 6060 mutex_exit(&dcp->c_statelock);
6060 6061 goto out;
6061 6062 }
6062 6063
6063 6064 /* the directory front file must be populated */
6064 6065 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
6065 6066 error = ETIMEDOUT;
6066 6067 mutex_exit(&dcp->c_statelock);
6067 6068 goto out;
6068 6069 }
6069 6070
6070 6071 /* make sure nm does not already exist in the directory */
6071 6072 error = cachefs_dir_look(dcp, nm, NULL, NULL, NULL, NULL);
6072 6073 if (error == ENOTDIR) {
6073 6074 error = ETIMEDOUT;
6074 6075 mutex_exit(&dcp->c_statelock);
6075 6076 goto out;
6076 6077 }
6077 6078 if (error != ENOENT) {
6078 6079 error = EEXIST;
6079 6080 mutex_exit(&dcp->c_statelock);
6080 6081 goto out;
6081 6082 }
6082 6083
6083 6084 /* make up a reasonable set of attributes */
6084 6085 cachefs_attr_setup(vap, &va, dcp, cr);
6085 6086 va.va_type = VDIR;
6086 6087 va.va_mode |= S_IFDIR;
6087 6088 va.va_nlink = 2;
6088 6089
6089 6090 mutex_exit(&dcp->c_statelock);
6090 6091
6091 6092 /* create the cnode */
6092 6093 error = cachefs_cnode_create(fscp, &va, 0, &newcp);
6093 6094 if (error)
6094 6095 goto out;
6095 6096
6096 6097 mutex_enter(&newcp->c_statelock);
6097 6098
6098 6099 error = cachefs_dlog_cidmap(fscp);
6099 6100 if (error) {
6100 6101 mutex_exit(&newcp->c_statelock);
6101 6102 goto out;
6102 6103 }
6103 6104
6104 6105 cachefs_creategid(dcp, newcp, vap, cr);
6105 6106 mutex_enter(&dcp->c_statelock);
6106 6107 cachefs_createacl(dcp, newcp);
6107 6108 mutex_exit(&dcp->c_statelock);
6108 6109 gethrestime(¤t_time);
6109 6110 newcp->c_metadata.md_vattr.va_atime = current_time;
6110 6111 newcp->c_metadata.md_localctime = current_time;
6111 6112 newcp->c_metadata.md_localmtime = current_time;
6112 6113 newcp->c_metadata.md_flags |= MD_MAPPING | MD_LOCALMTIME |
6113 6114 MD_LOCALCTIME;
6114 6115 newcp->c_flags |= CN_UPDATED;
6115 6116
6116 6117 /* make a front file for the new directory, add . and .. */
6117 6118 error = cachefs_dir_new(dcp, newcp);
6118 6119 if (error) {
6119 6120 mutex_exit(&newcp->c_statelock);
6120 6121 goto out;
6121 6122 }
6122 6123 cachefs_modified(newcp);
6123 6124
6124 6125 /*
6125 6126 * write the metadata now rather than waiting until
6126 6127 * inactive so that if there's no space we can let
6127 6128 * the caller know.
6128 6129 */
6129 6130 ASSERT(newcp->c_frontvp);
6130 6131 ASSERT((newcp->c_filegrp->fg_flags & CFS_FG_ALLOC_ATTR) == 0);
6131 6132 ASSERT((newcp->c_flags & CN_ALLOC_PENDING) == 0);
6132 6133 error = filegrp_write_metadata(newcp->c_filegrp,
6133 6134 &newcp->c_id, &newcp->c_metadata);
6134 6135 if (error) {
6135 6136 mutex_exit(&newcp->c_statelock);
6136 6137 goto out;
6137 6138 }
6138 6139 mutex_exit(&newcp->c_statelock);
6139 6140
6140 6141 /* log the operation */
6141 6142 commit = cachefs_dlog_mkdir(fscp, dcp, newcp, nm, &va, cr);
6142 6143 if (commit == 0) {
6143 6144 error = ENOSPC;
6144 6145 goto out;
6145 6146 }
6146 6147
6147 6148 mutex_enter(&dcp->c_statelock);
6148 6149
6149 6150 /* make sure directory is still populated */
6150 6151 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
6151 6152 mutex_exit(&dcp->c_statelock);
6152 6153 error = ETIMEDOUT;
6153 6154 goto out;
6154 6155 }
6155 6156 cachefs_modified(dcp);
6156 6157
6157 6158 /* enter the new file in the directory */
6158 6159 error = cachefs_dir_enter(dcp, nm, &newcp->c_metadata.md_cookie,
6159 6160 &newcp->c_id, SM_ASYNC);
6160 6161 if (error) {
6161 6162 mutex_exit(&dcp->c_statelock);
6162 6163 goto out;
6163 6164 }
6164 6165
6165 6166 /* update parent dir times */
6166 6167 dcp->c_metadata.md_localctime = current_time;
6167 6168 dcp->c_metadata.md_localmtime = current_time;
6168 6169 dcp->c_metadata.md_flags |= MD_LOCALCTIME | MD_LOCALMTIME;
6169 6170 dcp->c_attr.va_nlink++;
6170 6171 dcp->c_flags |= CN_UPDATED;
6171 6172 mutex_exit(&dcp->c_statelock);
6172 6173
6173 6174 out:
6174 6175 if (commit) {
6175 6176 /* commit the log entry */
6176 6177 if (cachefs_dlog_commit(fscp, commit, error)) {
6177 6178 /*EMPTY*/
6178 6179 /* XXX bob: fix on panic */
6179 6180 }
6180 6181 }
6181 6182 if (error) {
6182 6183 if (newcp) {
6183 6184 mutex_enter(&newcp->c_statelock);
6184 6185 newcp->c_flags |= CN_DESTROY;
6185 6186 mutex_exit(&newcp->c_statelock);
6186 6187 VN_RELE(CTOV(newcp));
6187 6188 }
6188 6189 } else {
6189 6190 *vpp = CTOV(newcp);
6190 6191 }
6191 6192 return (error);
6192 6193 }
6193 6194
6194 6195 /*ARGSUSED*/
6195 6196 static int
6196 6197 cachefs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
6197 6198 caller_context_t *ct, int flags)
6198 6199 {
6199 6200 cnode_t *dcp = VTOC(dvp);
6200 6201 fscache_t *fscp = C_TO_FSCACHE(dcp);
6201 6202 cachefscache_t *cachep = fscp->fs_cache;
6202 6203 int error = 0;
6203 6204 int held = 0;
6204 6205 int connected = 0;
6205 6206 size_t namlen;
6206 6207 vnode_t *vp = NULL;
6207 6208 int vfslock = 0;
6208 6209
6209 6210 #ifdef CFSDEBUG
6210 6211 CFS_DEBUG(CFSDEBUG_VOPS)
6211 6212 printf("cachefs_rmdir: ENTER vp %p\n", (void *)dvp);
6212 6213 #endif
6213 6214
6214 6215 if (getzoneid() != GLOBAL_ZONEID) {
6215 6216 error = EPERM;
6216 6217 goto out;
6217 6218 }
6218 6219
6219 6220 if (fscp->fs_cache->c_flags & (CACHE_NOFILL | CACHE_NOCACHE))
6220 6221 ASSERT(dcp->c_flags & CN_NOCACHE);
6221 6222
6222 6223 /*
6223 6224 * Cachefs only provides pass-through support for NFSv4,
6224 6225 * and all vnode operations are passed through to the
6225 6226 * back file system. For NFSv4 pass-through to work, only
6226 6227 * connected operation is supported, the cnode backvp must
6227 6228 * exist, and cachefs optional (eg., disconnectable) flags
6228 6229 * are turned off. Assert these conditions to ensure that
6229 6230 * the backfilesystem is called for the rmdir operation.
6230 6231 */
6231 6232 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
6232 6233 CFS_BACKFS_NFSV4_ASSERT_CNODE(dcp);
6233 6234
6234 6235 for (;;) {
6235 6236 if (vfslock) {
6236 6237 vn_vfsunlock(vp);
6237 6238 vfslock = 0;
6238 6239 }
6239 6240 if (vp) {
6240 6241 VN_RELE(vp);
6241 6242 vp = NULL;
6242 6243 }
6243 6244
6244 6245 /* get (or renew) access to the file system */
6245 6246 if (held) {
6246 6247 /* Won't loop with NFSv4 connected behavior */
6247 6248 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
6248 6249 cachefs_cd_release(fscp);
6249 6250 held = 0;
6250 6251 }
6251 6252 error = cachefs_cd_access(fscp, connected, 1);
6252 6253 if (error)
6253 6254 break;
6254 6255 held = 1;
6255 6256
6256 6257 /* if disconnected, do some extra error checking */
6257 6258 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
6258 6259 /* check permissions */
6259 6260 mutex_enter(&dcp->c_statelock);
6260 6261 error = cachefs_access_local(dcp, (VEXEC|VWRITE), cr);
6261 6262 mutex_exit(&dcp->c_statelock);
6262 6263 if (CFS_TIMEOUT(fscp, error)) {
6263 6264 connected = 1;
6264 6265 continue;
6265 6266 }
6266 6267 if (error)
6267 6268 break;
6268 6269
6269 6270 namlen = strlen(nm);
6270 6271 if (namlen == 0) {
6271 6272 error = EINVAL;
6272 6273 break;
6273 6274 }
6274 6275
6275 6276 /* cannot remove . and .. */
6276 6277 if (nm[0] == '.') {
6277 6278 if (namlen == 1) {
6278 6279 error = EINVAL;
6279 6280 break;
6280 6281 } else if (namlen == 2 && nm[1] == '.') {
6281 6282 error = EEXIST;
6282 6283 break;
6283 6284 }
6284 6285 }
6285 6286
6286 6287 }
6287 6288
6288 6289 /* get the cnode of the dir to remove */
6289 6290 error = cachefs_lookup_common(dvp, nm, &vp, NULL, 0, NULL, cr);
6290 6291 if (error) {
6291 6292 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
6292 6293 if (CFS_TIMEOUT(fscp, error)) {
6293 6294 cachefs_cd_release(fscp);
6294 6295 held = 0;
6295 6296 cachefs_cd_timedout(fscp);
6296 6297 connected = 0;
6297 6298 continue;
6298 6299 }
6299 6300 } else {
6300 6301 if (CFS_TIMEOUT(fscp, error)) {
6301 6302 connected = 1;
6302 6303 continue;
6303 6304 }
6304 6305 }
6305 6306 break;
6306 6307 }
6307 6308
6308 6309 /* must be a dir */
6309 6310 if (vp->v_type != VDIR) {
6310 6311 error = ENOTDIR;
6311 6312 break;
6312 6313 }
6313 6314
6314 6315 /* must not be current dir */
6315 6316 if (VOP_CMP(vp, cdir, ct)) {
6316 6317 error = EINVAL;
6317 6318 break;
6318 6319 }
6319 6320
6320 6321 /* see ufs_dirremove for why this is done, mount race */
6321 6322 if (vn_vfswlock(vp)) {
6322 6323 error = EBUSY;
6323 6324 break;
6324 6325 }
6325 6326 vfslock = 1;
6326 6327 if (vn_mountedvfs(vp) != NULL) {
6327 6328 error = EBUSY;
6328 6329 break;
6329 6330 }
6330 6331
6331 6332 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
6332 6333 error = cachefs_rmdir_connected(dvp, nm, cdir,
6333 6334 cr, vp);
6334 6335 if (CFS_TIMEOUT(fscp, error)) {
6335 6336 cachefs_cd_release(fscp);
6336 6337 held = 0;
6337 6338 cachefs_cd_timedout(fscp);
6338 6339 connected = 0;
6339 6340 continue;
6340 6341 }
6341 6342 } else {
6342 6343 error = cachefs_rmdir_disconnected(dvp, nm, cdir,
6343 6344 cr, vp);
6344 6345 if (CFS_TIMEOUT(fscp, error)) {
6345 6346 connected = 1;
6346 6347 continue;
6347 6348 }
6348 6349 }
6349 6350 break;
6350 6351 }
6351 6352
6352 6353 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RMDIR)) {
6353 6354 ino64_t fileno = 0;
6354 6355 fid_t *fidp = NULL;
6355 6356 cnode_t *cp = NULL;
6356 6357 if (vp)
6357 6358 cp = VTOC(vp);
6358 6359
6359 6360 if (cp != NULL) {
6360 6361 fidp = &cp->c_metadata.md_cookie;
6361 6362 fileno = cp->c_id.cid_fileno;
6362 6363 }
6363 6364
6364 6365 cachefs_log_rmdir(cachep, error, fscp->fs_cfsvfsp,
6365 6366 fidp, fileno, crgetuid(cr));
6366 6367 }
6367 6368
6368 6369 if (held) {
6369 6370 cachefs_cd_release(fscp);
6370 6371 }
6371 6372
6372 6373 if (vfslock)
6373 6374 vn_vfsunlock(vp);
6374 6375
6375 6376 if (vp)
6376 6377 VN_RELE(vp);
6377 6378
6378 6379 #ifdef CFS_CD_DEBUG
6379 6380 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
6380 6381 #endif
6381 6382 out:
6382 6383 #ifdef CFSDEBUG
6383 6384 CFS_DEBUG(CFSDEBUG_VOPS)
6384 6385 printf("cachefs_rmdir: EXIT error = %d\n", error);
6385 6386 #endif
6386 6387
6387 6388 return (error);
6388 6389 }
6389 6390
6390 6391 static int
6391 6392 cachefs_rmdir_connected(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
6392 6393 vnode_t *vp)
6393 6394 {
6394 6395 cnode_t *dcp = VTOC(dvp);
6395 6396 cnode_t *cp = VTOC(vp);
6396 6397 int error = 0;
6397 6398 fscache_t *fscp = C_TO_FSCACHE(dcp);
6398 6399
6399 6400 rw_enter(&dcp->c_rwlock, RW_WRITER);
6400 6401 mutex_enter(&dcp->c_statelock);
6401 6402 mutex_enter(&cp->c_statelock);
6402 6403
6403 6404 if (dcp->c_backvp == NULL) {
6404 6405 error = cachefs_getbackvp(fscp, dcp);
6405 6406 if (error) {
6406 6407 goto out;
6407 6408 }
6408 6409 }
6409 6410
6410 6411 error = CFSOP_CHECK_COBJECT(fscp, dcp, 0, cr);
6411 6412 if (error)
6412 6413 goto out;
6413 6414
6414 6415 /* rmdir on the back fs */
6415 6416 CFS_DPRINT_BACKFS_NFSV4(fscp,
6416 6417 ("cachefs_rmdir (nfsv4): dcp %p, dbackvp %p, "
6417 6418 "name %s\n", dcp, dcp->c_backvp, nm));
6418 6419 error = VOP_RMDIR(dcp->c_backvp, nm, cdir, cr, NULL, 0);
6419 6420 if (error)
6420 6421 goto out;
6421 6422
6422 6423 /* if the dir is populated, remove the entry from it */
6423 6424 if (CFS_ISFS_NONSHARED(fscp) &&
6424 6425 (dcp->c_metadata.md_flags & MD_POPULATED)) {
6425 6426 error = cachefs_dir_rmentry(dcp, nm);
6426 6427 if (error) {
6427 6428 cachefs_nocache(dcp);
6428 6429 error = 0;
6429 6430 }
6430 6431 }
6431 6432
6432 6433 /*
6433 6434 * *if* the (hard) link count goes to 0, then we set the CDESTROY
6434 6435 * flag on the cnode. The cached object will then be destroyed
6435 6436 * at inactive time where the chickens come home to roost :-)
6436 6437 * The link cnt for directories is bumped down by 2 'cause the "."
6437 6438 * entry has to be elided too ! The link cnt for the parent goes down
6438 6439 * by 1 (because of "..").
6439 6440 */
6440 6441 cp->c_attr.va_nlink -= 2;
6441 6442 dcp->c_attr.va_nlink--;
6442 6443 if (cp->c_attr.va_nlink == 0) {
6443 6444 cp->c_flags |= CN_DESTROY;
6444 6445 } else {
6445 6446 cp->c_flags |= CN_UPDATED;
6446 6447 }
6447 6448 dcp->c_flags |= CN_UPDATED;
6448 6449
6449 6450 dnlc_purge_vp(vp);
6450 6451 CFSOP_MODIFY_COBJECT(fscp, dcp, cr);
6451 6452
6452 6453 out:
6453 6454 mutex_exit(&cp->c_statelock);
6454 6455 mutex_exit(&dcp->c_statelock);
6455 6456 rw_exit(&dcp->c_rwlock);
6456 6457
6457 6458 return (error);
6458 6459 }
6459 6460
6460 6461 static int
6461 6462 /*ARGSUSED*/
6462 6463 cachefs_rmdir_disconnected(vnode_t *dvp, char *nm, vnode_t *cdir,
6463 6464 cred_t *cr, vnode_t *vp)
6464 6465 {
6465 6466 cnode_t *dcp = VTOC(dvp);
6466 6467 cnode_t *cp = VTOC(vp);
6467 6468 fscache_t *fscp = C_TO_FSCACHE(dcp);
6468 6469 int error = 0;
6469 6470 off_t commit = 0;
6470 6471 timestruc_t current_time;
6471 6472
6472 6473 if (CFS_ISFS_WRITE_AROUND(fscp))
6473 6474 return (ETIMEDOUT);
6474 6475
6475 6476 rw_enter(&dcp->c_rwlock, RW_WRITER);
6476 6477 mutex_enter(&dcp->c_statelock);
6477 6478 mutex_enter(&cp->c_statelock);
6478 6479
6479 6480 /* both directories must be populated */
6480 6481 if (((dcp->c_metadata.md_flags & MD_POPULATED) == 0) ||
6481 6482 ((cp->c_metadata.md_flags & MD_POPULATED) == 0)) {
6482 6483 error = ETIMEDOUT;
6483 6484 goto out;
6484 6485 }
6485 6486
6486 6487 /* if sticky bit set on the dir, more access checks to perform */
6487 6488 if (error = cachefs_stickyrmchk(dcp, cp, cr)) {
6488 6489 goto out;
6489 6490 }
6490 6491
6491 6492 /* make sure dir is empty */
6492 6493 if (cp->c_attr.va_nlink > 2) {
6493 6494 error = cachefs_dir_empty(cp);
6494 6495 if (error) {
6495 6496 if (error == ENOTDIR)
6496 6497 error = ETIMEDOUT;
6497 6498 goto out;
6498 6499 }
6499 6500 cachefs_modified(cp);
6500 6501 }
6501 6502 cachefs_modified(dcp);
6502 6503
6503 6504 /* log the operation */
6504 6505 commit = cachefs_dlog_rmdir(fscp, dcp, nm, cp, cr);
6505 6506 if (commit == 0) {
6506 6507 error = ENOSPC;
6507 6508 goto out;
6508 6509 }
6509 6510
6510 6511 /* remove name from parent dir */
6511 6512 error = cachefs_dir_rmentry(dcp, nm);
6512 6513 if (error == ENOTDIR) {
6513 6514 error = ETIMEDOUT;
6514 6515 goto out;
6515 6516 }
6516 6517 if (error)
6517 6518 goto out;
6518 6519
6519 6520 gethrestime(¤t_time);
6520 6521
6521 6522 /* update deleted dir values */
6522 6523 cp->c_attr.va_nlink -= 2;
6523 6524 if (cp->c_attr.va_nlink == 0)
6524 6525 cp->c_flags |= CN_DESTROY;
6525 6526 else {
6526 6527 cp->c_metadata.md_localctime = current_time;
6527 6528 cp->c_metadata.md_flags |= MD_LOCALCTIME;
6528 6529 cp->c_flags |= CN_UPDATED;
6529 6530 }
6530 6531
6531 6532 /* update parent values */
6532 6533 dcp->c_metadata.md_localctime = current_time;
6533 6534 dcp->c_metadata.md_localmtime = current_time;
6534 6535 dcp->c_metadata.md_flags |= MD_LOCALCTIME | MD_LOCALMTIME;
6535 6536 dcp->c_attr.va_nlink--;
6536 6537 dcp->c_flags |= CN_UPDATED;
6537 6538
6538 6539 out:
6539 6540 mutex_exit(&cp->c_statelock);
6540 6541 mutex_exit(&dcp->c_statelock);
6541 6542 rw_exit(&dcp->c_rwlock);
6542 6543 if (commit) {
6543 6544 /* commit the log entry */
6544 6545 if (cachefs_dlog_commit(fscp, commit, error)) {
6545 6546 /*EMPTY*/
6546 6547 /* XXX bob: fix on panic */
6547 6548 }
6548 6549 dnlc_purge_vp(vp);
6549 6550 }
6550 6551 return (error);
6551 6552 }
6552 6553
6553 6554 /*ARGSUSED*/
6554 6555 static int
6555 6556 cachefs_symlink(vnode_t *dvp, char *lnm, vattr_t *tva,
6556 6557 char *tnm, cred_t *cr, caller_context_t *ct, int flags)
6557 6558 {
6558 6559 cnode_t *dcp = VTOC(dvp);
6559 6560 fscache_t *fscp = C_TO_FSCACHE(dcp);
6560 6561 cachefscache_t *cachep = fscp->fs_cache;
6561 6562 int error = 0;
6562 6563 int held = 0;
6563 6564 int connected = 0;
6564 6565
6565 6566 #ifdef CFSDEBUG
6566 6567 CFS_DEBUG(CFSDEBUG_VOPS)
6567 6568 printf("cachefs_symlink: ENTER dvp %p lnm %s tnm %s\n",
6568 6569 (void *)dvp, lnm, tnm);
6569 6570 #endif
6570 6571
6571 6572 if (getzoneid() != GLOBAL_ZONEID) {
6572 6573 error = EPERM;
6573 6574 goto out;
6574 6575 }
6575 6576
6576 6577 if (fscp->fs_cache->c_flags & CACHE_NOCACHE)
6577 6578 ASSERT(dcp->c_flags & CN_NOCACHE);
6578 6579
6579 6580 /*
6580 6581 * Cachefs only provides pass-through support for NFSv4,
6581 6582 * and all vnode operations are passed through to the
6582 6583 * back file system. For NFSv4 pass-through to work, only
6583 6584 * connected operation is supported, the cnode backvp must
6584 6585 * exist, and cachefs optional (eg., disconnectable) flags
6585 6586 * are turned off. Assert these conditions to ensure that
6586 6587 * the backfilesystem is called for the symlink operation.
6587 6588 */
6588 6589 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
6589 6590 CFS_BACKFS_NFSV4_ASSERT_CNODE(dcp);
6590 6591
6591 6592 for (;;) {
6592 6593 /* get (or renew) access to the file system */
6593 6594 if (held) {
6594 6595 /* Won't loop with NFSv4 connected behavior */
6595 6596 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
6596 6597 rw_exit(&dcp->c_rwlock);
6597 6598 cachefs_cd_release(fscp);
6598 6599 held = 0;
6599 6600 }
6600 6601 error = cachefs_cd_access(fscp, connected, 1);
6601 6602 if (error)
6602 6603 break;
6603 6604 rw_enter(&dcp->c_rwlock, RW_WRITER);
6604 6605 held = 1;
6605 6606
6606 6607 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
6607 6608 error = cachefs_symlink_connected(dvp, lnm, tva,
6608 6609 tnm, cr);
6609 6610 if (CFS_TIMEOUT(fscp, error)) {
6610 6611 rw_exit(&dcp->c_rwlock);
6611 6612 cachefs_cd_release(fscp);
6612 6613 held = 0;
6613 6614 cachefs_cd_timedout(fscp);
6614 6615 connected = 0;
6615 6616 continue;
6616 6617 }
6617 6618 } else {
6618 6619 error = cachefs_symlink_disconnected(dvp, lnm, tva,
6619 6620 tnm, cr);
6620 6621 if (CFS_TIMEOUT(fscp, error)) {
6621 6622 connected = 1;
6622 6623 continue;
6623 6624 }
6624 6625 }
6625 6626 break;
6626 6627 }
6627 6628
6628 6629 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_SYMLINK))
6629 6630 cachefs_log_symlink(cachep, error, fscp->fs_cfsvfsp,
6630 6631 &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno,
6631 6632 crgetuid(cr), (uint_t)strlen(tnm));
6632 6633
6633 6634 if (held) {
6634 6635 rw_exit(&dcp->c_rwlock);
6635 6636 cachefs_cd_release(fscp);
6636 6637 }
6637 6638
6638 6639 #ifdef CFS_CD_DEBUG
6639 6640 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
6640 6641 #endif
6641 6642 out:
6642 6643 #ifdef CFSDEBUG
6643 6644 CFS_DEBUG(CFSDEBUG_VOPS)
6644 6645 printf("cachefs_symlink: EXIT error = %d\n", error);
6645 6646 #endif
6646 6647 return (error);
6647 6648 }
6648 6649
6649 6650 static int
6650 6651 cachefs_symlink_connected(vnode_t *dvp, char *lnm, vattr_t *tva,
6651 6652 char *tnm, cred_t *cr)
6652 6653 {
6653 6654 cnode_t *dcp = VTOC(dvp);
6654 6655 fscache_t *fscp = C_TO_FSCACHE(dcp);
6655 6656 int error = 0;
6656 6657 vnode_t *backvp = NULL;
6657 6658 cnode_t *newcp = NULL;
6658 6659 struct vattr va;
6659 6660 struct fid cookie;
6660 6661 cfs_cid_t cid;
6661 6662 uint32_t valid_fid;
6662 6663
6663 6664 mutex_enter(&dcp->c_statelock);
6664 6665
6665 6666 if (dcp->c_backvp == NULL) {
6666 6667 error = cachefs_getbackvp(fscp, dcp);
6667 6668 if (error) {
6668 6669 cachefs_nocache(dcp);
6669 6670 mutex_exit(&dcp->c_statelock);
6670 6671 goto out;
6671 6672 }
6672 6673 }
6673 6674
6674 6675 error = CFSOP_CHECK_COBJECT(fscp, dcp, 0, cr);
6675 6676 if (error) {
6676 6677 mutex_exit(&dcp->c_statelock);
6677 6678 goto out;
6678 6679 }
6679 6680 CFS_DPRINT_BACKFS_NFSV4(fscp,
6680 6681 ("cachefs_symlink (nfsv4): dcp %p, dbackvp %p, "
6681 6682 "lnm %s, tnm %s\n", dcp, dcp->c_backvp, lnm, tnm));
6682 6683 error = VOP_SYMLINK(dcp->c_backvp, lnm, tva, tnm, cr, NULL, 0);
6683 6684 if (error) {
6684 6685 mutex_exit(&dcp->c_statelock);
6685 6686 goto out;
6686 6687 }
6687 6688 if ((dcp->c_filegrp->fg_flags & CFS_FG_WRITE) == 0 &&
6688 6689 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
6689 6690 cachefs_nocache(dcp);
6690 6691 mutex_exit(&dcp->c_statelock);
6691 6692 goto out;
6692 6693 }
6693 6694
6694 6695 CFSOP_MODIFY_COBJECT(fscp, dcp, cr);
6695 6696
6696 6697 /* lookup the symlink we just created and get its fid and attrs */
6697 6698 (void) VOP_LOOKUP(dcp->c_backvp, lnm, &backvp, NULL, 0, NULL, cr,
6698 6699 NULL, NULL, NULL);
6699 6700 if (backvp == NULL) {
6700 6701 if (CFS_ISFS_BACKFS_NFSV4(fscp) == 0)
6701 6702 cachefs_nocache(dcp);
6702 6703 mutex_exit(&dcp->c_statelock);
6703 6704 goto out;
6704 6705 }
6705 6706
6706 6707 valid_fid = (CFS_ISFS_BACKFS_NFSV4(fscp) ? FALSE : TRUE);
6707 6708 error = cachefs_getcookie(backvp, &cookie, &va, cr, valid_fid);
6708 6709 if (error) {
6709 6710 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
6710 6711 error = 0;
6711 6712 cachefs_nocache(dcp);
6712 6713 mutex_exit(&dcp->c_statelock);
6713 6714 goto out;
6714 6715 }
6715 6716 cid.cid_fileno = va.va_nodeid;
6716 6717 cid.cid_flags = 0;
6717 6718
6718 6719 /* if the dir is cached, add the symlink to it */
6719 6720 if (CFS_ISFS_NONSHARED(fscp) &&
6720 6721 (dcp->c_metadata.md_flags & MD_POPULATED)) {
6721 6722 error = cachefs_dir_enter(dcp, lnm, &cookie, &cid, SM_ASYNC);
6722 6723 if (error) {
6723 6724 cachefs_nocache(dcp);
6724 6725 error = 0;
6725 6726 }
6726 6727 }
6727 6728 mutex_exit(&dcp->c_statelock);
6728 6729
6729 6730 /* make the cnode for the sym link */
6730 6731 error = cachefs_cnode_make(&cid, fscp, (valid_fid ? &cookie : NULL),
6731 6732 &va, backvp, cr, 0, &newcp);
6732 6733 if (error) {
6733 6734 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
6734 6735 cachefs_nocache(dcp);
6735 6736 error = 0;
6736 6737 goto out;
6737 6738 }
6738 6739
6739 6740 /* try to cache the symlink contents */
6740 6741 rw_enter(&newcp->c_rwlock, RW_WRITER);
6741 6742 mutex_enter(&newcp->c_statelock);
6742 6743
6743 6744 /*
6744 6745 * try to cache the sym link, note that its a noop if NOCACHE
6745 6746 * or NFSv4 is set
6746 6747 */
6747 6748 error = cachefs_stuffsymlink(newcp, tnm, (int)newcp->c_size);
6748 6749 if (error) {
6749 6750 cachefs_nocache(newcp);
6750 6751 error = 0;
6751 6752 }
6752 6753 mutex_exit(&newcp->c_statelock);
6753 6754 rw_exit(&newcp->c_rwlock);
6754 6755
6755 6756 out:
6756 6757 if (backvp)
6757 6758 VN_RELE(backvp);
6758 6759 if (newcp)
6759 6760 VN_RELE(CTOV(newcp));
6760 6761 return (error);
6761 6762 }
6762 6763
6763 6764 static int
6764 6765 cachefs_symlink_disconnected(vnode_t *dvp, char *lnm, vattr_t *tva,
6765 6766 char *tnm, cred_t *cr)
6766 6767 {
6767 6768 cnode_t *dcp = VTOC(dvp);
6768 6769 fscache_t *fscp = C_TO_FSCACHE(dcp);
6769 6770 int error;
6770 6771 cnode_t *newcp = NULL;
6771 6772 struct vattr va;
6772 6773 timestruc_t current_time;
6773 6774 off_t commit = 0;
6774 6775
6775 6776 if (CFS_ISFS_WRITE_AROUND(fscp))
6776 6777 return (ETIMEDOUT);
6777 6778
6778 6779 mutex_enter(&dcp->c_statelock);
6779 6780
6780 6781 /* check permissions */
6781 6782 if (error = cachefs_access_local(dcp, (VEXEC|VWRITE), cr)) {
6782 6783 mutex_exit(&dcp->c_statelock);
6783 6784 goto out;
6784 6785 }
6785 6786
6786 6787 /* the directory front file must be populated */
6787 6788 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
6788 6789 error = ETIMEDOUT;
6789 6790 mutex_exit(&dcp->c_statelock);
6790 6791 goto out;
6791 6792 }
6792 6793
6793 6794 /* make sure lnm does not already exist in the directory */
6794 6795 error = cachefs_dir_look(dcp, lnm, NULL, NULL, NULL, NULL);
6795 6796 if (error == ENOTDIR) {
6796 6797 error = ETIMEDOUT;
6797 6798 mutex_exit(&dcp->c_statelock);
6798 6799 goto out;
6799 6800 }
6800 6801 if (error != ENOENT) {
6801 6802 error = EEXIST;
6802 6803 mutex_exit(&dcp->c_statelock);
6803 6804 goto out;
6804 6805 }
6805 6806
6806 6807 /* make up a reasonable set of attributes */
6807 6808 cachefs_attr_setup(tva, &va, dcp, cr);
6808 6809 va.va_type = VLNK;
6809 6810 va.va_mode |= S_IFLNK;
6810 6811 va.va_size = strlen(tnm);
6811 6812
6812 6813 mutex_exit(&dcp->c_statelock);
6813 6814
6814 6815 /* create the cnode */
6815 6816 error = cachefs_cnode_create(fscp, &va, 0, &newcp);
6816 6817 if (error)
6817 6818 goto out;
6818 6819
6819 6820 rw_enter(&newcp->c_rwlock, RW_WRITER);
6820 6821 mutex_enter(&newcp->c_statelock);
6821 6822
6822 6823 error = cachefs_dlog_cidmap(fscp);
6823 6824 if (error) {
6824 6825 mutex_exit(&newcp->c_statelock);
6825 6826 rw_exit(&newcp->c_rwlock);
6826 6827 error = ENOSPC;
6827 6828 goto out;
6828 6829 }
6829 6830
6830 6831 cachefs_creategid(dcp, newcp, tva, cr);
6831 6832 mutex_enter(&dcp->c_statelock);
6832 6833 cachefs_createacl(dcp, newcp);
6833 6834 mutex_exit(&dcp->c_statelock);
6834 6835 gethrestime(¤t_time);
6835 6836 newcp->c_metadata.md_vattr.va_atime = current_time;
6836 6837 newcp->c_metadata.md_localctime = current_time;
6837 6838 newcp->c_metadata.md_localmtime = current_time;
6838 6839 newcp->c_metadata.md_flags |= MD_MAPPING | MD_LOCALMTIME |
6839 6840 MD_LOCALCTIME;
6840 6841 newcp->c_flags |= CN_UPDATED;
6841 6842
6842 6843 /* log the operation */
6843 6844 commit = cachefs_dlog_symlink(fscp, dcp, newcp, lnm, tva, tnm, cr);
6844 6845 if (commit == 0) {
6845 6846 mutex_exit(&newcp->c_statelock);
6846 6847 rw_exit(&newcp->c_rwlock);
6847 6848 error = ENOSPC;
6848 6849 goto out;
6849 6850 }
6850 6851
6851 6852 /* store the symlink contents */
6852 6853 error = cachefs_stuffsymlink(newcp, tnm, (int)newcp->c_size);
6853 6854 if (error) {
6854 6855 mutex_exit(&newcp->c_statelock);
6855 6856 rw_exit(&newcp->c_rwlock);
6856 6857 goto out;
6857 6858 }
6858 6859 if (cachefs_modified_alloc(newcp)) {
6859 6860 mutex_exit(&newcp->c_statelock);
6860 6861 rw_exit(&newcp->c_rwlock);
6861 6862 error = ENOSPC;
6862 6863 goto out;
6863 6864 }
6864 6865
6865 6866 /*
6866 6867 * write the metadata now rather than waiting until
6867 6868 * inactive so that if there's no space we can let
6868 6869 * the caller know.
6869 6870 */
6870 6871 if (newcp->c_flags & CN_ALLOC_PENDING) {
6871 6872 if (newcp->c_filegrp->fg_flags & CFS_FG_ALLOC_ATTR) {
6872 6873 (void) filegrp_allocattr(newcp->c_filegrp);
6873 6874 }
6874 6875 error = filegrp_create_metadata(newcp->c_filegrp,
6875 6876 &newcp->c_metadata, &newcp->c_id);
6876 6877 if (error) {
6877 6878 mutex_exit(&newcp->c_statelock);
6878 6879 rw_exit(&newcp->c_rwlock);
6879 6880 goto out;
6880 6881 }
6881 6882 newcp->c_flags &= ~CN_ALLOC_PENDING;
6882 6883 }
6883 6884 error = filegrp_write_metadata(newcp->c_filegrp,
6884 6885 &newcp->c_id, &newcp->c_metadata);
6885 6886 if (error) {
6886 6887 mutex_exit(&newcp->c_statelock);
6887 6888 rw_exit(&newcp->c_rwlock);
6888 6889 goto out;
6889 6890 }
6890 6891 mutex_exit(&newcp->c_statelock);
6891 6892 rw_exit(&newcp->c_rwlock);
6892 6893
6893 6894 mutex_enter(&dcp->c_statelock);
6894 6895
6895 6896 /* enter the new file in the directory */
6896 6897 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
6897 6898 error = ETIMEDOUT;
6898 6899 mutex_exit(&dcp->c_statelock);
6899 6900 goto out;
6900 6901 }
6901 6902 cachefs_modified(dcp);
6902 6903 error = cachefs_dir_enter(dcp, lnm, &newcp->c_metadata.md_cookie,
6903 6904 &newcp->c_id, SM_ASYNC);
6904 6905 if (error) {
6905 6906 mutex_exit(&dcp->c_statelock);
6906 6907 goto out;
6907 6908 }
6908 6909
6909 6910 /* update parent dir times */
6910 6911 dcp->c_metadata.md_localctime = current_time;
6911 6912 dcp->c_metadata.md_localmtime = current_time;
6912 6913 dcp->c_metadata.md_flags |= MD_LOCALMTIME | MD_LOCALCTIME;
6913 6914 dcp->c_flags |= CN_UPDATED;
6914 6915 mutex_exit(&dcp->c_statelock);
6915 6916
6916 6917 out:
6917 6918 if (commit) {
6918 6919 /* commit the log entry */
6919 6920 if (cachefs_dlog_commit(fscp, commit, error)) {
6920 6921 /*EMPTY*/
6921 6922 /* XXX bob: fix on panic */
6922 6923 }
6923 6924 }
6924 6925
6925 6926 if (error) {
6926 6927 if (newcp) {
6927 6928 mutex_enter(&newcp->c_statelock);
6928 6929 newcp->c_flags |= CN_DESTROY;
6929 6930 mutex_exit(&newcp->c_statelock);
6930 6931 }
6931 6932 }
6932 6933 if (newcp) {
6933 6934 VN_RELE(CTOV(newcp));
6934 6935 }
6935 6936
6936 6937 return (error);
6937 6938 }
6938 6939
6939 6940 /*ARGSUSED*/
6940 6941 static int
6941 6942 cachefs_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp,
6942 6943 caller_context_t *ct, int flags)
6943 6944 {
6944 6945 cnode_t *dcp = VTOC(vp);
6945 6946 fscache_t *fscp = C_TO_FSCACHE(dcp);
6946 6947 cachefscache_t *cachep = fscp->fs_cache;
6947 6948 int error = 0;
6948 6949 int held = 0;
6949 6950 int connected = 0;
6950 6951
6951 6952 #ifdef CFSDEBUG
6952 6953 CFS_DEBUG(CFSDEBUG_VOPS)
6953 6954 printf("cachefs_readdir: ENTER vp %p\n", (void *)vp);
6954 6955 #endif
6955 6956 if (getzoneid() != GLOBAL_ZONEID) {
6956 6957 error = EPERM;
6957 6958 goto out;
6958 6959 }
6959 6960
6960 6961 /*
6961 6962 * Cachefs only provides pass-through support for NFSv4,
6962 6963 * and all vnode operations are passed through to the
6963 6964 * back file system. For NFSv4 pass-through to work, only
6964 6965 * connected operation is supported, the cnode backvp must
6965 6966 * exist, and cachefs optional (eg., disconnectable) flags
6966 6967 * are turned off. Assert these conditions to ensure that
6967 6968 * the backfilesystem is called for the readdir operation.
6968 6969 */
6969 6970 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
6970 6971 CFS_BACKFS_NFSV4_ASSERT_CNODE(dcp);
6971 6972
6972 6973 for (;;) {
6973 6974 /* get (or renew) access to the file system */
6974 6975 if (held) {
6975 6976 /* Won't loop with NFSv4 connected behavior */
6976 6977 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
6977 6978 rw_exit(&dcp->c_rwlock);
6978 6979 cachefs_cd_release(fscp);
6979 6980 held = 0;
6980 6981 }
6981 6982 error = cachefs_cd_access(fscp, connected, 0);
6982 6983 if (error)
6983 6984 break;
6984 6985 rw_enter(&dcp->c_rwlock, RW_READER);
6985 6986 held = 1;
6986 6987
6987 6988 /* quit if link count of zero (posix) */
6988 6989 if (dcp->c_attr.va_nlink == 0) {
6989 6990 if (eofp)
6990 6991 *eofp = 1;
6991 6992 error = 0;
6992 6993 break;
6993 6994 }
6994 6995
6995 6996 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
6996 6997 error = cachefs_readdir_connected(vp, uiop, cr,
6997 6998 eofp);
6998 6999 if (CFS_TIMEOUT(fscp, error)) {
6999 7000 rw_exit(&dcp->c_rwlock);
7000 7001 cachefs_cd_release(fscp);
7001 7002 held = 0;
7002 7003 cachefs_cd_timedout(fscp);
7003 7004 connected = 0;
7004 7005 continue;
7005 7006 }
7006 7007 } else {
7007 7008 error = cachefs_readdir_disconnected(vp, uiop, cr,
7008 7009 eofp);
7009 7010 if (CFS_TIMEOUT(fscp, error)) {
7010 7011 if (cachefs_cd_access_miss(fscp)) {
7011 7012 error = cachefs_readdir_connected(vp,
7012 7013 uiop, cr, eofp);
7013 7014 if (!CFS_TIMEOUT(fscp, error))
7014 7015 break;
7015 7016 delay(5*hz);
7016 7017 connected = 0;
7017 7018 continue;
7018 7019 }
7019 7020 connected = 1;
7020 7021 continue;
7021 7022 }
7022 7023 }
7023 7024 break;
7024 7025 }
7025 7026
7026 7027 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_READDIR))
7027 7028 cachefs_log_readdir(cachep, error, fscp->fs_cfsvfsp,
7028 7029 &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno,
7029 7030 crgetuid(cr), uiop->uio_loffset, *eofp);
7030 7031
7031 7032 if (held) {
7032 7033 rw_exit(&dcp->c_rwlock);
7033 7034 cachefs_cd_release(fscp);
7034 7035 }
7035 7036
7036 7037 #ifdef CFS_CD_DEBUG
7037 7038 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
7038 7039 #endif
7039 7040 out:
7040 7041 #ifdef CFSDEBUG
7041 7042 CFS_DEBUG(CFSDEBUG_VOPS)
7042 7043 printf("cachefs_readdir: EXIT error = %d\n", error);
7043 7044 #endif
7044 7045
7045 7046 return (error);
7046 7047 }
7047 7048
7048 7049 static int
7049 7050 cachefs_readdir_connected(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp)
7050 7051 {
7051 7052 cnode_t *dcp = VTOC(vp);
7052 7053 int error;
7053 7054 fscache_t *fscp = C_TO_FSCACHE(dcp);
7054 7055 struct cachefs_req *rp;
7055 7056
7056 7057 mutex_enter(&dcp->c_statelock);
7057 7058
7058 7059 /* check directory consistency */
7059 7060 error = CFSOP_CHECK_COBJECT(fscp, dcp, 0, cr);
7060 7061 if (error)
7061 7062 goto out;
7062 7063 dcp->c_usage++;
7063 7064
7064 7065 /* if dir was modified, toss old contents */
7065 7066 if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
7066 7067 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
7067 7068 cachefs_inval_object(dcp);
7068 7069 }
7069 7070
7070 7071 error = 0;
7071 7072 if (((dcp->c_metadata.md_flags & MD_POPULATED) == 0) &&
7072 7073 ((dcp->c_flags & (CN_ASYNC_POPULATE | CN_NOCACHE)) == 0) &&
7073 7074 !CFS_ISFS_BACKFS_NFSV4(fscp) &&
7074 7075 (fscp->fs_cdconnected == CFS_CD_CONNECTED)) {
7075 7076
7076 7077 if (cachefs_async_okay()) {
7077 7078
7078 7079 /*
7079 7080 * Set up asynchronous request to fill this
7080 7081 * directory.
7081 7082 */
7082 7083
7083 7084 dcp->c_flags |= CN_ASYNC_POPULATE;
7084 7085
7085 7086 rp = kmem_cache_alloc(cachefs_req_cache, KM_SLEEP);
7086 7087 rp->cfs_cmd = CFS_POPULATE;
7087 7088 rp->cfs_req_u.cu_populate.cpop_vp = vp;
7088 7089 rp->cfs_cr = cr;
7089 7090
7090 7091 crhold(cr);
7091 7092 VN_HOLD(vp);
7092 7093
7093 7094 cachefs_addqueue(rp, &fscp->fs_workq);
7094 7095 } else {
7095 7096 error = cachefs_dir_fill(dcp, cr);
7096 7097 if (error != 0)
7097 7098 cachefs_nocache(dcp);
7098 7099 }
7099 7100 }
7100 7101
7101 7102 /* if front file is populated */
7102 7103 if (((dcp->c_flags & (CN_NOCACHE | CN_ASYNC_POPULATE)) == 0) &&
7103 7104 !CFS_ISFS_BACKFS_NFSV4(fscp) &&
7104 7105 (dcp->c_metadata.md_flags & MD_POPULATED)) {
7105 7106 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
7106 7107 error = cachefs_dir_read(dcp, uiop, eofp);
7107 7108 if (error == 0)
7108 7109 fscp->fs_stats.st_hits++;
7109 7110 }
7110 7111
7111 7112 /* if front file could not be used */
7112 7113 if ((error != 0) ||
7113 7114 CFS_ISFS_BACKFS_NFSV4(fscp) ||
7114 7115 (dcp->c_flags & (CN_NOCACHE | CN_ASYNC_POPULATE)) ||
7115 7116 ((dcp->c_metadata.md_flags & MD_POPULATED) == 0)) {
7116 7117
7117 7118 if (error && !(dcp->c_flags & CN_NOCACHE) &&
7118 7119 !CFS_ISFS_BACKFS_NFSV4(fscp))
7119 7120 cachefs_nocache(dcp);
7120 7121
7121 7122 /* get the back vp */
7122 7123 if (dcp->c_backvp == NULL) {
7123 7124 error = cachefs_getbackvp(fscp, dcp);
7124 7125 if (error)
7125 7126 goto out;
7126 7127 }
7127 7128
7128 7129 if (fscp->fs_inum_size > 0) {
7129 7130 error = cachefs_readback_translate(dcp, uiop, cr, eofp);
7130 7131 } else {
7131 7132 /* do the dir read from the back fs */
7132 7133 (void) VOP_RWLOCK(dcp->c_backvp,
7133 7134 V_WRITELOCK_FALSE, NULL);
7134 7135 CFS_DPRINT_BACKFS_NFSV4(fscp,
7135 7136 ("cachefs_readdir (nfsv4): "
7136 7137 "dcp %p, dbackvp %p\n", dcp, dcp->c_backvp));
7137 7138 error = VOP_READDIR(dcp->c_backvp, uiop, cr, eofp,
7138 7139 NULL, 0);
7139 7140 VOP_RWUNLOCK(dcp->c_backvp, V_WRITELOCK_FALSE, NULL);
7140 7141 }
7141 7142
7142 7143 if (error == 0)
7143 7144 fscp->fs_stats.st_misses++;
7144 7145 }
7145 7146
7146 7147 out:
7147 7148 mutex_exit(&dcp->c_statelock);
7148 7149
7149 7150 return (error);
7150 7151 }
7151 7152
7152 7153 static int
7153 7154 cachefs_readback_translate(cnode_t *cp, uio_t *uiop, cred_t *cr, int *eofp)
7154 7155 {
7155 7156 int error = 0;
7156 7157 fscache_t *fscp = C_TO_FSCACHE(cp);
7157 7158 caddr_t buffy = NULL;
7158 7159 int buffysize = MAXBSIZE;
7159 7160 caddr_t chrp, end;
7160 7161 ino64_t newinum;
7161 7162 struct dirent64 *de;
7162 7163 uio_t uioin;
7163 7164 iovec_t iov;
7164 7165
7165 7166 ASSERT(cp->c_backvp != NULL);
7166 7167 ASSERT(fscp->fs_inum_size > 0);
7167 7168
7168 7169 if (uiop->uio_resid < buffysize)
7169 7170 buffysize = (int)uiop->uio_resid;
7170 7171 buffy = cachefs_kmem_alloc(buffysize, KM_SLEEP);
7171 7172
7172 7173 iov.iov_base = buffy;
7173 7174 iov.iov_len = buffysize;
7174 7175 uioin.uio_iov = &iov;
7175 7176 uioin.uio_iovcnt = 1;
7176 7177 uioin.uio_segflg = UIO_SYSSPACE;
7177 7178 uioin.uio_fmode = 0;
7178 7179 uioin.uio_extflg = UIO_COPY_CACHED;
7179 7180 uioin.uio_loffset = uiop->uio_loffset;
7180 7181 uioin.uio_resid = buffysize;
7181 7182
7182 7183 (void) VOP_RWLOCK(cp->c_backvp, V_WRITELOCK_FALSE, NULL);
7183 7184 error = VOP_READDIR(cp->c_backvp, &uioin, cr, eofp, NULL, 0);
7184 7185 VOP_RWUNLOCK(cp->c_backvp, V_WRITELOCK_FALSE, NULL);
7185 7186
7186 7187 if (error != 0)
7187 7188 goto out;
7188 7189
7189 7190 end = buffy + buffysize - uioin.uio_resid;
7190 7191
7191 7192 mutex_exit(&cp->c_statelock);
7192 7193 mutex_enter(&fscp->fs_fslock);
7193 7194
7194 7195
7195 7196 for (chrp = buffy; chrp < end; chrp += de->d_reclen) {
7196 7197 de = (dirent64_t *)chrp;
7197 7198 newinum = cachefs_inum_real2fake(fscp, de->d_ino);
7198 7199 if (newinum == 0)
7199 7200 newinum = cachefs_fileno_conflict(fscp, de->d_ino);
7200 7201 de->d_ino = newinum;
7201 7202 }
7202 7203 mutex_exit(&fscp->fs_fslock);
7203 7204 mutex_enter(&cp->c_statelock);
7204 7205
7205 7206 error = uiomove(buffy, end - buffy, UIO_READ, uiop);
7206 7207 uiop->uio_loffset = uioin.uio_loffset;
7207 7208
7208 7209 out:
7209 7210
7210 7211 if (buffy != NULL)
7211 7212 cachefs_kmem_free(buffy, buffysize);
7212 7213
7213 7214 return (error);
7214 7215 }
7215 7216
7216 7217 static int
7217 7218 /*ARGSUSED*/
7218 7219 cachefs_readdir_disconnected(vnode_t *vp, uio_t *uiop, cred_t *cr,
7219 7220 int *eofp)
7220 7221 {
7221 7222 cnode_t *dcp = VTOC(vp);
7222 7223 int error;
7223 7224
7224 7225 mutex_enter(&dcp->c_statelock);
7225 7226 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
7226 7227 error = ETIMEDOUT;
7227 7228 } else {
7228 7229 error = cachefs_dir_read(dcp, uiop, eofp);
7229 7230 if (error == ENOTDIR)
7230 7231 error = ETIMEDOUT;
7231 7232 }
7232 7233 mutex_exit(&dcp->c_statelock);
7233 7234
7234 7235 return (error);
7235 7236 }
7236 7237
7237 7238 /*ARGSUSED*/
7238 7239 static int
7239 7240 cachefs_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct)
7240 7241 {
7241 7242 int error = 0;
7242 7243 struct cnode *cp = VTOC(vp);
7243 7244 fscache_t *fscp = C_TO_FSCACHE(cp);
7244 7245
7245 7246 /*
7246 7247 * Cachefs only provides pass-through support for NFSv4,
7247 7248 * and all vnode operations are passed through to the
7248 7249 * back file system. For NFSv4 pass-through to work, only
7249 7250 * connected operation is supported, the cnode backvp must
7250 7251 * exist, and cachefs optional (eg., disconnectable) flags
7251 7252 * are turned off. Assert these conditions, then bail
7252 7253 * as NFSv4 doesn't support VOP_FID.
7253 7254 */
7254 7255 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
7255 7256 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
7256 7257 if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
7257 7258 return (ENOTSUP);
7258 7259 }
7259 7260
7260 7261 mutex_enter(&cp->c_statelock);
7261 7262 if (fidp->fid_len < cp->c_metadata.md_cookie.fid_len) {
7262 7263 fidp->fid_len = cp->c_metadata.md_cookie.fid_len;
7263 7264 error = ENOSPC;
7264 7265 } else {
7265 7266 bcopy(cp->c_metadata.md_cookie.fid_data, fidp->fid_data,
7266 7267 cp->c_metadata.md_cookie.fid_len);
7267 7268 fidp->fid_len = cp->c_metadata.md_cookie.fid_len;
7268 7269 }
7269 7270 mutex_exit(&cp->c_statelock);
7270 7271 return (error);
7271 7272 }
7272 7273
7273 7274 /* ARGSUSED2 */
7274 7275 static int
7275 7276 cachefs_rwlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
7276 7277 {
7277 7278 cnode_t *cp = VTOC(vp);
7278 7279
7279 7280 /*
7280 7281 * XXX - This is ifdef'ed out for now. The problem -
7281 7282 * getdents() acquires the read version of rwlock, then we come
7282 7283 * into cachefs_readdir() and that wants to acquire the write version
7283 7284 * of this lock (if its going to populate the directory). This is
7284 7285 * a problem, this can be solved by introducing another lock in the
7285 7286 * cnode.
7286 7287 */
7287 7288 /* XXX */
7288 7289 if (vp->v_type != VREG)
7289 7290 return (-1);
7290 7291 if (write_lock)
7291 7292 rw_enter(&cp->c_rwlock, RW_WRITER);
7292 7293 else
7293 7294 rw_enter(&cp->c_rwlock, RW_READER);
7294 7295 return (write_lock);
7295 7296 }
7296 7297
7297 7298 /* ARGSUSED */
7298 7299 static void
7299 7300 cachefs_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
7300 7301 {
7301 7302 cnode_t *cp = VTOC(vp);
7302 7303 if (vp->v_type != VREG)
7303 7304 return;
7304 7305 rw_exit(&cp->c_rwlock);
7305 7306 }
7306 7307
7307 7308 /* ARGSUSED */
7308 7309 static int
7309 7310 cachefs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp,
7310 7311 caller_context_t *ct)
7311 7312 {
7312 7313 return (0);
7313 7314 }
7314 7315
7315 7316 static int cachefs_lostpage = 0;
7316 7317 /*
7317 7318 * Return all the pages from [off..off+len] in file
7318 7319 */
7319 7320 /*ARGSUSED*/
7320 7321 static int
7321 7322 cachefs_getpage(struct vnode *vp, offset_t off, size_t len,
7322 7323 uint_t *protp, struct page *pl[], size_t plsz, struct seg *seg,
7323 7324 caddr_t addr, enum seg_rw rw, cred_t *cr, caller_context_t *ct)
7324 7325 {
7325 7326 cnode_t *cp = VTOC(vp);
7326 7327 int error;
7327 7328 fscache_t *fscp = C_TO_FSCACHE(cp);
7328 7329 cachefscache_t *cachep = fscp->fs_cache;
7329 7330 int held = 0;
7330 7331 int connected = 0;
7331 7332
7332 7333 #ifdef CFSDEBUG
7333 7334 u_offset_t offx = (u_offset_t)off;
7334 7335
7335 7336 CFS_DEBUG(CFSDEBUG_VOPS)
7336 7337 printf("cachefs_getpage: ENTER vp %p off %lld len %lu rw %d\n",
7337 7338 (void *)vp, offx, len, rw);
7338 7339 #endif
7339 7340 if (getzoneid() != GLOBAL_ZONEID) {
7340 7341 error = EPERM;
7341 7342 goto out;
7342 7343 }
7343 7344
7344 7345 if (vp->v_flag & VNOMAP) {
7345 7346 error = ENOSYS;
7346 7347 goto out;
7347 7348 }
7348 7349
7349 7350 /* Call backfilesystem if NFSv4 */
7350 7351 if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
7351 7352 error = cachefs_getpage_backfs_nfsv4(vp, off, len, protp, pl,
7352 7353 plsz, seg, addr, rw, cr);
7353 7354 goto out;
7354 7355 }
7355 7356
7356 7357 /* XXX sam: make this do an async populate? */
7357 7358 if (pl == NULL) {
7358 7359 error = 0;
7359 7360 goto out;
7360 7361 }
7361 7362 if (protp != NULL)
7362 7363 *protp = PROT_ALL;
7363 7364
7364 7365 for (;;) {
7365 7366 /* get (or renew) access to the file system */
7366 7367 if (held) {
7367 7368 cachefs_cd_release(fscp);
7368 7369 held = 0;
7369 7370 }
7370 7371 error = cachefs_cd_access(fscp, connected, 0);
7371 7372 if (error)
7372 7373 break;
7373 7374 held = 1;
7374 7375
7375 7376 /*
7376 7377 * If we are getting called as a side effect of a
7377 7378 * cachefs_write()
↓ open down ↓ |
7345 lines elided |
↑ open up ↑ |
7378 7379 * operation the local file size might not be extended yet.
7379 7380 * In this case we want to be able to return pages of zeroes.
7380 7381 */
7381 7382 if ((u_offset_t)off + len >
7382 7383 ((cp->c_size + PAGEOFFSET) & (offset_t)PAGEMASK)) {
7383 7384 if (seg != segkmap) {
7384 7385 error = EFAULT;
7385 7386 break;
7386 7387 }
7387 7388 }
7388 - if (len <= PAGESIZE)
7389 - error = cachefs_getapage(vp, (u_offset_t)off, len,
7390 - protp, pl, plsz, seg, addr, rw, cr);
7391 - else
7392 - error = pvn_getpages(cachefs_getapage, vp,
7393 - (u_offset_t)off, len, protp, pl, plsz, seg, addr,
7394 - rw, cr);
7389 + error = pvn_getpages(cachefs_getapage, vp, (u_offset_t)off,
7390 + len, protp, pl, plsz, seg, addr, rw, cr);
7395 7391 if (error == 0)
7396 7392 break;
7397 7393
7398 7394 if (((cp->c_flags & CN_NOCACHE) && (error == ENOSPC)) ||
7399 7395 error == EAGAIN) {
7400 7396 connected = 0;
7401 7397 continue;
7402 7398 }
7403 7399 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
7404 7400 if (CFS_TIMEOUT(fscp, error)) {
7405 7401 cachefs_cd_release(fscp);
7406 7402 held = 0;
7407 7403 cachefs_cd_timedout(fscp);
7408 7404 connected = 0;
7409 7405 continue;
7410 7406 }
7411 7407 } else {
7412 7408 if (CFS_TIMEOUT(fscp, error)) {
7413 7409 if (cachefs_cd_access_miss(fscp)) {
7414 - if (len <= PAGESIZE)
7415 - error = cachefs_getapage_back(
7416 - vp, (u_offset_t)off,
7417 - len, protp, pl,
7418 - plsz, seg, addr, rw, cr);
7419 - else
7420 - error = pvn_getpages(
7421 - cachefs_getapage_back, vp,
7422 - (u_offset_t)off, len,
7423 - protp, pl,
7424 - plsz, seg, addr, rw, cr);
7410 + error = pvn_getpages(
7411 + cachefs_getapage_back, vp,
7412 + (u_offset_t)off, len, protp, pl,
7413 + plsz, seg, addr, rw, cr);
7425 7414 if (!CFS_TIMEOUT(fscp, error) &&
7426 7415 (error != EAGAIN))
7427 7416 break;
7428 7417 delay(5*hz);
7429 7418 connected = 0;
7430 7419 continue;
7431 7420 }
7432 7421 connected = 1;
7433 7422 continue;
7434 7423 }
7435 7424 }
7436 7425 break;
7437 7426 }
7438 7427
7439 7428 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_GETPAGE))
7440 7429 cachefs_log_getpage(cachep, error, vp->v_vfsp,
7441 7430 &cp->c_metadata.md_cookie, cp->c_id.cid_fileno,
7442 7431 crgetuid(cr), off, len);
7443 7432
7444 7433 if (held) {
7445 7434 cachefs_cd_release(fscp);
7446 7435 }
7447 7436
7448 7437 out:
7449 7438 #ifdef CFS_CD_DEBUG
7450 7439 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
7451 7440 #endif
7452 7441 #ifdef CFSDEBUG
7453 7442 CFS_DEBUG(CFSDEBUG_VOPS)
7454 7443 printf("cachefs_getpage: EXIT vp %p error %d\n",
7455 7444 (void *)vp, error);
7456 7445 #endif
7457 7446 return (error);
7458 7447 }
7459 7448
7460 7449 /*
7461 7450 * cachefs_getpage_backfs_nfsv4
7462 7451 *
7463 7452 * Call NFSv4 back filesystem to handle the getpage (cachefs
7464 7453 * pass-through support for NFSv4).
7465 7454 */
7466 7455 static int
7467 7456 cachefs_getpage_backfs_nfsv4(struct vnode *vp, offset_t off, size_t len,
7468 7457 uint_t *protp, struct page *pl[], size_t plsz,
7469 7458 struct seg *seg, caddr_t addr, enum seg_rw rw,
7470 7459 cred_t *cr)
7471 7460 {
7472 7461 cnode_t *cp = VTOC(vp);
7473 7462 fscache_t *fscp = C_TO_FSCACHE(cp);
7474 7463 vnode_t *backvp;
7475 7464 int error;
7476 7465
7477 7466 /*
7478 7467 * For NFSv4 pass-through to work, only connected operation is
7479 7468 * supported, the cnode backvp must exist, and cachefs optional
7480 7469 * (eg., disconnectable) flags are turned off. Assert these
7481 7470 * conditions for the getpage operation.
7482 7471 */
7483 7472 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
7484 7473 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
7485 7474
7486 7475 /* Call backfs vnode op after extracting backvp */
7487 7476 mutex_enter(&cp->c_statelock);
7488 7477 backvp = cp->c_backvp;
7489 7478 mutex_exit(&cp->c_statelock);
7490 7479
↓ open down ↓ |
56 lines elided |
↑ open up ↑ |
7491 7480 CFS_DPRINT_BACKFS_NFSV4(fscp,
7492 7481 ("cachefs_getpage_backfs_nfsv4: cnode %p, backvp %p\n",
7493 7482 cp, backvp));
7494 7483 error = VOP_GETPAGE(backvp, off, len, protp, pl, plsz, seg,
7495 7484 addr, rw, cr, NULL);
7496 7485
7497 7486 return (error);
7498 7487 }
7499 7488
7500 7489 /*
7501 - * Called from pvn_getpages or cachefs_getpage to get a particular page.
7490 + * Called from pvn_getpages to get a particular page.
7502 7491 */
7503 7492 /*ARGSUSED*/
7504 7493 static int
7505 7494 cachefs_getapage(struct vnode *vp, u_offset_t off, size_t len, uint_t *protp,
7506 7495 struct page *pl[], size_t plsz, struct seg *seg, caddr_t addr,
7507 7496 enum seg_rw rw, cred_t *cr)
7508 7497 {
7509 7498 cnode_t *cp = VTOC(vp);
7510 7499 page_t **ppp, *pp = NULL;
7511 7500 fscache_t *fscp = C_TO_FSCACHE(cp);
7512 7501 cachefscache_t *cachep = fscp->fs_cache;
7513 7502 int error = 0;
7514 7503 struct page **ourpl;
7515 7504 struct page *ourstackpl[17]; /* see ASSERT() below for 17 */
7516 7505 int index = 0;
7517 7506 int downgrade;
7518 7507 int have_statelock = 0;
7519 7508 u_offset_t popoff;
7520 7509 size_t popsize = 0;
7521 7510
7522 7511 /*LINTED*/
7523 7512 ASSERT(((DEF_POP_SIZE / PAGESIZE) + 1) <= 17);
7524 7513
7525 7514 if (fscp->fs_info.fi_popsize > DEF_POP_SIZE)
7526 7515 ourpl = cachefs_kmem_alloc(sizeof (struct page *) *
7527 7516 ((fscp->fs_info.fi_popsize / PAGESIZE) + 1), KM_SLEEP);
7528 7517 else
7529 7518 ourpl = ourstackpl;
7530 7519
7531 7520 ourpl[0] = NULL;
7532 7521 off = off & (offset_t)PAGEMASK;
7533 7522 again:
7534 7523 /*
7535 7524 * Look for the page
7536 7525 */
7537 7526 if (page_exists(vp, off) == 0) {
7538 7527 /*
7539 7528 * Need to do work to get the page.
7540 7529 * Grab our lock because we are going to
7541 7530 * modify the state of the cnode.
7542 7531 */
7543 7532 if (! have_statelock) {
7544 7533 mutex_enter(&cp->c_statelock);
7545 7534 have_statelock = 1;
7546 7535 }
7547 7536 /*
7548 7537 * If we're in NOCACHE mode, we will need a backvp
7549 7538 */
7550 7539 if (cp->c_flags & CN_NOCACHE) {
7551 7540 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
7552 7541 error = ETIMEDOUT;
7553 7542 goto out;
7554 7543 }
7555 7544 if (cp->c_backvp == NULL) {
7556 7545 error = cachefs_getbackvp(fscp, cp);
7557 7546 if (error)
7558 7547 goto out;
7559 7548 }
7560 7549 error = VOP_GETPAGE(cp->c_backvp, off,
7561 7550 PAGESIZE, protp, ourpl, PAGESIZE, seg,
7562 7551 addr, S_READ, cr, NULL);
7563 7552 /*
7564 7553 * backfs returns EFAULT when we are trying for a
7565 7554 * page beyond EOF but cachefs has the knowledge that
7566 7555 * it is not beyond EOF be cause cp->c_size is
7567 7556 * greater then the offset requested.
7568 7557 */
7569 7558 if (error == EFAULT) {
7570 7559 error = 0;
7571 7560 pp = page_create_va(vp, off, PAGESIZE,
7572 7561 PG_EXCL | PG_WAIT, seg, addr);
7573 7562 if (pp == NULL)
7574 7563 goto again;
7575 7564 pagezero(pp, 0, PAGESIZE);
7576 7565 pvn_plist_init(pp, pl, plsz, off, PAGESIZE, rw);
7577 7566 goto out;
7578 7567 }
7579 7568 if (error)
7580 7569 goto out;
7581 7570 goto getpages;
7582 7571 }
7583 7572 /*
7584 7573 * We need a front file. If we can't get it,
7585 7574 * put the cnode in NOCACHE mode and try again.
7586 7575 */
7587 7576 if (cp->c_frontvp == NULL) {
7588 7577 error = cachefs_getfrontfile(cp);
7589 7578 if (error) {
7590 7579 cachefs_nocache(cp);
7591 7580 error = EAGAIN;
7592 7581 goto out;
7593 7582 }
7594 7583 }
7595 7584 /*
7596 7585 * Check if the front file needs population.
7597 7586 * If population is necessary, make sure we have a
7598 7587 * backvp as well. We will get the page from the backvp.
7599 7588 * bug 4152459-
7600 7589 * But if the file system is in disconnected mode
7601 7590 * and the file is a local file then do not check the
7602 7591 * allocmap.
7603 7592 */
7604 7593 if (((fscp->fs_cdconnected == CFS_CD_CONNECTED) ||
7605 7594 ((cp->c_metadata.md_flags & MD_LOCALFILENO) == 0)) &&
7606 7595 (cachefs_check_allocmap(cp, off) == 0)) {
7607 7596 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
7608 7597 error = ETIMEDOUT;
7609 7598 goto out;
7610 7599 }
7611 7600 if (cp->c_backvp == NULL) {
7612 7601 error = cachefs_getbackvp(fscp, cp);
7613 7602 if (error)
7614 7603 goto out;
7615 7604 }
7616 7605 if (cp->c_filegrp->fg_flags & CFS_FG_WRITE) {
7617 7606 cachefs_cluster_allocmap(off, &popoff,
7618 7607 &popsize,
7619 7608 fscp->fs_info.fi_popsize, cp);
7620 7609 if (popsize != 0) {
7621 7610 error = cachefs_populate(cp,
7622 7611 popoff, popsize,
7623 7612 cp->c_frontvp, cp->c_backvp,
7624 7613 cp->c_size, cr);
7625 7614 if (error) {
7626 7615 cachefs_nocache(cp);
7627 7616 error = EAGAIN;
7628 7617 goto out;
7629 7618 } else {
7630 7619 cp->c_flags |=
7631 7620 CN_UPDATED |
7632 7621 CN_NEED_FRONT_SYNC |
7633 7622 CN_POPULATION_PENDING;
7634 7623 }
7635 7624 popsize = popsize - (off - popoff);
7636 7625 } else {
7637 7626 popsize = PAGESIZE;
7638 7627 }
7639 7628 }
7640 7629 /* else XXX assert CN_NOCACHE? */
7641 7630 error = VOP_GETPAGE(cp->c_backvp, (offset_t)off,
7642 7631 PAGESIZE, protp, ourpl, popsize,
7643 7632 seg, addr, S_READ, cr, NULL);
7644 7633 if (error)
7645 7634 goto out;
7646 7635 fscp->fs_stats.st_misses++;
7647 7636 } else {
7648 7637 if (cp->c_flags & CN_POPULATION_PENDING) {
7649 7638 error = VOP_FSYNC(cp->c_frontvp, FSYNC, cr,
7650 7639 NULL);
7651 7640 cp->c_flags &= ~CN_POPULATION_PENDING;
7652 7641 if (error) {
7653 7642 cachefs_nocache(cp);
7654 7643 error = EAGAIN;
7655 7644 goto out;
7656 7645 }
7657 7646 }
7658 7647 /*
7659 7648 * File was populated so we get the page from the
7660 7649 * frontvp
7661 7650 */
7662 7651 error = VOP_GETPAGE(cp->c_frontvp, (offset_t)off,
7663 7652 PAGESIZE, protp, ourpl, PAGESIZE, seg, addr,
7664 7653 rw, cr, NULL);
7665 7654 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_GPFRONT))
7666 7655 cachefs_log_gpfront(cachep, error,
7667 7656 fscp->fs_cfsvfsp,
7668 7657 &cp->c_metadata.md_cookie, cp->c_fileno,
7669 7658 crgetuid(cr), off, PAGESIZE);
7670 7659 if (error) {
7671 7660 cachefs_nocache(cp);
7672 7661 error = EAGAIN;
7673 7662 goto out;
7674 7663 }
7675 7664 fscp->fs_stats.st_hits++;
7676 7665 }
7677 7666 getpages:
7678 7667 ASSERT(have_statelock);
7679 7668 if (have_statelock) {
7680 7669 mutex_exit(&cp->c_statelock);
7681 7670 have_statelock = 0;
7682 7671 }
7683 7672 downgrade = 0;
7684 7673 for (ppp = ourpl; *ppp; ppp++) {
7685 7674 if ((*ppp)->p_offset < off) {
7686 7675 index++;
7687 7676 page_unlock(*ppp);
7688 7677 continue;
7689 7678 }
7690 7679 if (PAGE_SHARED(*ppp)) {
7691 7680 if (page_tryupgrade(*ppp) == 0) {
7692 7681 for (ppp = &ourpl[index]; *ppp; ppp++)
7693 7682 page_unlock(*ppp);
7694 7683 error = EAGAIN;
7695 7684 goto out;
7696 7685 }
7697 7686 downgrade = 1;
7698 7687 }
7699 7688 ASSERT(PAGE_EXCL(*ppp));
7700 7689 (void) hat_pageunload((*ppp), HAT_FORCE_PGUNLOAD);
7701 7690 page_rename(*ppp, vp, (*ppp)->p_offset);
7702 7691 }
7703 7692 pl[0] = ourpl[index];
7704 7693 pl[1] = NULL;
7705 7694 if (downgrade) {
7706 7695 page_downgrade(ourpl[index]);
7707 7696 }
7708 7697 /* Unlock the rest of the pages from the cluster */
7709 7698 for (ppp = &ourpl[index+1]; *ppp; ppp++)
7710 7699 page_unlock(*ppp);
7711 7700 } else {
7712 7701 ASSERT(! have_statelock);
7713 7702 if (have_statelock) {
7714 7703 mutex_exit(&cp->c_statelock);
7715 7704 have_statelock = 0;
7716 7705 }
7717 7706 /* XXX SE_SHARED probably isn't what we *always* want */
7718 7707 if ((pp = page_lookup(vp, off, SE_SHARED)) == NULL) {
7719 7708 cachefs_lostpage++;
7720 7709 goto again;
7721 7710 }
7722 7711 pl[0] = pp;
7723 7712 pl[1] = NULL;
7724 7713 /* XXX increment st_hits? i don't think so, but... */
7725 7714 }
7726 7715
7727 7716 out:
7728 7717 if (have_statelock) {
7729 7718 mutex_exit(&cp->c_statelock);
7730 7719 have_statelock = 0;
7731 7720 }
7732 7721 if (fscp->fs_info.fi_popsize > DEF_POP_SIZE)
7733 7722 cachefs_kmem_free(ourpl, sizeof (struct page *) *
7734 7723 ((fscp->fs_info.fi_popsize / PAGESIZE) + 1));
7735 7724 return (error);
7736 7725 }
7737 7726
7738 7727 /* gets a page but only from the back fs */
7739 7728 /*ARGSUSED*/
7740 7729 static int
7741 7730 cachefs_getapage_back(struct vnode *vp, u_offset_t off, size_t len,
7742 7731 uint_t *protp, struct page *pl[], size_t plsz, struct seg *seg,
7743 7732 caddr_t addr, enum seg_rw rw, cred_t *cr)
7744 7733 {
7745 7734 cnode_t *cp = VTOC(vp);
7746 7735 page_t **ppp, *pp = NULL;
7747 7736 fscache_t *fscp = C_TO_FSCACHE(cp);
7748 7737 int error = 0;
7749 7738 struct page *ourpl[17];
7750 7739 int index = 0;
7751 7740 int have_statelock = 0;
7752 7741 int downgrade;
7753 7742
7754 7743 /*
7755 7744 * Grab the cnode statelock so the cnode state won't change
7756 7745 * while we're in here.
7757 7746 */
7758 7747 ourpl[0] = NULL;
7759 7748 off = off & (offset_t)PAGEMASK;
7760 7749 again:
7761 7750 if (page_exists(vp, off) == 0) {
7762 7751 if (! have_statelock) {
7763 7752 mutex_enter(&cp->c_statelock);
7764 7753 have_statelock = 1;
7765 7754 }
7766 7755
7767 7756 if (cp->c_backvp == NULL) {
7768 7757 error = cachefs_getbackvp(fscp, cp);
7769 7758 if (error)
7770 7759 goto out;
7771 7760 }
7772 7761 error = VOP_GETPAGE(cp->c_backvp, (offset_t)off,
7773 7762 PAGESIZE, protp, ourpl, PAGESIZE, seg,
7774 7763 addr, S_READ, cr, NULL);
7775 7764 if (error)
7776 7765 goto out;
7777 7766
7778 7767 if (have_statelock) {
7779 7768 mutex_exit(&cp->c_statelock);
7780 7769 have_statelock = 0;
7781 7770 }
7782 7771 downgrade = 0;
7783 7772 for (ppp = ourpl; *ppp; ppp++) {
7784 7773 if ((*ppp)->p_offset < off) {
7785 7774 index++;
7786 7775 page_unlock(*ppp);
7787 7776 continue;
7788 7777 }
7789 7778 if (PAGE_SHARED(*ppp)) {
7790 7779 if (page_tryupgrade(*ppp) == 0) {
7791 7780 for (ppp = &ourpl[index]; *ppp; ppp++)
7792 7781 page_unlock(*ppp);
7793 7782 error = EAGAIN;
7794 7783 goto out;
7795 7784 }
7796 7785 downgrade = 1;
7797 7786 }
7798 7787 ASSERT(PAGE_EXCL(*ppp));
7799 7788 (void) hat_pageunload((*ppp), HAT_FORCE_PGUNLOAD);
7800 7789 page_rename(*ppp, vp, (*ppp)->p_offset);
7801 7790 }
7802 7791 pl[0] = ourpl[index];
7803 7792 pl[1] = NULL;
7804 7793 if (downgrade) {
7805 7794 page_downgrade(ourpl[index]);
7806 7795 }
7807 7796 /* Unlock the rest of the pages from the cluster */
7808 7797 for (ppp = &ourpl[index+1]; *ppp; ppp++)
7809 7798 page_unlock(*ppp);
7810 7799 } else {
7811 7800 ASSERT(! have_statelock);
7812 7801 if (have_statelock) {
7813 7802 mutex_exit(&cp->c_statelock);
7814 7803 have_statelock = 0;
7815 7804 }
7816 7805 if ((pp = page_lookup(vp, off, SE_SHARED)) == NULL) {
7817 7806 cachefs_lostpage++;
7818 7807 goto again;
7819 7808 }
7820 7809 pl[0] = pp;
7821 7810 pl[1] = NULL;
7822 7811 }
7823 7812
7824 7813 out:
7825 7814 if (have_statelock) {
7826 7815 mutex_exit(&cp->c_statelock);
7827 7816 have_statelock = 0;
7828 7817 }
7829 7818 return (error);
7830 7819 }
7831 7820
7832 7821 /*ARGSUSED*/
7833 7822 static int
7834 7823 cachefs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr,
7835 7824 caller_context_t *ct)
7836 7825 {
7837 7826 cnode_t *cp = VTOC(vp);
7838 7827 int error = 0;
7839 7828 fscache_t *fscp = C_TO_FSCACHE(cp);
7840 7829 int held = 0;
7841 7830 int connected = 0;
7842 7831
7843 7832 if (getzoneid() != GLOBAL_ZONEID)
7844 7833 return (EPERM);
7845 7834
7846 7835 /* Call backfilesytem if NFSv4 */
7847 7836 if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
7848 7837 error = cachefs_putpage_backfs_nfsv4(vp, off, len, flags, cr);
7849 7838 goto out;
7850 7839 }
7851 7840
7852 7841 for (;;) {
7853 7842 /* get (or renew) access to the file system */
7854 7843 if (held) {
7855 7844 cachefs_cd_release(fscp);
7856 7845 held = 0;
7857 7846 }
7858 7847 error = cachefs_cd_access(fscp, connected, 1);
7859 7848 if (error)
7860 7849 break;
7861 7850 held = 1;
7862 7851
7863 7852 error = cachefs_putpage_common(vp, off, len, flags, cr);
7864 7853 if (error == 0)
7865 7854 break;
7866 7855
7867 7856 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
7868 7857 if (CFS_TIMEOUT(fscp, error)) {
7869 7858 cachefs_cd_release(fscp);
7870 7859 held = 0;
7871 7860 cachefs_cd_timedout(fscp);
7872 7861 connected = 0;
7873 7862 continue;
7874 7863 }
7875 7864 } else {
7876 7865 if (NOMEMWAIT()) {
7877 7866 error = 0;
7878 7867 goto out;
7879 7868 }
7880 7869 if (CFS_TIMEOUT(fscp, error)) {
7881 7870 connected = 1;
7882 7871 continue;
7883 7872 }
7884 7873 }
7885 7874 break;
7886 7875 }
7887 7876
7888 7877 out:
7889 7878
7890 7879 if (held) {
7891 7880 cachefs_cd_release(fscp);
7892 7881 }
7893 7882
7894 7883 #ifdef CFS_CD_DEBUG
7895 7884 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
7896 7885 #endif
7897 7886 return (error);
7898 7887 }
7899 7888
7900 7889 /*
7901 7890 * cachefs_putpage_backfs_nfsv4
7902 7891 *
7903 7892 * Call NFSv4 back filesystem to handle the putpage (cachefs
7904 7893 * pass-through support for NFSv4).
7905 7894 */
7906 7895 static int
7907 7896 cachefs_putpage_backfs_nfsv4(vnode_t *vp, offset_t off, size_t len, int flags,
7908 7897 cred_t *cr)
7909 7898 {
7910 7899 cnode_t *cp = VTOC(vp);
7911 7900 fscache_t *fscp = C_TO_FSCACHE(cp);
7912 7901 vnode_t *backvp;
7913 7902 int error;
7914 7903
7915 7904 /*
7916 7905 * For NFSv4 pass-through to work, only connected operation is
7917 7906 * supported, the cnode backvp must exist, and cachefs optional
7918 7907 * (eg., disconnectable) flags are turned off. Assert these
7919 7908 * conditions for the putpage operation.
7920 7909 */
7921 7910 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
7922 7911 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
7923 7912
7924 7913 /* Call backfs vnode op after extracting backvp */
7925 7914 mutex_enter(&cp->c_statelock);
7926 7915 backvp = cp->c_backvp;
7927 7916 mutex_exit(&cp->c_statelock);
7928 7917
7929 7918 CFS_DPRINT_BACKFS_NFSV4(fscp,
7930 7919 ("cachefs_putpage_backfs_nfsv4: cnode %p, backvp %p\n",
7931 7920 cp, backvp));
7932 7921 error = VOP_PUTPAGE(backvp, off, len, flags, cr, NULL);
7933 7922
7934 7923 return (error);
7935 7924 }
7936 7925
7937 7926 /*
7938 7927 * Flags are composed of {B_INVAL, B_FREE, B_DONTNEED, B_FORCE}
7939 7928 * If len == 0, do from off to EOF.
7940 7929 *
7941 7930 * The normal cases should be len == 0 & off == 0 (entire vp list),
7942 7931 * len == MAXBSIZE (from segmap_release actions), and len == PAGESIZE
7943 7932 * (from pageout).
7944 7933 */
7945 7934
7946 7935 /*ARGSUSED*/
7947 7936 int
7948 7937 cachefs_putpage_common(struct vnode *vp, offset_t off, size_t len,
7949 7938 int flags, cred_t *cr)
7950 7939 {
7951 7940 struct cnode *cp = VTOC(vp);
7952 7941 struct page *pp;
7953 7942 size_t io_len;
7954 7943 u_offset_t eoff, io_off;
7955 7944 int error = 0;
7956 7945 fscache_t *fscp = C_TO_FSCACHE(cp);
7957 7946 cachefscache_t *cachep = fscp->fs_cache;
7958 7947
7959 7948 if (len == 0 && (flags & B_INVAL) == 0 && vn_is_readonly(vp)) {
7960 7949 return (0);
7961 7950 }
7962 7951 if (!vn_has_cached_data(vp) || (off >= cp->c_size &&
7963 7952 (flags & B_INVAL) == 0))
7964 7953 return (0);
7965 7954
7966 7955 /*
7967 7956 * Should never have cached data for the cachefs vnode
7968 7957 * if NFSv4 is in use.
7969 7958 */
7970 7959 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
7971 7960
7972 7961 /*
7973 7962 * If this is an async putpage let a thread handle it.
7974 7963 */
7975 7964 if (flags & B_ASYNC) {
7976 7965 struct cachefs_req *rp;
7977 7966 int tflags = (flags & ~(B_ASYNC|B_DONTNEED));
7978 7967
7979 7968 if (ttoproc(curthread) == proc_pageout) {
7980 7969 /*
7981 7970 * If this is the page daemon we
7982 7971 * do the push synchronously (Dangerous!) and hope
7983 7972 * we can free enough to keep running...
7984 7973 */
7985 7974 flags &= ~B_ASYNC;
7986 7975 goto again;
7987 7976 }
7988 7977
7989 7978 if (! cachefs_async_okay()) {
7990 7979
7991 7980 /*
7992 7981 * this is somewhat like NFS's behavior. keep
7993 7982 * the system from thrashing. we've seen
7994 7983 * cases where async queues get out of
7995 7984 * control, especially if
7996 7985 * madvise(MADV_SEQUENTIAL) is done on a large
7997 7986 * mmap()ed file that is read sequentially.
7998 7987 */
7999 7988
8000 7989 flags &= ~B_ASYNC;
8001 7990 goto again;
8002 7991 }
8003 7992
8004 7993 /*
8005 7994 * if no flags other than B_ASYNC were set,
8006 7995 * we coalesce putpage requests into a single one for the
8007 7996 * whole file (len = off = 0). If such a request is
8008 7997 * already queued, we're done.
8009 7998 *
8010 7999 * If there are other flags set (e.g., B_INVAL), we don't
8011 8000 * attempt to coalesce and we use the specified length and
8012 8001 * offset.
8013 8002 */
8014 8003 rp = kmem_cache_alloc(cachefs_req_cache, KM_SLEEP);
8015 8004 mutex_enter(&cp->c_iomutex);
8016 8005 if ((cp->c_ioflags & CIO_PUTPAGES) == 0 || tflags != 0) {
8017 8006 rp->cfs_cmd = CFS_PUTPAGE;
8018 8007 rp->cfs_req_u.cu_putpage.cp_vp = vp;
8019 8008 if (tflags == 0) {
8020 8009 off = len = 0;
8021 8010 cp->c_ioflags |= CIO_PUTPAGES;
8022 8011 }
8023 8012 rp->cfs_req_u.cu_putpage.cp_off = off;
8024 8013 rp->cfs_req_u.cu_putpage.cp_len = (uint_t)len;
8025 8014 rp->cfs_req_u.cu_putpage.cp_flags = flags & ~B_ASYNC;
8026 8015 rp->cfs_cr = cr;
8027 8016 crhold(rp->cfs_cr);
8028 8017 VN_HOLD(vp);
8029 8018 cp->c_nio++;
8030 8019 cachefs_addqueue(rp, &(C_TO_FSCACHE(cp)->fs_workq));
8031 8020 } else {
8032 8021 kmem_cache_free(cachefs_req_cache, rp);
8033 8022 }
8034 8023
8035 8024 mutex_exit(&cp->c_iomutex);
8036 8025 return (0);
8037 8026 }
8038 8027
8039 8028
8040 8029 again:
8041 8030 if (len == 0) {
8042 8031 /*
8043 8032 * Search the entire vp list for pages >= off
8044 8033 */
8045 8034 error = pvn_vplist_dirty(vp, off, cachefs_push, flags, cr);
8046 8035 } else {
8047 8036 /*
8048 8037 * Do a range from [off...off + len] looking for pages
8049 8038 * to deal with.
8050 8039 */
8051 8040 eoff = (u_offset_t)off + len;
8052 8041 for (io_off = off; io_off < eoff && io_off < cp->c_size;
8053 8042 io_off += io_len) {
8054 8043 /*
8055 8044 * If we are not invalidating, synchronously
8056 8045 * freeing or writing pages use the routine
8057 8046 * page_lookup_nowait() to prevent reclaiming
8058 8047 * them from the free list.
8059 8048 */
8060 8049 if ((flags & B_INVAL) || ((flags & B_ASYNC) == 0)) {
8061 8050 pp = page_lookup(vp, io_off,
8062 8051 (flags & (B_INVAL | B_FREE)) ?
8063 8052 SE_EXCL : SE_SHARED);
8064 8053 } else {
8065 8054 /* XXX this looks like dead code */
8066 8055 pp = page_lookup_nowait(vp, io_off,
8067 8056 (flags & B_FREE) ? SE_EXCL : SE_SHARED);
8068 8057 }
8069 8058
8070 8059 if (pp == NULL || pvn_getdirty(pp, flags) == 0)
8071 8060 io_len = PAGESIZE;
8072 8061 else {
8073 8062 error = cachefs_push(vp, pp, &io_off,
8074 8063 &io_len, flags, cr);
8075 8064 if (error != 0)
8076 8065 break;
8077 8066 /*
8078 8067 * "io_off" and "io_len" are returned as
8079 8068 * the range of pages we actually wrote.
8080 8069 * This allows us to skip ahead more quickly
8081 8070 * since several pages may've been dealt
8082 8071 * with by this iteration of the loop.
8083 8072 */
8084 8073 }
8085 8074 }
8086 8075 }
8087 8076
8088 8077 if (error == 0 && off == 0 && (len == 0 || len >= cp->c_size)) {
8089 8078 cp->c_flags &= ~CDIRTY;
8090 8079 }
8091 8080
8092 8081 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_PUTPAGE))
8093 8082 cachefs_log_putpage(cachep, error, fscp->fs_cfsvfsp,
8094 8083 &cp->c_metadata.md_cookie, cp->c_id.cid_fileno,
8095 8084 crgetuid(cr), off, len);
8096 8085
8097 8086 return (error);
8098 8087
8099 8088 }
8100 8089
8101 8090 /*ARGSUSED*/
8102 8091 static int
8103 8092 cachefs_map(struct vnode *vp, offset_t off, struct as *as, caddr_t *addrp,
8104 8093 size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
8105 8094 caller_context_t *ct)
8106 8095 {
8107 8096 cnode_t *cp = VTOC(vp);
8108 8097 fscache_t *fscp = C_TO_FSCACHE(cp);
8109 8098 struct segvn_crargs vn_a;
8110 8099 int error;
8111 8100 int held = 0;
8112 8101 int writing;
8113 8102 int connected = 0;
8114 8103
8115 8104 #ifdef CFSDEBUG
8116 8105 u_offset_t offx = (u_offset_t)off;
8117 8106
8118 8107 CFS_DEBUG(CFSDEBUG_VOPS)
8119 8108 printf("cachefs_map: ENTER vp %p off %lld len %lu flags %d\n",
8120 8109 (void *)vp, offx, len, flags);
8121 8110 #endif
8122 8111 if (getzoneid() != GLOBAL_ZONEID) {
8123 8112 error = EPERM;
8124 8113 goto out;
8125 8114 }
8126 8115
8127 8116 if (vp->v_flag & VNOMAP) {
8128 8117 error = ENOSYS;
8129 8118 goto out;
8130 8119 }
8131 8120 if (off < 0 || (offset_t)(off + len) < 0) {
8132 8121 error = ENXIO;
8133 8122 goto out;
8134 8123 }
8135 8124 if (vp->v_type != VREG) {
8136 8125 error = ENODEV;
8137 8126 goto out;
8138 8127 }
8139 8128
8140 8129 /*
8141 8130 * Check to see if the vnode is currently marked as not cachable.
8142 8131 * If so, we have to refuse the map request as this violates the
8143 8132 * don't cache attribute.
8144 8133 */
8145 8134 if (vp->v_flag & VNOCACHE)
8146 8135 return (EAGAIN);
8147 8136
8148 8137 #ifdef OBSOLETE
8149 8138 /*
8150 8139 * If file is being locked, disallow mapping.
8151 8140 */
8152 8141 if (vn_has_flocks(vp)) {
8153 8142 error = EAGAIN;
8154 8143 goto out;
8155 8144 }
8156 8145 #endif
8157 8146
8158 8147 /* call backfilesystem if NFSv4 */
8159 8148 if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
8160 8149 error = cachefs_map_backfs_nfsv4(vp, off, as, addrp, len, prot,
8161 8150 maxprot, flags, cr);
8162 8151 goto out;
8163 8152 }
8164 8153
8165 8154 writing = (prot & PROT_WRITE && ((flags & MAP_PRIVATE) == 0));
8166 8155
8167 8156 for (;;) {
8168 8157 /* get (or renew) access to the file system */
8169 8158 if (held) {
8170 8159 cachefs_cd_release(fscp);
8171 8160 held = 0;
8172 8161 }
8173 8162 error = cachefs_cd_access(fscp, connected, writing);
8174 8163 if (error)
8175 8164 break;
8176 8165 held = 1;
8177 8166
8178 8167 if (writing) {
8179 8168 mutex_enter(&cp->c_statelock);
8180 8169 if (CFS_ISFS_WRITE_AROUND(fscp)) {
8181 8170 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
8182 8171 connected = 1;
8183 8172 continue;
8184 8173 } else {
8185 8174 cachefs_nocache(cp);
8186 8175 }
8187 8176 }
8188 8177
8189 8178 /*
8190 8179 * CN_MAPWRITE is for an optimization in cachefs_delmap.
8191 8180 * If CN_MAPWRITE is not set then cachefs_delmap does
8192 8181 * not need to try to push out any pages.
8193 8182 * This bit gets cleared when the cnode goes inactive.
8194 8183 */
8195 8184 cp->c_flags |= CN_MAPWRITE;
8196 8185
8197 8186 mutex_exit(&cp->c_statelock);
8198 8187 }
8199 8188 break;
8200 8189 }
8201 8190
8202 8191 if (held) {
8203 8192 cachefs_cd_release(fscp);
8204 8193 }
8205 8194
8206 8195 as_rangelock(as);
8207 8196 error = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags);
8208 8197 if (error != 0) {
8209 8198 as_rangeunlock(as);
8210 8199 goto out;
8211 8200 }
8212 8201
8213 8202 /*
8214 8203 * package up all the data passed in into a segvn_args struct and
8215 8204 * call as_map with segvn_create function to create a new segment
8216 8205 * in the address space.
8217 8206 */
8218 8207 vn_a.vp = vp;
8219 8208 vn_a.offset = off;
8220 8209 vn_a.type = flags & MAP_TYPE;
8221 8210 vn_a.prot = (uchar_t)prot;
8222 8211 vn_a.maxprot = (uchar_t)maxprot;
8223 8212 vn_a.cred = cr;
8224 8213 vn_a.amp = NULL;
8225 8214 vn_a.flags = flags & ~MAP_TYPE;
8226 8215 vn_a.szc = 0;
8227 8216 vn_a.lgrp_mem_policy_flags = 0;
8228 8217 error = as_map(as, *addrp, len, segvn_create, &vn_a);
8229 8218 as_rangeunlock(as);
8230 8219 out:
8231 8220
8232 8221 #ifdef CFS_CD_DEBUG
8233 8222 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
8234 8223 #endif
8235 8224 #ifdef CFSDEBUG
8236 8225 CFS_DEBUG(CFSDEBUG_VOPS)
8237 8226 printf("cachefs_map: EXIT vp %p error %d\n", (void *)vp, error);
8238 8227 #endif
8239 8228 return (error);
8240 8229 }
8241 8230
8242 8231 /*
8243 8232 * cachefs_map_backfs_nfsv4
8244 8233 *
8245 8234 * Call NFSv4 back filesystem to handle the map (cachefs
8246 8235 * pass-through support for NFSv4).
8247 8236 */
8248 8237 static int
8249 8238 cachefs_map_backfs_nfsv4(struct vnode *vp, offset_t off, struct as *as,
8250 8239 caddr_t *addrp, size_t len, uchar_t prot,
8251 8240 uchar_t maxprot, uint_t flags, cred_t *cr)
8252 8241 {
8253 8242 cnode_t *cp = VTOC(vp);
8254 8243 fscache_t *fscp = C_TO_FSCACHE(cp);
8255 8244 vnode_t *backvp;
8256 8245 int error;
8257 8246
8258 8247 /*
8259 8248 * For NFSv4 pass-through to work, only connected operation is
8260 8249 * supported, the cnode backvp must exist, and cachefs optional
8261 8250 * (eg., disconnectable) flags are turned off. Assert these
8262 8251 * conditions for the map operation.
8263 8252 */
8264 8253 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
8265 8254 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
8266 8255
8267 8256 /* Call backfs vnode op after extracting backvp */
8268 8257 mutex_enter(&cp->c_statelock);
8269 8258 backvp = cp->c_backvp;
8270 8259 mutex_exit(&cp->c_statelock);
8271 8260
8272 8261 CFS_DPRINT_BACKFS_NFSV4(fscp,
8273 8262 ("cachefs_map_backfs_nfsv4: cnode %p, backvp %p\n",
8274 8263 cp, backvp));
8275 8264 error = VOP_MAP(backvp, off, as, addrp, len, prot, maxprot, flags, cr,
8276 8265 NULL);
8277 8266
8278 8267 return (error);
8279 8268 }
8280 8269
8281 8270 /*ARGSUSED*/
8282 8271 static int
8283 8272 cachefs_addmap(struct vnode *vp, offset_t off, struct as *as,
8284 8273 caddr_t addr, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags,
8285 8274 cred_t *cr, caller_context_t *ct)
8286 8275 {
8287 8276 cnode_t *cp = VTOC(vp);
8288 8277 fscache_t *fscp = C_TO_FSCACHE(cp);
8289 8278
8290 8279 if (getzoneid() != GLOBAL_ZONEID)
8291 8280 return (EPERM);
8292 8281
8293 8282 if (vp->v_flag & VNOMAP)
8294 8283 return (ENOSYS);
8295 8284
8296 8285 /*
8297 8286 * Check this is not an NFSv4 filesystem, as the mapping
8298 8287 * is not done on the cachefs filesystem if NFSv4 is in
8299 8288 * use.
8300 8289 */
8301 8290 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
8302 8291
8303 8292 mutex_enter(&cp->c_statelock);
8304 8293 cp->c_mapcnt += btopr(len);
8305 8294 mutex_exit(&cp->c_statelock);
8306 8295 return (0);
8307 8296 }
8308 8297
8309 8298 /*ARGSUSED*/
8310 8299 static int
8311 8300 cachefs_delmap(struct vnode *vp, offset_t off, struct as *as,
8312 8301 caddr_t addr, size_t len, uint_t prot, uint_t maxprot, uint_t flags,
8313 8302 cred_t *cr, caller_context_t *ct)
8314 8303 {
8315 8304 cnode_t *cp = VTOC(vp);
8316 8305 fscache_t *fscp = C_TO_FSCACHE(cp);
8317 8306 int error;
8318 8307 int connected = 0;
8319 8308 int held = 0;
8320 8309
8321 8310 /*
8322 8311 * The file may be passed in to (or inherited into) the zone, so we
8323 8312 * need to let this operation go through since it happens as part of
8324 8313 * exiting.
8325 8314 */
8326 8315 if (vp->v_flag & VNOMAP)
8327 8316 return (ENOSYS);
8328 8317
8329 8318 /*
8330 8319 * Check this is not an NFSv4 filesystem, as the mapping
8331 8320 * is not done on the cachefs filesystem if NFSv4 is in
8332 8321 * use.
8333 8322 */
8334 8323 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
8335 8324
8336 8325 mutex_enter(&cp->c_statelock);
8337 8326 cp->c_mapcnt -= btopr(len);
8338 8327 ASSERT(cp->c_mapcnt >= 0);
8339 8328 mutex_exit(&cp->c_statelock);
8340 8329
8341 8330 if (cp->c_mapcnt || !vn_has_cached_data(vp) ||
8342 8331 ((cp->c_flags & CN_MAPWRITE) == 0))
8343 8332 return (0);
8344 8333
8345 8334 for (;;) {
8346 8335 /* get (or renew) access to the file system */
8347 8336 if (held) {
8348 8337 cachefs_cd_release(fscp);
8349 8338 held = 0;
8350 8339 }
8351 8340 error = cachefs_cd_access(fscp, connected, 1);
8352 8341 if (error)
8353 8342 break;
8354 8343 held = 1;
8355 8344 connected = 0;
8356 8345
8357 8346 error = cachefs_putpage_common(vp, (offset_t)0,
8358 8347 (uint_t)0, 0, cr);
8359 8348 if (CFS_TIMEOUT(fscp, error)) {
8360 8349 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
8361 8350 cachefs_cd_release(fscp);
8362 8351 held = 0;
8363 8352 cachefs_cd_timedout(fscp);
8364 8353 continue;
8365 8354 } else {
8366 8355 connected = 1;
8367 8356 continue;
8368 8357 }
8369 8358 }
8370 8359
8371 8360 /* if no space left in cache, wait until connected */
8372 8361 if ((error == ENOSPC) &&
8373 8362 (fscp->fs_cdconnected != CFS_CD_CONNECTED)) {
8374 8363 connected = 1;
8375 8364 continue;
8376 8365 }
8377 8366
8378 8367 mutex_enter(&cp->c_statelock);
8379 8368 if (!error)
8380 8369 error = cp->c_error;
8381 8370 cp->c_error = 0;
8382 8371 mutex_exit(&cp->c_statelock);
8383 8372 break;
8384 8373 }
8385 8374
8386 8375 if (held)
8387 8376 cachefs_cd_release(fscp);
8388 8377
8389 8378 #ifdef CFS_CD_DEBUG
8390 8379 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
8391 8380 #endif
8392 8381 return (error);
8393 8382 }
8394 8383
8395 8384 /* ARGSUSED */
8396 8385 static int
8397 8386 cachefs_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
8398 8387 offset_t offset, struct flk_callback *flk_cbp, cred_t *cr,
8399 8388 caller_context_t *ct)
8400 8389 {
8401 8390 struct cnode *cp = VTOC(vp);
8402 8391 int error;
8403 8392 struct fscache *fscp = C_TO_FSCACHE(cp);
8404 8393 vnode_t *backvp;
8405 8394 int held = 0;
8406 8395 int connected = 0;
8407 8396
8408 8397 if (getzoneid() != GLOBAL_ZONEID)
8409 8398 return (EPERM);
8410 8399
8411 8400 if ((cmd != F_GETLK) && (cmd != F_SETLK) && (cmd != F_SETLKW))
8412 8401 return (EINVAL);
8413 8402
8414 8403 /* Disallow locking of files that are currently mapped */
8415 8404 if (((cmd == F_SETLK) || (cmd == F_SETLKW)) && (cp->c_mapcnt > 0)) {
8416 8405 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
8417 8406 return (EAGAIN);
8418 8407 }
8419 8408
8420 8409 /*
8421 8410 * Cachefs only provides pass-through support for NFSv4,
8422 8411 * and all vnode operations are passed through to the
8423 8412 * back file system. For NFSv4 pass-through to work, only
8424 8413 * connected operation is supported, the cnode backvp must
8425 8414 * exist, and cachefs optional (eg., disconnectable) flags
8426 8415 * are turned off. Assert these conditions to ensure that
8427 8416 * the backfilesystem is called for the frlock operation.
8428 8417 */
8429 8418 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
8430 8419 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
8431 8420
8432 8421 /* XXX bob: nfs does a bunch more checks than we do */
8433 8422 if (CFS_ISFS_LLOCK(fscp)) {
8434 8423 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
8435 8424 return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct));
8436 8425 }
8437 8426
8438 8427 for (;;) {
8439 8428 /* get (or renew) access to the file system */
8440 8429 if (held) {
8441 8430 /* Won't loop with NFSv4 connected behavior */
8442 8431 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
8443 8432 cachefs_cd_release(fscp);
8444 8433 held = 0;
8445 8434 }
8446 8435 error = cachefs_cd_access(fscp, connected, 0);
8447 8436 if (error)
8448 8437 break;
8449 8438 held = 1;
8450 8439
8451 8440 /* if not connected, quit or wait */
8452 8441 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
8453 8442 connected = 1;
8454 8443 continue;
8455 8444 }
8456 8445
8457 8446 /* nocache the file */
8458 8447 if ((cp->c_flags & CN_NOCACHE) == 0 &&
8459 8448 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
8460 8449 mutex_enter(&cp->c_statelock);
8461 8450 cachefs_nocache(cp);
8462 8451 mutex_exit(&cp->c_statelock);
8463 8452 }
8464 8453
8465 8454 /*
8466 8455 * XXX bob: probably should do a consistency check
8467 8456 * Pass arguments unchanged if NFSv4 is the backfs.
8468 8457 */
8469 8458 if (bfp->l_whence == 2 && CFS_ISFS_BACKFS_NFSV4(fscp) == 0) {
8470 8459 bfp->l_start += cp->c_size;
8471 8460 bfp->l_whence = 0;
8472 8461 }
8473 8462
8474 8463 /* get the back vp */
8475 8464 mutex_enter(&cp->c_statelock);
8476 8465 if (cp->c_backvp == NULL) {
8477 8466 error = cachefs_getbackvp(fscp, cp);
8478 8467 if (error) {
8479 8468 mutex_exit(&cp->c_statelock);
8480 8469 break;
8481 8470 }
8482 8471 }
8483 8472 backvp = cp->c_backvp;
8484 8473 VN_HOLD(backvp);
8485 8474 mutex_exit(&cp->c_statelock);
8486 8475
8487 8476 /*
8488 8477 * make sure we can flush currently dirty pages before
8489 8478 * allowing the lock
8490 8479 */
8491 8480 if (bfp->l_type != F_UNLCK && cmd != F_GETLK &&
8492 8481 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
8493 8482 error = cachefs_putpage(
8494 8483 vp, (offset_t)0, 0, B_INVAL, cr, ct);
8495 8484 if (error) {
8496 8485 error = ENOLCK;
8497 8486 VN_RELE(backvp);
8498 8487 break;
8499 8488 }
8500 8489 }
8501 8490
8502 8491 /* do lock on the back file */
8503 8492 CFS_DPRINT_BACKFS_NFSV4(fscp,
8504 8493 ("cachefs_frlock (nfsv4): cp %p, backvp %p\n",
8505 8494 cp, backvp));
8506 8495 error = VOP_FRLOCK(backvp, cmd, bfp, flag, offset, NULL, cr,
8507 8496 ct);
8508 8497 VN_RELE(backvp);
8509 8498 if (CFS_TIMEOUT(fscp, error)) {
8510 8499 connected = 1;
8511 8500 continue;
8512 8501 }
8513 8502 break;
8514 8503 }
8515 8504
8516 8505 if (held) {
8517 8506 cachefs_cd_release(fscp);
8518 8507 }
8519 8508
8520 8509 /*
8521 8510 * If we are setting a lock mark the vnode VNOCACHE so the page
8522 8511 * cache does not give inconsistent results on locked files shared
8523 8512 * between clients. The VNOCACHE flag is never turned off as long
8524 8513 * as the vnode is active because it is hard to figure out when the
8525 8514 * last lock is gone.
8526 8515 * XXX - what if some already has the vnode mapped in?
8527 8516 * XXX bob: see nfs3_frlock, do not allow locking if vnode mapped in.
8528 8517 */
8529 8518 if ((error == 0) && (bfp->l_type != F_UNLCK) && (cmd != F_GETLK) &&
8530 8519 !CFS_ISFS_BACKFS_NFSV4(fscp))
8531 8520 vp->v_flag |= VNOCACHE;
8532 8521
8533 8522 #ifdef CFS_CD_DEBUG
8534 8523 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
8535 8524 #endif
8536 8525 return (error);
8537 8526 }
8538 8527
8539 8528 /*
8540 8529 * Free storage space associated with the specified vnode. The portion
8541 8530 * to be freed is specified by bfp->l_start and bfp->l_len (already
8542 8531 * normalized to a "whence" of 0).
8543 8532 *
8544 8533 * This is an experimental facility whose continued existence is not
8545 8534 * guaranteed. Currently, we only support the special case
8546 8535 * of l_len == 0, meaning free to end of file.
8547 8536 */
8548 8537 /* ARGSUSED */
8549 8538 static int
8550 8539 cachefs_space(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
8551 8540 offset_t offset, cred_t *cr, caller_context_t *ct)
8552 8541 {
8553 8542 cnode_t *cp = VTOC(vp);
8554 8543 fscache_t *fscp = C_TO_FSCACHE(cp);
8555 8544 int error;
8556 8545
8557 8546 ASSERT(vp->v_type == VREG);
8558 8547 if (getzoneid() != GLOBAL_ZONEID)
8559 8548 return (EPERM);
8560 8549 if (cmd != F_FREESP)
8561 8550 return (EINVAL);
8562 8551
8563 8552 /* call backfilesystem if NFSv4 */
8564 8553 if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
8565 8554 error = cachefs_space_backfs_nfsv4(vp, cmd, bfp, flag,
8566 8555 offset, cr, ct);
8567 8556 goto out;
8568 8557 }
8569 8558
8570 8559 if ((error = convoff(vp, bfp, 0, offset)) == 0) {
8571 8560 ASSERT(bfp->l_start >= 0);
8572 8561 if (bfp->l_len == 0) {
8573 8562 struct vattr va;
8574 8563
8575 8564 va.va_size = bfp->l_start;
8576 8565 va.va_mask = AT_SIZE;
8577 8566 error = cachefs_setattr(vp, &va, 0, cr, ct);
8578 8567 } else
8579 8568 error = EINVAL;
8580 8569 }
8581 8570
8582 8571 out:
8583 8572 return (error);
8584 8573 }
8585 8574
8586 8575 /*
8587 8576 * cachefs_space_backfs_nfsv4
8588 8577 *
8589 8578 * Call NFSv4 back filesystem to handle the space (cachefs
8590 8579 * pass-through support for NFSv4).
8591 8580 */
8592 8581 static int
8593 8582 cachefs_space_backfs_nfsv4(struct vnode *vp, int cmd, struct flock64 *bfp,
8594 8583 int flag, offset_t offset, cred_t *cr, caller_context_t *ct)
8595 8584 {
8596 8585 cnode_t *cp = VTOC(vp);
8597 8586 fscache_t *fscp = C_TO_FSCACHE(cp);
8598 8587 vnode_t *backvp;
8599 8588 int error;
8600 8589
8601 8590 /*
8602 8591 * For NFSv4 pass-through to work, only connected operation is
8603 8592 * supported, the cnode backvp must exist, and cachefs optional
8604 8593 * (eg., disconnectable) flags are turned off. Assert these
8605 8594 * conditions for the space operation.
8606 8595 */
8607 8596 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
8608 8597 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
8609 8598
8610 8599 /* Call backfs vnode op after extracting backvp */
8611 8600 mutex_enter(&cp->c_statelock);
8612 8601 backvp = cp->c_backvp;
8613 8602 mutex_exit(&cp->c_statelock);
8614 8603
8615 8604 CFS_DPRINT_BACKFS_NFSV4(fscp,
8616 8605 ("cachefs_space_backfs_nfsv4: cnode %p, backvp %p\n",
8617 8606 cp, backvp));
8618 8607 error = VOP_SPACE(backvp, cmd, bfp, flag, offset, cr, ct);
8619 8608
8620 8609 return (error);
8621 8610 }
8622 8611
8623 8612 /*ARGSUSED*/
8624 8613 static int
8625 8614 cachefs_realvp(struct vnode *vp, struct vnode **vpp, caller_context_t *ct)
8626 8615 {
8627 8616 return (EINVAL);
8628 8617 }
8629 8618
8630 8619 /*ARGSUSED*/
8631 8620 static int
8632 8621 cachefs_pageio(struct vnode *vp, page_t *pp, u_offset_t io_off, size_t io_len,
8633 8622 int flags, cred_t *cr, caller_context_t *ct)
8634 8623 {
8635 8624 return (ENOSYS);
8636 8625 }
8637 8626
8638 8627 static int
8639 8628 cachefs_setsecattr_connected(cnode_t *cp,
8640 8629 vsecattr_t *vsec, int flag, cred_t *cr)
8641 8630 {
8642 8631 fscache_t *fscp = C_TO_FSCACHE(cp);
8643 8632 int error = 0;
8644 8633
8645 8634 ASSERT(RW_WRITE_HELD(&cp->c_rwlock));
8646 8635 ASSERT((fscp->fs_info.fi_mntflags & CFS_NOACL) == 0);
8647 8636
8648 8637 mutex_enter(&cp->c_statelock);
8649 8638
8650 8639 if (cp->c_backvp == NULL) {
8651 8640 error = cachefs_getbackvp(fscp, cp);
8652 8641 if (error) {
8653 8642 cachefs_nocache(cp);
8654 8643 goto out;
8655 8644 }
8656 8645 }
8657 8646
8658 8647 error = CFSOP_CHECK_COBJECT(fscp, cp, 0, cr);
8659 8648 if (error)
8660 8649 goto out;
8661 8650
8662 8651 /* only owner can set acl */
8663 8652 if (cp->c_metadata.md_vattr.va_uid != crgetuid(cr)) {
8664 8653 error = EINVAL;
8665 8654 goto out;
8666 8655 }
8667 8656
8668 8657
8669 8658 CFS_DPRINT_BACKFS_NFSV4(fscp,
8670 8659 ("cachefs_setsecattr (nfsv4): cp %p, backvp %p",
8671 8660 cp, cp->c_backvp));
8672 8661 error = VOP_SETSECATTR(cp->c_backvp, vsec, flag, cr, NULL);
8673 8662 if (error) {
8674 8663 goto out;
8675 8664 }
8676 8665
8677 8666 if ((cp->c_filegrp->fg_flags & CFS_FG_WRITE) == 0 &&
8678 8667 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
8679 8668 cachefs_nocache(cp);
8680 8669 goto out;
8681 8670 }
8682 8671
8683 8672 CFSOP_MODIFY_COBJECT(fscp, cp, cr);
8684 8673
8685 8674 /* acl may have changed permissions -- handle this. */
8686 8675 if (!CFS_ISFS_BACKFS_NFSV4(fscp))
8687 8676 cachefs_acl2perm(cp, vsec);
8688 8677
8689 8678 if ((cp->c_flags & CN_NOCACHE) == 0 &&
8690 8679 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
8691 8680 error = cachefs_cacheacl(cp, vsec);
8692 8681 if (error != 0) {
8693 8682 #ifdef CFSDEBUG
8694 8683 CFS_DEBUG(CFSDEBUG_VOPS)
8695 8684 printf("cachefs_setacl: cacheacl: error %d\n",
8696 8685 error);
8697 8686 #endif /* CFSDEBUG */
8698 8687 error = 0;
8699 8688 cachefs_nocache(cp);
8700 8689 }
8701 8690 }
8702 8691
8703 8692 out:
8704 8693 mutex_exit(&cp->c_statelock);
8705 8694
8706 8695 return (error);
8707 8696 }
8708 8697
8709 8698 static int
8710 8699 cachefs_setsecattr_disconnected(cnode_t *cp,
8711 8700 vsecattr_t *vsec, int flag, cred_t *cr)
8712 8701 {
8713 8702 fscache_t *fscp = C_TO_FSCACHE(cp);
8714 8703 mode_t failmode = cp->c_metadata.md_vattr.va_mode;
8715 8704 off_t commit = 0;
8716 8705 int error = 0;
8717 8706
8718 8707 ASSERT((fscp->fs_info.fi_mntflags & CFS_NOACL) == 0);
8719 8708
8720 8709 if (CFS_ISFS_WRITE_AROUND(fscp))
8721 8710 return (ETIMEDOUT);
8722 8711
8723 8712 mutex_enter(&cp->c_statelock);
8724 8713
8725 8714 /* only owner can set acl */
8726 8715 if (cp->c_metadata.md_vattr.va_uid != crgetuid(cr)) {
8727 8716 error = EINVAL;
8728 8717 goto out;
8729 8718 }
8730 8719
8731 8720 if (cp->c_metadata.md_flags & MD_NEEDATTRS) {
8732 8721 error = ETIMEDOUT;
8733 8722 goto out;
8734 8723 }
8735 8724
8736 8725 /* XXX do i need this? is this right? */
8737 8726 if (cp->c_flags & CN_ALLOC_PENDING) {
8738 8727 if (cp->c_filegrp->fg_flags & CFS_FG_ALLOC_ATTR) {
8739 8728 (void) filegrp_allocattr(cp->c_filegrp);
8740 8729 }
8741 8730 error = filegrp_create_metadata(cp->c_filegrp,
8742 8731 &cp->c_metadata, &cp->c_id);
8743 8732 if (error) {
8744 8733 goto out;
8745 8734 }
8746 8735 cp->c_flags &= ~CN_ALLOC_PENDING;
8747 8736 }
8748 8737
8749 8738 /* XXX is this right? */
8750 8739 if ((cp->c_metadata.md_flags & MD_MAPPING) == 0) {
8751 8740 error = cachefs_dlog_cidmap(fscp);
8752 8741 if (error) {
8753 8742 error = ENOSPC;
8754 8743 goto out;
8755 8744 }
8756 8745 cp->c_metadata.md_flags |= MD_MAPPING;
8757 8746 cp->c_flags |= CN_UPDATED;
8758 8747 }
8759 8748
8760 8749 commit = cachefs_dlog_setsecattr(fscp, vsec, flag, cp, cr);
8761 8750 if (commit == 0)
8762 8751 goto out;
8763 8752
8764 8753 /* fix modes in metadata */
8765 8754 cachefs_acl2perm(cp, vsec);
8766 8755
8767 8756 if ((cp->c_flags & CN_NOCACHE) == 0) {
8768 8757 error = cachefs_cacheacl(cp, vsec);
8769 8758 if (error != 0) {
8770 8759 goto out;
8771 8760 }
8772 8761 }
8773 8762
8774 8763 /* XXX is this right? */
8775 8764 if (cachefs_modified_alloc(cp)) {
8776 8765 error = ENOSPC;
8777 8766 goto out;
8778 8767 }
8779 8768
8780 8769 out:
8781 8770 if (error != 0)
8782 8771 cp->c_metadata.md_vattr.va_mode = failmode;
8783 8772
8784 8773 mutex_exit(&cp->c_statelock);
8785 8774
8786 8775 if (commit) {
8787 8776 if (cachefs_dlog_commit(fscp, commit, error)) {
8788 8777 /*EMPTY*/
8789 8778 /* XXX fix on panic? */
8790 8779 }
8791 8780 }
8792 8781
8793 8782 return (error);
8794 8783 }
8795 8784
8796 8785 /*ARGSUSED*/
8797 8786 static int
8798 8787 cachefs_setsecattr(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr,
8799 8788 caller_context_t *ct)
8800 8789 {
8801 8790 cnode_t *cp = VTOC(vp);
8802 8791 fscache_t *fscp = C_TO_FSCACHE(cp);
8803 8792 int connected = 0;
8804 8793 int held = 0;
8805 8794 int error = 0;
8806 8795
8807 8796 #ifdef CFSDEBUG
8808 8797 CFS_DEBUG(CFSDEBUG_VOPS)
8809 8798 printf("cachefs_setsecattr: ENTER vp %p\n", (void *)vp);
8810 8799 #endif
8811 8800 if (getzoneid() != GLOBAL_ZONEID) {
8812 8801 error = EPERM;
8813 8802 goto out;
8814 8803 }
8815 8804
8816 8805 if (fscp->fs_info.fi_mntflags & CFS_NOACL) {
8817 8806 error = ENOSYS;
8818 8807 goto out;
8819 8808 }
8820 8809
8821 8810 if (! cachefs_vtype_aclok(vp)) {
8822 8811 error = EINVAL;
8823 8812 goto out;
8824 8813 }
8825 8814
8826 8815 /*
8827 8816 * Cachefs only provides pass-through support for NFSv4,
8828 8817 * and all vnode operations are passed through to the
8829 8818 * back file system. For NFSv4 pass-through to work, only
8830 8819 * connected operation is supported, the cnode backvp must
8831 8820 * exist, and cachefs optional (eg., disconnectable) flags
8832 8821 * are turned off. Assert these conditions to ensure that
8833 8822 * the backfilesystem is called for the setsecattr operation.
8834 8823 */
8835 8824 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
8836 8825 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
8837 8826
8838 8827 for (;;) {
8839 8828 /* drop hold on file system */
8840 8829 if (held) {
8841 8830 /* Won't loop with NFSv4 connected operation */
8842 8831 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
8843 8832 cachefs_cd_release(fscp);
8844 8833 held = 0;
8845 8834 }
8846 8835
8847 8836 /* acquire access to the file system */
8848 8837 error = cachefs_cd_access(fscp, connected, 1);
8849 8838 if (error)
8850 8839 break;
8851 8840 held = 1;
8852 8841
8853 8842 /* perform the setattr */
8854 8843 if (fscp->fs_cdconnected == CFS_CD_CONNECTED)
8855 8844 error = cachefs_setsecattr_connected(cp,
8856 8845 vsec, flag, cr);
8857 8846 else
8858 8847 error = cachefs_setsecattr_disconnected(cp,
8859 8848 vsec, flag, cr);
8860 8849 if (error) {
8861 8850 /* if connected */
8862 8851 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
8863 8852 if (CFS_TIMEOUT(fscp, error)) {
8864 8853 cachefs_cd_release(fscp);
8865 8854 held = 0;
8866 8855 cachefs_cd_timedout(fscp);
8867 8856 connected = 0;
8868 8857 continue;
8869 8858 }
8870 8859 }
8871 8860
8872 8861 /* else must be disconnected */
8873 8862 else {
8874 8863 if (CFS_TIMEOUT(fscp, error)) {
8875 8864 connected = 1;
8876 8865 continue;
8877 8866 }
8878 8867 }
8879 8868 }
8880 8869 break;
8881 8870 }
8882 8871
8883 8872 if (held) {
8884 8873 cachefs_cd_release(fscp);
8885 8874 }
8886 8875 return (error);
8887 8876
8888 8877 out:
8889 8878 #ifdef CFS_CD_DEBUG
8890 8879 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
8891 8880 #endif
8892 8881
8893 8882 #ifdef CFSDEBUG
8894 8883 CFS_DEBUG(CFSDEBUG_VOPS)
8895 8884 printf("cachefs_setsecattr: EXIT error = %d\n", error);
8896 8885 #endif
8897 8886 return (error);
8898 8887 }
8899 8888
8900 8889 /*
8901 8890 * call this BEFORE calling cachefs_cacheacl(), as the latter will
8902 8891 * sanitize the acl.
8903 8892 */
8904 8893
8905 8894 static void
8906 8895 cachefs_acl2perm(cnode_t *cp, vsecattr_t *vsec)
8907 8896 {
8908 8897 aclent_t *aclp;
8909 8898 int i;
8910 8899
8911 8900 for (i = 0; i < vsec->vsa_aclcnt; i++) {
8912 8901 aclp = ((aclent_t *)vsec->vsa_aclentp) + i;
8913 8902 switch (aclp->a_type) {
8914 8903 case USER_OBJ:
8915 8904 cp->c_metadata.md_vattr.va_mode &= (~0700);
8916 8905 cp->c_metadata.md_vattr.va_mode |= (aclp->a_perm << 6);
8917 8906 break;
8918 8907
8919 8908 case GROUP_OBJ:
8920 8909 cp->c_metadata.md_vattr.va_mode &= (~070);
8921 8910 cp->c_metadata.md_vattr.va_mode |= (aclp->a_perm << 3);
8922 8911 break;
8923 8912
8924 8913 case OTHER_OBJ:
8925 8914 cp->c_metadata.md_vattr.va_mode &= (~07);
8926 8915 cp->c_metadata.md_vattr.va_mode |= (aclp->a_perm);
8927 8916 break;
8928 8917
8929 8918 case CLASS_OBJ:
8930 8919 cp->c_metadata.md_aclclass = aclp->a_perm;
8931 8920 break;
8932 8921 }
8933 8922 }
8934 8923
8935 8924 cp->c_flags |= CN_UPDATED;
8936 8925 }
8937 8926
8938 8927 static int
8939 8928 cachefs_getsecattr(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr,
8940 8929 caller_context_t *ct)
8941 8930 {
8942 8931 cnode_t *cp = VTOC(vp);
8943 8932 fscache_t *fscp = C_TO_FSCACHE(cp);
8944 8933 int held = 0, connected = 0;
8945 8934 int error = 0;
8946 8935
8947 8936 #ifdef CFSDEBUG
8948 8937 CFS_DEBUG(CFSDEBUG_VOPS)
8949 8938 printf("cachefs_getsecattr: ENTER vp %p\n", (void *)vp);
8950 8939 #endif
8951 8940
8952 8941 if (getzoneid() != GLOBAL_ZONEID) {
8953 8942 error = EPERM;
8954 8943 goto out;
8955 8944 }
8956 8945
8957 8946 /*
8958 8947 * Cachefs only provides pass-through support for NFSv4,
8959 8948 * and all vnode operations are passed through to the
8960 8949 * back file system. For NFSv4 pass-through to work, only
8961 8950 * connected operation is supported, the cnode backvp must
8962 8951 * exist, and cachefs optional (eg., disconnectable) flags
8963 8952 * are turned off. Assert these conditions to ensure that
8964 8953 * the backfilesystem is called for the getsecattr operation.
8965 8954 */
8966 8955 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
8967 8956 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
8968 8957
8969 8958 if (fscp->fs_info.fi_mntflags & CFS_NOACL) {
8970 8959 error = fs_fab_acl(vp, vsec, flag, cr, ct);
8971 8960 goto out;
8972 8961 }
8973 8962
8974 8963 for (;;) {
8975 8964 if (held) {
8976 8965 /* Won't loop with NFSv4 connected behavior */
8977 8966 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
8978 8967 cachefs_cd_release(fscp);
8979 8968 held = 0;
8980 8969 }
8981 8970 error = cachefs_cd_access(fscp, connected, 0);
8982 8971 if (error)
8983 8972 break;
8984 8973 held = 1;
8985 8974
8986 8975 if (fscp->fs_cdconnected == CFS_CD_CONNECTED) {
8987 8976 error = cachefs_getsecattr_connected(vp, vsec, flag,
8988 8977 cr);
8989 8978 if (CFS_TIMEOUT(fscp, error)) {
8990 8979 cachefs_cd_release(fscp);
8991 8980 held = 0;
8992 8981 cachefs_cd_timedout(fscp);
8993 8982 connected = 0;
8994 8983 continue;
8995 8984 }
8996 8985 } else {
8997 8986 error = cachefs_getsecattr_disconnected(vp, vsec, flag,
8998 8987 cr);
8999 8988 if (CFS_TIMEOUT(fscp, error)) {
9000 8989 if (cachefs_cd_access_miss(fscp)) {
9001 8990 error = cachefs_getsecattr_connected(vp,
9002 8991 vsec, flag, cr);
9003 8992 if (!CFS_TIMEOUT(fscp, error))
9004 8993 break;
9005 8994 delay(5*hz);
9006 8995 connected = 0;
9007 8996 continue;
9008 8997 }
9009 8998 connected = 1;
9010 8999 continue;
9011 9000 }
9012 9001 }
9013 9002 break;
9014 9003 }
9015 9004
9016 9005 out:
9017 9006 if (held)
9018 9007 cachefs_cd_release(fscp);
9019 9008
9020 9009 #ifdef CFS_CD_DEBUG
9021 9010 ASSERT((curthread->t_flag & T_CD_HELD) == 0);
9022 9011 #endif
9023 9012 #ifdef CFSDEBUG
9024 9013 CFS_DEBUG(CFSDEBUG_VOPS)
9025 9014 printf("cachefs_getsecattr: EXIT error = %d\n", error);
9026 9015 #endif
9027 9016 return (error);
9028 9017 }
9029 9018
9030 9019 static int
9031 9020 cachefs_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr,
9032 9021 caller_context_t *ct)
9033 9022 {
9034 9023 cnode_t *cp = VTOC(vp);
9035 9024 fscache_t *fscp = C_TO_FSCACHE(cp);
9036 9025 int error = 0;
9037 9026 vnode_t *backvp;
9038 9027
9039 9028 #ifdef CFSDEBUG
9040 9029 CFS_DEBUG(CFSDEBUG_VOPS)
9041 9030 printf("cachefs_shrlock: ENTER vp %p\n", (void *)vp);
9042 9031 #endif
9043 9032
9044 9033 if (getzoneid() != GLOBAL_ZONEID) {
9045 9034 error = EPERM;
9046 9035 goto out;
9047 9036 }
9048 9037
9049 9038 /*
9050 9039 * Cachefs only provides pass-through support for NFSv4,
9051 9040 * and all vnode operations are passed through to the
9052 9041 * back file system. For NFSv4 pass-through to work, only
9053 9042 * connected operation is supported, the cnode backvp must
9054 9043 * exist, and cachefs optional (eg., disconnectable) flags
9055 9044 * are turned off. Assert these conditions to ensure that
9056 9045 * the backfilesystem is called for the shrlock operation.
9057 9046 */
9058 9047 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
9059 9048 CFS_BACKFS_NFSV4_ASSERT_CNODE(cp);
9060 9049
9061 9050 mutex_enter(&cp->c_statelock);
9062 9051 if (cp->c_backvp == NULL)
9063 9052 error = cachefs_getbackvp(fscp, cp);
9064 9053 backvp = cp->c_backvp;
9065 9054 mutex_exit(&cp->c_statelock);
9066 9055 ASSERT((error != 0) || (backvp != NULL));
9067 9056
9068 9057 if (error == 0) {
9069 9058 CFS_DPRINT_BACKFS_NFSV4(fscp,
9070 9059 ("cachefs_shrlock (nfsv4): cp %p, backvp %p",
9071 9060 cp, backvp));
9072 9061 error = VOP_SHRLOCK(backvp, cmd, shr, flag, cr, ct);
9073 9062 }
9074 9063
9075 9064 out:
9076 9065 #ifdef CFSDEBUG
9077 9066 CFS_DEBUG(CFSDEBUG_VOPS)
9078 9067 printf("cachefs_shrlock: EXIT error = %d\n", error);
9079 9068 #endif
9080 9069 return (error);
9081 9070 }
9082 9071
9083 9072 static int
9084 9073 cachefs_getsecattr_connected(vnode_t *vp, vsecattr_t *vsec, int flag,
9085 9074 cred_t *cr)
9086 9075 {
9087 9076 cnode_t *cp = VTOC(vp);
9088 9077 fscache_t *fscp = C_TO_FSCACHE(cp);
9089 9078 int hit = 0;
9090 9079 int error = 0;
9091 9080
9092 9081
9093 9082 mutex_enter(&cp->c_statelock);
9094 9083 error = CFSOP_CHECK_COBJECT(fscp, cp, 0, cr);
9095 9084 if (error)
9096 9085 goto out;
9097 9086
9098 9087 /* read from the cache if we can */
9099 9088 if ((cp->c_metadata.md_flags & MD_ACL) &&
9100 9089 ((cp->c_flags & CN_NOCACHE) == 0) &&
9101 9090 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
9102 9091 ASSERT((cp->c_flags & CN_NOCACHE) == 0);
9103 9092 error = cachefs_getaclfromcache(cp, vsec);
9104 9093 if (error) {
9105 9094 cachefs_nocache(cp);
9106 9095 ASSERT((cp->c_metadata.md_flags & MD_ACL) == 0);
9107 9096 error = 0;
9108 9097 } else {
9109 9098 hit = 1;
9110 9099 goto out;
9111 9100 }
9112 9101 }
9113 9102
9114 9103 ASSERT(error == 0);
9115 9104 if (cp->c_backvp == NULL)
9116 9105 error = cachefs_getbackvp(fscp, cp);
9117 9106 if (error)
9118 9107 goto out;
9119 9108
9120 9109 CFS_DPRINT_BACKFS_NFSV4(fscp,
9121 9110 ("cachefs_getsecattr (nfsv4): cp %p, backvp %p",
9122 9111 cp, cp->c_backvp));
9123 9112 error = VOP_GETSECATTR(cp->c_backvp, vsec, flag, cr, NULL);
9124 9113 if (error)
9125 9114 goto out;
9126 9115
9127 9116 if (((fscp->fs_info.fi_mntflags & CFS_NOACL) == 0) &&
9128 9117 (cachefs_vtype_aclok(vp)) &&
9129 9118 ((cp->c_flags & CN_NOCACHE) == 0) &&
9130 9119 !CFS_ISFS_BACKFS_NFSV4(fscp)) {
9131 9120 error = cachefs_cacheacl(cp, vsec);
9132 9121 if (error) {
9133 9122 error = 0;
9134 9123 cachefs_nocache(cp);
9135 9124 }
9136 9125 }
9137 9126
9138 9127 out:
9139 9128 if (error == 0) {
9140 9129 if (hit)
9141 9130 fscp->fs_stats.st_hits++;
9142 9131 else
9143 9132 fscp->fs_stats.st_misses++;
9144 9133 }
9145 9134 mutex_exit(&cp->c_statelock);
9146 9135
9147 9136 return (error);
9148 9137 }
9149 9138
9150 9139 static int
9151 9140 /*ARGSUSED*/
9152 9141 cachefs_getsecattr_disconnected(vnode_t *vp, vsecattr_t *vsec, int flag,
9153 9142 cred_t *cr)
9154 9143 {
9155 9144 cnode_t *cp = VTOC(vp);
9156 9145 fscache_t *fscp = C_TO_FSCACHE(cp);
9157 9146 int hit = 0;
9158 9147 int error = 0;
9159 9148
9160 9149
9161 9150 mutex_enter(&cp->c_statelock);
9162 9151
9163 9152 /* read from the cache if we can */
9164 9153 if (((cp->c_flags & CN_NOCACHE) == 0) &&
9165 9154 (cp->c_metadata.md_flags & MD_ACL)) {
9166 9155 error = cachefs_getaclfromcache(cp, vsec);
9167 9156 if (error) {
9168 9157 cachefs_nocache(cp);
9169 9158 ASSERT((cp->c_metadata.md_flags & MD_ACL) == 0);
9170 9159 error = 0;
9171 9160 } else {
9172 9161 hit = 1;
9173 9162 goto out;
9174 9163 }
9175 9164 }
9176 9165 error = ETIMEDOUT;
9177 9166
9178 9167 out:
9179 9168 if (error == 0) {
9180 9169 if (hit)
9181 9170 fscp->fs_stats.st_hits++;
9182 9171 else
9183 9172 fscp->fs_stats.st_misses++;
9184 9173 }
9185 9174 mutex_exit(&cp->c_statelock);
9186 9175
9187 9176 return (error);
9188 9177 }
9189 9178
9190 9179 /*
9191 9180 * cachefs_cacheacl() -- cache an ACL, which we do by applying it to
9192 9181 * the frontfile if possible; otherwise, the adjunct directory.
9193 9182 *
9194 9183 * inputs:
9195 9184 * cp - the cnode, with its statelock already held
9196 9185 * vsecp - a pointer to a vsecattr_t you'd like us to cache as-is,
9197 9186 * or NULL if you want us to do the VOP_GETSECATTR(backvp).
9198 9187 *
9199 9188 * returns:
9200 9189 * 0 - all is well
9201 9190 * nonzero - errno
9202 9191 */
9203 9192
9204 9193 int
9205 9194 cachefs_cacheacl(cnode_t *cp, vsecattr_t *vsecp)
9206 9195 {
9207 9196 fscache_t *fscp = C_TO_FSCACHE(cp);
9208 9197 vsecattr_t vsec;
9209 9198 aclent_t *aclp;
9210 9199 int gotvsec = 0;
9211 9200 int error = 0;
9212 9201 vnode_t *vp = NULL;
9213 9202 void *aclkeep = NULL;
9214 9203 int i;
9215 9204
9216 9205 ASSERT(MUTEX_HELD(&cp->c_statelock));
9217 9206 ASSERT((cp->c_flags & CN_NOCACHE) == 0);
9218 9207 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
9219 9208 ASSERT((fscp->fs_info.fi_mntflags & CFS_NOACL) == 0);
9220 9209 ASSERT(cachefs_vtype_aclok(CTOV(cp)));
9221 9210
9222 9211 if (fscp->fs_info.fi_mntflags & CFS_NOACL) {
9223 9212 error = ENOSYS;
9224 9213 goto out;
9225 9214 }
9226 9215
9227 9216 if (vsecp == NULL) {
9228 9217 if (cp->c_backvp == NULL)
9229 9218 error = cachefs_getbackvp(fscp, cp);
9230 9219 if (error != 0)
9231 9220 goto out;
9232 9221 vsecp = &vsec;
9233 9222 bzero(&vsec, sizeof (vsec));
9234 9223 vsecp->vsa_mask =
9235 9224 VSA_ACL | VSA_ACLCNT | VSA_DFACL | VSA_DFACLCNT;
9236 9225 error = VOP_GETSECATTR(cp->c_backvp, vsecp, 0, kcred, NULL);
9237 9226 if (error != 0) {
9238 9227 goto out;
9239 9228 }
9240 9229 gotvsec = 1;
9241 9230 } else if (vsecp->vsa_mask & VSA_ACL) {
9242 9231 aclkeep = vsecp->vsa_aclentp;
9243 9232 vsecp->vsa_aclentp = cachefs_kmem_alloc(vsecp->vsa_aclcnt *
9244 9233 sizeof (aclent_t), KM_SLEEP);
9245 9234 bcopy(aclkeep, vsecp->vsa_aclentp, vsecp->vsa_aclcnt *
9246 9235 sizeof (aclent_t));
9247 9236 } else if ((vsecp->vsa_mask & (VSA_ACL | VSA_DFACL)) == 0) {
9248 9237 /* unless there's real data, we can cache nothing. */
9249 9238 return (0);
9250 9239 }
9251 9240
9252 9241 /*
9253 9242 * prevent the ACL from chmoding our frontfile, and
9254 9243 * snarf the class info
9255 9244 */
9256 9245
9257 9246 if ((vsecp->vsa_mask & (VSA_ACL | VSA_ACLCNT)) ==
9258 9247 (VSA_ACL | VSA_ACLCNT)) {
9259 9248 for (i = 0; i < vsecp->vsa_aclcnt; i++) {
9260 9249 aclp = ((aclent_t *)vsecp->vsa_aclentp) + i;
9261 9250 switch (aclp->a_type) {
9262 9251 case CLASS_OBJ:
9263 9252 cp->c_metadata.md_aclclass =
9264 9253 aclp->a_perm;
9265 9254 /*FALLTHROUGH*/
9266 9255 case USER_OBJ:
9267 9256 case GROUP_OBJ:
9268 9257 case OTHER_OBJ:
9269 9258 aclp->a_perm = 06;
9270 9259 }
9271 9260 }
9272 9261 }
9273 9262
9274 9263 /*
9275 9264 * if the frontfile exists, then we always do the work. but,
9276 9265 * if there's no frontfile, and the ACL isn't a `real' ACL,
9277 9266 * then we don't want to do the work. otherwise, an `ls -l'
9278 9267 * will create tons of emtpy frontfiles.
9279 9268 */
9280 9269
9281 9270 if (((cp->c_metadata.md_flags & MD_FILE) == 0) &&
9282 9271 ((vsecp->vsa_aclcnt + vsecp->vsa_dfaclcnt)
9283 9272 <= MIN_ACL_ENTRIES)) {
9284 9273 cp->c_metadata.md_flags |= MD_ACL;
9285 9274 cp->c_flags |= CN_UPDATED;
9286 9275 goto out;
9287 9276 }
9288 9277
9289 9278 /*
9290 9279 * if we have a default ACL, then we need a
9291 9280 * real live directory in the frontfs that we
9292 9281 * can apply the ACL to. if not, then we just
9293 9282 * use the frontfile. we get the frontfile
9294 9283 * regardless -- that way, we know the
9295 9284 * directory for the frontfile exists.
9296 9285 */
9297 9286
9298 9287 if (vsecp->vsa_dfaclcnt > 0) {
9299 9288 if (cp->c_acldirvp == NULL)
9300 9289 error = cachefs_getacldirvp(cp);
9301 9290 if (error != 0)
9302 9291 goto out;
9303 9292 vp = cp->c_acldirvp;
9304 9293 } else {
9305 9294 if (cp->c_frontvp == NULL)
9306 9295 error = cachefs_getfrontfile(cp);
9307 9296 if (error != 0)
9308 9297 goto out;
9309 9298 vp = cp->c_frontvp;
9310 9299 }
9311 9300 ASSERT(vp != NULL);
9312 9301
9313 9302 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
9314 9303 error = VOP_SETSECATTR(vp, vsecp, 0, kcred, NULL);
9315 9304 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
9316 9305 if (error != 0) {
9317 9306 #ifdef CFSDEBUG
9318 9307 CFS_DEBUG(CFSDEBUG_VOPS)
9319 9308 printf("cachefs_cacheacl: setsecattr: error %d\n",
9320 9309 error);
9321 9310 #endif /* CFSDEBUG */
9322 9311 /*
9323 9312 * If there was an error, we don't want to call
9324 9313 * cachefs_nocache(); so, set error to 0.
9325 9314 * We will call cachefs_purgeacl(), in order to
9326 9315 * clean such things as adjunct ACL directories.
9327 9316 */
9328 9317 cachefs_purgeacl(cp);
9329 9318 error = 0;
9330 9319 goto out;
9331 9320 }
9332 9321 if (vp == cp->c_frontvp)
9333 9322 cp->c_flags |= CN_NEED_FRONT_SYNC;
9334 9323
9335 9324 cp->c_metadata.md_flags |= MD_ACL;
9336 9325 cp->c_flags |= CN_UPDATED;
9337 9326
9338 9327 out:
9339 9328 if ((error) && (fscp->fs_cdconnected == CFS_CD_CONNECTED))
9340 9329 cachefs_nocache(cp);
9341 9330
9342 9331 if (gotvsec) {
9343 9332 if (vsec.vsa_aclcnt)
9344 9333 kmem_free(vsec.vsa_aclentp,
9345 9334 vsec.vsa_aclcnt * sizeof (aclent_t));
9346 9335 if (vsec.vsa_dfaclcnt)
9347 9336 kmem_free(vsec.vsa_dfaclentp,
9348 9337 vsec.vsa_dfaclcnt * sizeof (aclent_t));
9349 9338 } else if (aclkeep != NULL) {
9350 9339 cachefs_kmem_free(vsecp->vsa_aclentp,
9351 9340 vsecp->vsa_aclcnt * sizeof (aclent_t));
9352 9341 vsecp->vsa_aclentp = aclkeep;
9353 9342 }
9354 9343
9355 9344 return (error);
9356 9345 }
9357 9346
9358 9347 void
9359 9348 cachefs_purgeacl(cnode_t *cp)
9360 9349 {
9361 9350 ASSERT(MUTEX_HELD(&cp->c_statelock));
9362 9351
9363 9352 ASSERT(!CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(cp)));
9364 9353
9365 9354 if (cp->c_acldirvp != NULL) {
9366 9355 VN_RELE(cp->c_acldirvp);
9367 9356 cp->c_acldirvp = NULL;
9368 9357 }
9369 9358
9370 9359 if (cp->c_metadata.md_flags & MD_ACLDIR) {
9371 9360 char name[CFS_FRONTFILE_NAME_SIZE + 2];
9372 9361
9373 9362 ASSERT(cp->c_filegrp->fg_dirvp != NULL);
9374 9363 make_ascii_name(&cp->c_id, name);
9375 9364 (void) strcat(name, ".d");
9376 9365
9377 9366 (void) VOP_RMDIR(cp->c_filegrp->fg_dirvp, name,
9378 9367 cp->c_filegrp->fg_dirvp, kcred, NULL, 0);
9379 9368 }
9380 9369
9381 9370 cp->c_metadata.md_flags &= ~(MD_ACL | MD_ACLDIR);
9382 9371 cp->c_flags |= CN_UPDATED;
9383 9372 }
9384 9373
9385 9374 static int
9386 9375 cachefs_getacldirvp(cnode_t *cp)
9387 9376 {
9388 9377 char name[CFS_FRONTFILE_NAME_SIZE + 2];
9389 9378 int error = 0;
9390 9379
9391 9380 ASSERT(MUTEX_HELD(&cp->c_statelock));
9392 9381 ASSERT(cp->c_acldirvp == NULL);
9393 9382
9394 9383 if (cp->c_frontvp == NULL)
9395 9384 error = cachefs_getfrontfile(cp);
9396 9385 if (error != 0)
9397 9386 goto out;
9398 9387
9399 9388 ASSERT(cp->c_filegrp->fg_dirvp != NULL);
9400 9389 make_ascii_name(&cp->c_id, name);
9401 9390 (void) strcat(name, ".d");
9402 9391 error = VOP_LOOKUP(cp->c_filegrp->fg_dirvp,
9403 9392 name, &cp->c_acldirvp, NULL, 0, NULL, kcred, NULL, NULL, NULL);
9404 9393 if ((error != 0) && (error != ENOENT))
9405 9394 goto out;
9406 9395
9407 9396 if (error != 0) {
9408 9397 vattr_t va;
9409 9398
9410 9399 va.va_mode = S_IFDIR | 0777;
9411 9400 va.va_uid = 0;
9412 9401 va.va_gid = 0;
9413 9402 va.va_type = VDIR;
9414 9403 va.va_mask = AT_TYPE | AT_MODE |
9415 9404 AT_UID | AT_GID;
9416 9405 error =
9417 9406 VOP_MKDIR(cp->c_filegrp->fg_dirvp,
9418 9407 name, &va, &cp->c_acldirvp, kcred, NULL, 0, NULL);
9419 9408 if (error != 0)
9420 9409 goto out;
9421 9410 }
9422 9411
9423 9412 ASSERT(cp->c_acldirvp != NULL);
9424 9413 cp->c_metadata.md_flags |= MD_ACLDIR;
9425 9414 cp->c_flags |= CN_UPDATED;
9426 9415
9427 9416 out:
9428 9417 if (error != 0)
9429 9418 cp->c_acldirvp = NULL;
9430 9419 return (error);
9431 9420 }
9432 9421
9433 9422 static int
9434 9423 cachefs_getaclfromcache(cnode_t *cp, vsecattr_t *vsec)
9435 9424 {
9436 9425 aclent_t *aclp;
9437 9426 int error = 0;
9438 9427 vnode_t *vp = NULL;
9439 9428 int i;
9440 9429
9441 9430 ASSERT(cp->c_metadata.md_flags & MD_ACL);
9442 9431 ASSERT(MUTEX_HELD(&cp->c_statelock));
9443 9432 ASSERT(vsec->vsa_aclentp == NULL);
9444 9433
9445 9434 if (cp->c_metadata.md_flags & MD_ACLDIR) {
9446 9435 if (cp->c_acldirvp == NULL)
9447 9436 error = cachefs_getacldirvp(cp);
9448 9437 if (error != 0)
9449 9438 goto out;
9450 9439 vp = cp->c_acldirvp;
9451 9440 } else if (cp->c_metadata.md_flags & MD_FILE) {
9452 9441 if (cp->c_frontvp == NULL)
9453 9442 error = cachefs_getfrontfile(cp);
9454 9443 if (error != 0)
9455 9444 goto out;
9456 9445 vp = cp->c_frontvp;
9457 9446 } else {
9458 9447
9459 9448 /*
9460 9449 * if we get here, then we know that MD_ACL is on,
9461 9450 * meaning an ACL was successfully cached. we also
9462 9451 * know that neither MD_ACLDIR nor MD_FILE are on, so
9463 9452 * this has to be an entry without a `real' ACL.
9464 9453 * thus, we forge whatever is necessary.
9465 9454 */
9466 9455
9467 9456 if (vsec->vsa_mask & VSA_ACLCNT)
9468 9457 vsec->vsa_aclcnt = MIN_ACL_ENTRIES;
9469 9458
9470 9459 if (vsec->vsa_mask & VSA_ACL) {
9471 9460 vsec->vsa_aclentp =
9472 9461 kmem_zalloc(MIN_ACL_ENTRIES *
9473 9462 sizeof (aclent_t), KM_SLEEP);
9474 9463 aclp = (aclent_t *)vsec->vsa_aclentp;
9475 9464 aclp->a_type = USER_OBJ;
9476 9465 ++aclp;
9477 9466 aclp->a_type = GROUP_OBJ;
9478 9467 ++aclp;
9479 9468 aclp->a_type = OTHER_OBJ;
9480 9469 ++aclp;
9481 9470 aclp->a_type = CLASS_OBJ;
9482 9471 ksort((caddr_t)vsec->vsa_aclentp, MIN_ACL_ENTRIES,
9483 9472 sizeof (aclent_t), cmp2acls);
9484 9473 }
9485 9474
9486 9475 ASSERT(vp == NULL);
9487 9476 }
9488 9477
9489 9478 if (vp != NULL) {
9490 9479 if ((error = VOP_GETSECATTR(vp, vsec, 0, kcred, NULL)) != 0) {
9491 9480 #ifdef CFSDEBUG
9492 9481 CFS_DEBUG(CFSDEBUG_VOPS)
9493 9482 printf("cachefs_getaclfromcache: error %d\n",
9494 9483 error);
9495 9484 #endif /* CFSDEBUG */
9496 9485 goto out;
9497 9486 }
9498 9487 }
9499 9488
9500 9489 if (vsec->vsa_aclentp != NULL) {
9501 9490 for (i = 0; i < vsec->vsa_aclcnt; i++) {
9502 9491 aclp = ((aclent_t *)vsec->vsa_aclentp) + i;
9503 9492 switch (aclp->a_type) {
9504 9493 case USER_OBJ:
9505 9494 aclp->a_id = cp->c_metadata.md_vattr.va_uid;
9506 9495 aclp->a_perm =
9507 9496 cp->c_metadata.md_vattr.va_mode & 0700;
9508 9497 aclp->a_perm >>= 6;
9509 9498 break;
9510 9499
9511 9500 case GROUP_OBJ:
9512 9501 aclp->a_id = cp->c_metadata.md_vattr.va_gid;
9513 9502 aclp->a_perm =
9514 9503 cp->c_metadata.md_vattr.va_mode & 070;
9515 9504 aclp->a_perm >>= 3;
9516 9505 break;
9517 9506
9518 9507 case OTHER_OBJ:
9519 9508 aclp->a_perm =
9520 9509 cp->c_metadata.md_vattr.va_mode & 07;
9521 9510 break;
9522 9511
9523 9512 case CLASS_OBJ:
9524 9513 aclp->a_perm =
9525 9514 cp->c_metadata.md_aclclass;
9526 9515 break;
9527 9516 }
9528 9517 }
9529 9518 }
9530 9519
9531 9520 out:
9532 9521
9533 9522 if (error != 0)
9534 9523 cachefs_nocache(cp);
9535 9524
9536 9525 return (error);
9537 9526 }
9538 9527
9539 9528 /*
9540 9529 * Fills in targp with attribute information from srcp, cp
9541 9530 * and if necessary the system.
9542 9531 */
9543 9532 static void
9544 9533 cachefs_attr_setup(vattr_t *srcp, vattr_t *targp, cnode_t *cp, cred_t *cr)
9545 9534 {
9546 9535 time_t now;
9547 9536
9548 9537 ASSERT((srcp->va_mask & (AT_TYPE | AT_MODE)) == (AT_TYPE | AT_MODE));
9549 9538
9550 9539 /*
9551 9540 * Add code to fill in the va struct. We use the fields from
9552 9541 * the srcp struct if they are populated, otherwise we guess
9553 9542 */
9554 9543
9555 9544 targp->va_mask = 0; /* initialize all fields */
9556 9545 targp->va_mode = srcp->va_mode;
9557 9546 targp->va_type = srcp->va_type;
9558 9547 targp->va_nlink = 1;
9559 9548 targp->va_nodeid = 0;
9560 9549
9561 9550 if (srcp->va_mask & AT_UID)
9562 9551 targp->va_uid = srcp->va_uid;
9563 9552 else
9564 9553 targp->va_uid = crgetuid(cr);
9565 9554
9566 9555 if (srcp->va_mask & AT_GID)
9567 9556 targp->va_gid = srcp->va_gid;
9568 9557 else
9569 9558 targp->va_gid = crgetgid(cr);
9570 9559
9571 9560 if (srcp->va_mask & AT_FSID)
9572 9561 targp->va_fsid = srcp->va_fsid;
9573 9562 else
9574 9563 targp->va_fsid = 0; /* initialize all fields */
9575 9564
9576 9565 now = gethrestime_sec();
9577 9566 if (srcp->va_mask & AT_ATIME)
9578 9567 targp->va_atime = srcp->va_atime;
9579 9568 else
9580 9569 targp->va_atime.tv_sec = now;
9581 9570
9582 9571 if (srcp->va_mask & AT_MTIME)
9583 9572 targp->va_mtime = srcp->va_mtime;
9584 9573 else
9585 9574 targp->va_mtime.tv_sec = now;
9586 9575
9587 9576 if (srcp->va_mask & AT_CTIME)
9588 9577 targp->va_ctime = srcp->va_ctime;
9589 9578 else
9590 9579 targp->va_ctime.tv_sec = now;
9591 9580
9592 9581
9593 9582 if (srcp->va_mask & AT_SIZE)
9594 9583 targp->va_size = srcp->va_size;
9595 9584 else
9596 9585 targp->va_size = 0;
9597 9586
9598 9587 /*
9599 9588 * the remaing fields are set by the fs and not changable.
9600 9589 * we populate these entries useing the parent directory
9601 9590 * values. It's a small hack, but should work.
9602 9591 */
9603 9592 targp->va_blksize = cp->c_metadata.md_vattr.va_blksize;
9604 9593 targp->va_rdev = cp->c_metadata.md_vattr.va_rdev;
9605 9594 targp->va_nblocks = cp->c_metadata.md_vattr.va_nblocks;
9606 9595 targp->va_seq = 0; /* Never keep the sequence number */
9607 9596 }
9608 9597
9609 9598 /*
9610 9599 * set the gid for a newly created file. The algorithm is as follows:
9611 9600 *
9612 9601 * 1) If the gid is set in the attribute list, then use it if
9613 9602 * the caller is privileged, belongs to the target group, or
9614 9603 * the group is the same as the parent directory.
9615 9604 *
9616 9605 * 2) If the parent directory's set-gid bit is clear, then use
9617 9606 * the process gid
9618 9607 *
9619 9608 * 3) Otherwise, use the gid of the parent directory.
9620 9609 *
9621 9610 * Note: newcp->c_attr.va_{mode,type} must already be set before calling
9622 9611 * this routine.
9623 9612 */
9624 9613 static void
9625 9614 cachefs_creategid(cnode_t *dcp, cnode_t *newcp, vattr_t *vap, cred_t *cr)
9626 9615 {
9627 9616 if ((vap->va_mask & AT_GID) &&
9628 9617 ((vap->va_gid == dcp->c_attr.va_gid) ||
9629 9618 groupmember(vap->va_gid, cr) ||
9630 9619 secpolicy_vnode_create_gid(cr) != 0)) {
9631 9620 newcp->c_attr.va_gid = vap->va_gid;
9632 9621 } else {
9633 9622 if (dcp->c_attr.va_mode & S_ISGID)
9634 9623 newcp->c_attr.va_gid = dcp->c_attr.va_gid;
9635 9624 else
9636 9625 newcp->c_attr.va_gid = crgetgid(cr);
9637 9626 }
9638 9627
9639 9628 /*
9640 9629 * if we're creating a directory, and the parent directory has the
9641 9630 * set-GID bit set, set it on the new directory.
9642 9631 * Otherwise, if the user is neither privileged nor a member of the
9643 9632 * file's new group, clear the file's set-GID bit.
9644 9633 */
9645 9634 if (dcp->c_attr.va_mode & S_ISGID && newcp->c_attr.va_type == VDIR) {
9646 9635 newcp->c_attr.va_mode |= S_ISGID;
9647 9636 } else if ((newcp->c_attr.va_mode & S_ISGID) &&
9648 9637 secpolicy_vnode_setids_setgids(cr, newcp->c_attr.va_gid) != 0)
9649 9638 newcp->c_attr.va_mode &= ~S_ISGID;
9650 9639 }
9651 9640
9652 9641 /*
9653 9642 * create an acl for the newly created file. should be called right
9654 9643 * after cachefs_creategid.
9655 9644 */
9656 9645
9657 9646 static void
9658 9647 cachefs_createacl(cnode_t *dcp, cnode_t *newcp)
9659 9648 {
9660 9649 fscache_t *fscp = C_TO_FSCACHE(dcp);
9661 9650 vsecattr_t vsec;
9662 9651 int gotvsec = 0;
9663 9652 int error = 0; /* placeholder */
9664 9653 aclent_t *aclp;
9665 9654 o_mode_t *classp = NULL;
9666 9655 o_mode_t gunion = 0;
9667 9656 int i;
9668 9657
9669 9658 if ((fscp->fs_info.fi_mntflags & CFS_NOACL) ||
9670 9659 (! cachefs_vtype_aclok(CTOV(newcp))))
9671 9660 return;
9672 9661
9673 9662 ASSERT(dcp->c_metadata.md_flags & MD_ACL);
9674 9663 ASSERT(MUTEX_HELD(&dcp->c_statelock));
9675 9664 ASSERT(MUTEX_HELD(&newcp->c_statelock));
9676 9665
9677 9666 /*
9678 9667 * XXX should probably not do VSA_ACL and VSA_ACLCNT, but that
9679 9668 * would hit code paths that isn't hit anywhere else.
9680 9669 */
9681 9670
9682 9671 bzero(&vsec, sizeof (vsec));
9683 9672 vsec.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | VSA_DFACLCNT;
9684 9673 error = cachefs_getaclfromcache(dcp, &vsec);
9685 9674 if (error != 0)
9686 9675 goto out;
9687 9676 gotvsec = 1;
9688 9677
9689 9678 if ((vsec.vsa_dfaclcnt > 0) && (vsec.vsa_dfaclentp != NULL)) {
9690 9679 if ((vsec.vsa_aclcnt > 0) && (vsec.vsa_aclentp != NULL))
9691 9680 kmem_free(vsec.vsa_aclentp,
9692 9681 vsec.vsa_aclcnt * sizeof (aclent_t));
9693 9682
9694 9683 vsec.vsa_aclcnt = vsec.vsa_dfaclcnt;
9695 9684 vsec.vsa_aclentp = vsec.vsa_dfaclentp;
9696 9685 vsec.vsa_dfaclcnt = 0;
9697 9686 vsec.vsa_dfaclentp = NULL;
9698 9687
9699 9688 if (newcp->c_attr.va_type == VDIR) {
9700 9689 vsec.vsa_dfaclentp = kmem_alloc(vsec.vsa_aclcnt *
9701 9690 sizeof (aclent_t), KM_SLEEP);
9702 9691 vsec.vsa_dfaclcnt = vsec.vsa_aclcnt;
9703 9692 bcopy(vsec.vsa_aclentp, vsec.vsa_dfaclentp,
9704 9693 vsec.vsa_aclcnt * sizeof (aclent_t));
9705 9694 }
9706 9695
9707 9696 /*
9708 9697 * this function should be called pretty much after
9709 9698 * the rest of the file creation stuff is done. so,
9710 9699 * uid, gid, etc. should be `right'. we'll go with
9711 9700 * that, rather than trying to determine whether to
9712 9701 * get stuff from cr or va.
9713 9702 */
9714 9703
9715 9704 for (i = 0; i < vsec.vsa_aclcnt; i++) {
9716 9705 aclp = ((aclent_t *)vsec.vsa_aclentp) + i;
9717 9706 switch (aclp->a_type) {
9718 9707 case DEF_USER_OBJ:
9719 9708 aclp->a_type = USER_OBJ;
9720 9709 aclp->a_id = newcp->c_metadata.md_vattr.va_uid;
9721 9710 aclp->a_perm =
9722 9711 newcp->c_metadata.md_vattr.va_mode;
9723 9712 aclp->a_perm &= 0700;
9724 9713 aclp->a_perm >>= 6;
9725 9714 break;
9726 9715
9727 9716 case DEF_GROUP_OBJ:
9728 9717 aclp->a_type = GROUP_OBJ;
9729 9718 aclp->a_id = newcp->c_metadata.md_vattr.va_gid;
9730 9719 aclp->a_perm =
9731 9720 newcp->c_metadata.md_vattr.va_mode;
9732 9721 aclp->a_perm &= 070;
9733 9722 aclp->a_perm >>= 3;
9734 9723 gunion |= aclp->a_perm;
9735 9724 break;
9736 9725
9737 9726 case DEF_OTHER_OBJ:
9738 9727 aclp->a_type = OTHER_OBJ;
9739 9728 aclp->a_perm =
9740 9729 newcp->c_metadata.md_vattr.va_mode & 07;
9741 9730 break;
9742 9731
9743 9732 case DEF_CLASS_OBJ:
9744 9733 aclp->a_type = CLASS_OBJ;
9745 9734 classp = &(aclp->a_perm);
9746 9735 break;
9747 9736
9748 9737 case DEF_USER:
9749 9738 aclp->a_type = USER;
9750 9739 gunion |= aclp->a_perm;
9751 9740 break;
9752 9741
9753 9742 case DEF_GROUP:
9754 9743 aclp->a_type = GROUP;
9755 9744 gunion |= aclp->a_perm;
9756 9745 break;
9757 9746 }
9758 9747 }
9759 9748
9760 9749 /* XXX is this the POSIX thing to do? */
9761 9750 if (classp != NULL)
9762 9751 *classp &= gunion;
9763 9752
9764 9753 /*
9765 9754 * we don't need to log this; rather, we clear the
9766 9755 * MD_ACL bit when we reconnect.
9767 9756 */
9768 9757
9769 9758 error = cachefs_cacheacl(newcp, &vsec);
9770 9759 if (error != 0)
9771 9760 goto out;
9772 9761 }
9773 9762
9774 9763 newcp->c_metadata.md_aclclass = 07; /* XXX check posix */
9775 9764 newcp->c_metadata.md_flags |= MD_ACL;
9776 9765 newcp->c_flags |= CN_UPDATED;
9777 9766
9778 9767 out:
9779 9768
9780 9769 if (gotvsec) {
9781 9770 if ((vsec.vsa_aclcnt > 0) && (vsec.vsa_aclentp != NULL))
9782 9771 kmem_free(vsec.vsa_aclentp,
9783 9772 vsec.vsa_aclcnt * sizeof (aclent_t));
9784 9773 if ((vsec.vsa_dfaclcnt > 0) && (vsec.vsa_dfaclentp != NULL))
9785 9774 kmem_free(vsec.vsa_dfaclentp,
9786 9775 vsec.vsa_dfaclcnt * sizeof (aclent_t));
9787 9776 }
9788 9777 }
9789 9778
9790 9779 /*
9791 9780 * this is translated from the UFS code for access checking.
9792 9781 */
9793 9782
9794 9783 static int
9795 9784 cachefs_access_local(void *vcp, int mode, cred_t *cr)
9796 9785 {
9797 9786 cnode_t *cp = vcp;
9798 9787 fscache_t *fscp = C_TO_FSCACHE(cp);
9799 9788 int shift = 0;
9800 9789
9801 9790 ASSERT(MUTEX_HELD(&cp->c_statelock));
9802 9791
9803 9792 if (mode & VWRITE) {
9804 9793 /*
9805 9794 * Disallow write attempts on read-only
9806 9795 * file systems, unless the file is special.
9807 9796 */
9808 9797 struct vnode *vp = CTOV(cp);
9809 9798 if (vn_is_readonly(vp)) {
9810 9799 if (!IS_DEVVP(vp)) {
9811 9800 return (EROFS);
9812 9801 }
9813 9802 }
9814 9803 }
9815 9804
9816 9805 /*
9817 9806 * if we need to do ACLs, do it. this works whether anyone
9818 9807 * has explicitly made an ACL or not.
9819 9808 */
9820 9809
9821 9810 if (((fscp->fs_info.fi_mntflags & CFS_NOACL) == 0) &&
9822 9811 (cachefs_vtype_aclok(CTOV(cp))))
9823 9812 return (cachefs_acl_access(cp, mode, cr));
9824 9813
9825 9814 if (crgetuid(cr) != cp->c_attr.va_uid) {
9826 9815 shift += 3;
9827 9816 if (!groupmember(cp->c_attr.va_gid, cr))
9828 9817 shift += 3;
9829 9818 }
9830 9819
9831 9820 return (secpolicy_vnode_access2(cr, CTOV(cp), cp->c_attr.va_uid,
9832 9821 cp->c_attr.va_mode << shift, mode));
9833 9822 }
9834 9823
9835 9824 /*
9836 9825 * This is transcribed from ufs_acl_access(). If that changes, then
9837 9826 * this should, too.
9838 9827 *
9839 9828 * Check the cnode's ACL's to see if this mode of access is
9840 9829 * allowed; return 0 if allowed, EACCES if not.
9841 9830 *
9842 9831 * We follow the procedure defined in Sec. 3.3.5, ACL Access
9843 9832 * Check Algorithm, of the POSIX 1003.6 Draft Standard.
9844 9833 */
9845 9834
9846 9835 #define ACL_MODE_CHECK(M, PERM, C, I) \
9847 9836 secpolicy_vnode_access2(C, CTOV(I), owner, (PERM), (M))
9848 9837
9849 9838 static int
9850 9839 cachefs_acl_access(struct cnode *cp, int mode, cred_t *cr)
9851 9840 {
9852 9841 int error = 0;
9853 9842
9854 9843 fscache_t *fscp = C_TO_FSCACHE(cp);
9855 9844
9856 9845 int mask = ~0;
9857 9846 int ismask = 0;
9858 9847
9859 9848 int gperm = 0;
9860 9849 int ngroup = 0;
9861 9850
9862 9851 vsecattr_t vsec;
9863 9852 int gotvsec = 0;
9864 9853 aclent_t *aclp;
9865 9854
9866 9855 uid_t owner = cp->c_attr.va_uid;
9867 9856
9868 9857 int i;
9869 9858
9870 9859 ASSERT(MUTEX_HELD(&cp->c_statelock));
9871 9860 ASSERT((fscp->fs_info.fi_mntflags & CFS_NOACL) == 0);
9872 9861
9873 9862 /*
9874 9863 * strictly speaking, we shouldn't set VSA_DFACL and DFACLCNT,
9875 9864 * but then i believe we'd be the only thing exercising those
9876 9865 * code paths -- probably a bad thing.
9877 9866 */
9878 9867
9879 9868 bzero(&vsec, sizeof (vsec));
9880 9869 vsec.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | VSA_DFACLCNT;
9881 9870
9882 9871 /* XXX KLUDGE! correct insidious 0-class problem */
9883 9872 if (cp->c_metadata.md_aclclass == 0 &&
9884 9873 fscp->fs_cdconnected == CFS_CD_CONNECTED)
9885 9874 cachefs_purgeacl(cp);
9886 9875 again:
9887 9876 if (cp->c_metadata.md_flags & MD_ACL) {
9888 9877 error = cachefs_getaclfromcache(cp, &vsec);
9889 9878 if (error != 0) {
9890 9879 #ifdef CFSDEBUG
9891 9880 if (error != ETIMEDOUT)
9892 9881 CFS_DEBUG(CFSDEBUG_VOPS)
9893 9882 printf("cachefs_acl_access():"
9894 9883 "error %d from getaclfromcache()\n",
9895 9884 error);
9896 9885 #endif /* CFSDEBUG */
9897 9886 if ((cp->c_metadata.md_flags & MD_ACL) == 0) {
9898 9887 goto again;
9899 9888 } else {
9900 9889 goto out;
9901 9890 }
9902 9891 }
9903 9892 } else {
9904 9893 if (cp->c_backvp == NULL) {
9905 9894 if (fscp->fs_cdconnected == CFS_CD_CONNECTED)
9906 9895 error = cachefs_getbackvp(fscp, cp);
9907 9896 else
9908 9897 error = ETIMEDOUT;
9909 9898 }
9910 9899 if (error == 0)
9911 9900 error = VOP_GETSECATTR(cp->c_backvp, &vsec, 0, cr,
9912 9901 NULL);
9913 9902 if (error != 0) {
9914 9903 #ifdef CFSDEBUG
9915 9904 CFS_DEBUG(CFSDEBUG_VOPS)
9916 9905 printf("cachefs_acl_access():"
9917 9906 "error %d from getsecattr(backvp)\n",
9918 9907 error);
9919 9908 #endif /* CFSDEBUG */
9920 9909 goto out;
9921 9910 }
9922 9911 if ((cp->c_flags & CN_NOCACHE) == 0 &&
9923 9912 !CFS_ISFS_BACKFS_NFSV4(fscp))
9924 9913 (void) cachefs_cacheacl(cp, &vsec);
9925 9914 }
9926 9915 gotvsec = 1;
9927 9916
9928 9917 ASSERT(error == 0);
9929 9918 for (i = 0; i < vsec.vsa_aclcnt; i++) {
9930 9919 aclp = ((aclent_t *)vsec.vsa_aclentp) + i;
9931 9920 switch (aclp->a_type) {
9932 9921 case USER_OBJ:
9933 9922 /*
9934 9923 * this might look cleaner in the 2nd loop
9935 9924 * below, but we do it here as an
9936 9925 * optimization.
9937 9926 */
9938 9927
9939 9928 owner = aclp->a_id;
9940 9929 if (crgetuid(cr) == owner) {
9941 9930 error = ACL_MODE_CHECK(mode, aclp->a_perm << 6,
9942 9931 cr, cp);
9943 9932 goto out;
9944 9933 }
9945 9934 break;
9946 9935
9947 9936 case CLASS_OBJ:
9948 9937 mask = aclp->a_perm;
9949 9938 ismask = 1;
9950 9939 break;
9951 9940 }
9952 9941 }
9953 9942
9954 9943 ASSERT(error == 0);
9955 9944 for (i = 0; i < vsec.vsa_aclcnt; i++) {
9956 9945 aclp = ((aclent_t *)vsec.vsa_aclentp) + i;
9957 9946 switch (aclp->a_type) {
9958 9947 case USER:
9959 9948 if (crgetuid(cr) == aclp->a_id) {
9960 9949 error = ACL_MODE_CHECK(mode,
9961 9950 (aclp->a_perm & mask) << 6, cr, cp);
9962 9951 goto out;
9963 9952 }
9964 9953 break;
9965 9954
9966 9955 case GROUP_OBJ:
9967 9956 if (groupmember(aclp->a_id, cr)) {
9968 9957 ++ngroup;
9969 9958 gperm |= aclp->a_perm;
9970 9959 if (! ismask) {
9971 9960 error = ACL_MODE_CHECK(mode,
9972 9961 aclp->a_perm << 6,
9973 9962 cr, cp);
9974 9963 goto out;
9975 9964 }
9976 9965 }
9977 9966 break;
9978 9967
9979 9968 case GROUP:
9980 9969 if (groupmember(aclp->a_id, cr)) {
9981 9970 ++ngroup;
9982 9971 gperm |= aclp->a_perm;
9983 9972 }
9984 9973 break;
9985 9974
9986 9975 case OTHER_OBJ:
9987 9976 if (ngroup == 0) {
9988 9977 error = ACL_MODE_CHECK(mode, aclp->a_perm << 6,
9989 9978 cr, cp);
9990 9979 goto out;
9991 9980 }
9992 9981 break;
9993 9982
9994 9983 default:
9995 9984 break;
9996 9985 }
9997 9986 }
9998 9987
9999 9988 ASSERT(ngroup > 0);
10000 9989 error = ACL_MODE_CHECK(mode, (gperm & mask) << 6, cr, cp);
10001 9990
10002 9991 out:
10003 9992 if (gotvsec) {
10004 9993 if (vsec.vsa_aclcnt && vsec.vsa_aclentp)
10005 9994 kmem_free(vsec.vsa_aclentp,
10006 9995 vsec.vsa_aclcnt * sizeof (aclent_t));
10007 9996 if (vsec.vsa_dfaclcnt && vsec.vsa_dfaclentp)
10008 9997 kmem_free(vsec.vsa_dfaclentp,
10009 9998 vsec.vsa_dfaclcnt * sizeof (aclent_t));
10010 9999 }
10011 10000
10012 10001 return (error);
10013 10002 }
10014 10003
10015 10004 /*
10016 10005 * see if permissions allow for removal of the given file from
10017 10006 * the given directory.
10018 10007 */
10019 10008 static int
10020 10009 cachefs_stickyrmchk(struct cnode *dcp, struct cnode *cp, cred_t *cr)
10021 10010 {
10022 10011 uid_t uid;
10023 10012 /*
10024 10013 * If the containing directory is sticky, the user must:
10025 10014 * - own the directory, or
10026 10015 * - own the file, or
10027 10016 * - be able to write the file (if it's a plain file), or
10028 10017 * - be sufficiently privileged.
10029 10018 */
10030 10019 if ((dcp->c_attr.va_mode & S_ISVTX) &&
10031 10020 ((uid = crgetuid(cr)) != dcp->c_attr.va_uid) &&
10032 10021 (uid != cp->c_attr.va_uid) &&
10033 10022 (cp->c_attr.va_type != VREG ||
10034 10023 cachefs_access_local(cp, VWRITE, cr) != 0))
10035 10024 return (secpolicy_vnode_remove(cr));
10036 10025
10037 10026 return (0);
10038 10027 }
10039 10028
10040 10029 /*
10041 10030 * Returns a new name, may even be unique.
10042 10031 * Stolen from nfs code.
10043 10032 * Since now we will use renaming to .cfs* in place of .nfs*
10044 10033 * for CacheFS. Both NFS and CacheFS will rename opened files.
10045 10034 */
10046 10035 static char cachefs_prefix[] = ".cfs";
10047 10036 kmutex_t cachefs_newnum_lock;
10048 10037
10049 10038 static char *
10050 10039 cachefs_newname(void)
10051 10040 {
10052 10041 static uint_t newnum = 0;
10053 10042 char *news;
10054 10043 char *s, *p;
10055 10044 uint_t id;
10056 10045
10057 10046 mutex_enter(&cachefs_newnum_lock);
10058 10047 if (newnum == 0) {
10059 10048 newnum = gethrestime_sec() & 0xfffff;
10060 10049 newnum |= 0x10000;
10061 10050 }
10062 10051 id = newnum++;
10063 10052 mutex_exit(&cachefs_newnum_lock);
10064 10053
10065 10054 news = cachefs_kmem_alloc(MAXNAMELEN, KM_SLEEP);
10066 10055 s = news;
10067 10056 p = cachefs_prefix;
10068 10057 while (*p != '\0')
10069 10058 *s++ = *p++;
10070 10059 while (id != 0) {
10071 10060 *s++ = "0123456789ABCDEF"[id & 0x0f];
10072 10061 id >>= 4;
10073 10062 }
10074 10063 *s = '\0';
10075 10064 return (news);
10076 10065 }
10077 10066
10078 10067 /*
10079 10068 * Called to rename the specified file to a temporary file so
10080 10069 * operations to the file after remove work.
10081 10070 * Must call this routine with the dir c_rwlock held as a writer.
10082 10071 */
10083 10072 static int
10084 10073 /*ARGSUSED*/
10085 10074 cachefs_remove_dolink(vnode_t *dvp, vnode_t *vp, char *nm, cred_t *cr)
10086 10075 {
10087 10076 cnode_t *cp = VTOC(vp);
10088 10077 char *tmpname;
10089 10078 fscache_t *fscp = C_TO_FSCACHE(cp);
10090 10079 int error;
10091 10080
10092 10081 ASSERT(RW_WRITE_HELD(&(VTOC(dvp)->c_rwlock)));
10093 10082
10094 10083 /* get the new name for the file */
10095 10084 tmpname = cachefs_newname();
10096 10085
10097 10086 /* do the link */
10098 10087 if (fscp->fs_cdconnected == CFS_CD_CONNECTED)
10099 10088 error = cachefs_link_connected(dvp, vp, tmpname, cr);
10100 10089 else
10101 10090 error = cachefs_link_disconnected(dvp, vp, tmpname, cr);
10102 10091 if (error) {
10103 10092 cachefs_kmem_free(tmpname, MAXNAMELEN);
10104 10093 return (error);
10105 10094 }
10106 10095
10107 10096 mutex_enter(&cp->c_statelock);
10108 10097 if (cp->c_unldvp) {
10109 10098 VN_RELE(cp->c_unldvp);
10110 10099 cachefs_kmem_free(cp->c_unlname, MAXNAMELEN);
10111 10100 crfree(cp->c_unlcred);
10112 10101 }
10113 10102
10114 10103 VN_HOLD(dvp);
10115 10104 cp->c_unldvp = dvp;
10116 10105 crhold(cr);
10117 10106 cp->c_unlcred = cr;
10118 10107 cp->c_unlname = tmpname;
10119 10108
10120 10109 /* drop the backvp so NFS does not also do a rename */
10121 10110 mutex_exit(&cp->c_statelock);
10122 10111
10123 10112 return (0);
10124 10113 }
10125 10114
10126 10115 /*
10127 10116 * Marks the cnode as modified.
10128 10117 */
10129 10118 static void
10130 10119 cachefs_modified(cnode_t *cp)
10131 10120 {
10132 10121 fscache_t *fscp = C_TO_FSCACHE(cp);
10133 10122 struct vattr va;
10134 10123 int error;
10135 10124
10136 10125 ASSERT(MUTEX_HELD(&cp->c_statelock));
10137 10126 ASSERT(cp->c_metadata.md_rlno);
10138 10127
10139 10128 /* if not on the modify list */
10140 10129 if (cp->c_metadata.md_rltype != CACHEFS_RL_MODIFIED) {
10141 10130 /* put on modified list, also marks the file as modified */
10142 10131 cachefs_rlent_moveto(fscp->fs_cache, CACHEFS_RL_MODIFIED,
10143 10132 cp->c_metadata.md_rlno, cp->c_metadata.md_frontblks);
10144 10133 cp->c_metadata.md_rltype = CACHEFS_RL_MODIFIED;
10145 10134 cp->c_flags |= CN_UPDATED;
10146 10135
10147 10136 /* if a modified regular file that is not local */
10148 10137 if (((cp->c_id.cid_flags & CFS_CID_LOCAL) == 0) &&
10149 10138 (cp->c_metadata.md_flags & MD_FILE) &&
10150 10139 (cp->c_attr.va_type == VREG)) {
10151 10140
10152 10141 if (cp->c_frontvp == NULL)
10153 10142 (void) cachefs_getfrontfile(cp);
10154 10143 if (cp->c_frontvp) {
10155 10144 /* identify file so fsck knows it is modified */
10156 10145 va.va_mode = 0766;
10157 10146 va.va_mask = AT_MODE;
10158 10147 error = VOP_SETATTR(cp->c_frontvp,
10159 10148 &va, 0, kcred, NULL);
10160 10149 if (error) {
10161 10150 cmn_err(CE_WARN,
10162 10151 "Cannot change ff mode.\n");
10163 10152 }
10164 10153 }
10165 10154 }
10166 10155 }
10167 10156 }
10168 10157
10169 10158 /*
10170 10159 * Marks the cnode as modified.
10171 10160 * Allocates a rl slot for the cnode if necessary.
10172 10161 * Returns 0 for success, !0 if cannot get an rl slot.
10173 10162 */
10174 10163 static int
10175 10164 cachefs_modified_alloc(cnode_t *cp)
10176 10165 {
10177 10166 fscache_t *fscp = C_TO_FSCACHE(cp);
10178 10167 filegrp_t *fgp = cp->c_filegrp;
10179 10168 int error;
10180 10169 rl_entry_t rl_ent;
10181 10170
10182 10171 ASSERT(MUTEX_HELD(&cp->c_statelock));
10183 10172
10184 10173 /* get the rl slot if needed */
10185 10174 if (cp->c_metadata.md_rlno == 0) {
10186 10175 /* get a metadata slot if we do not have one yet */
10187 10176 if (cp->c_flags & CN_ALLOC_PENDING) {
10188 10177 if (cp->c_filegrp->fg_flags & CFS_FG_ALLOC_ATTR) {
10189 10178 (void) filegrp_allocattr(cp->c_filegrp);
10190 10179 }
10191 10180 error = filegrp_create_metadata(cp->c_filegrp,
10192 10181 &cp->c_metadata, &cp->c_id);
10193 10182 if (error)
10194 10183 return (error);
10195 10184 cp->c_flags &= ~CN_ALLOC_PENDING;
10196 10185 }
10197 10186
10198 10187 /* get a free rl entry */
10199 10188 rl_ent.rl_fileno = cp->c_id.cid_fileno;
10200 10189 rl_ent.rl_local = (cp->c_id.cid_flags & CFS_CID_LOCAL) ? 1 : 0;
10201 10190 rl_ent.rl_fsid = fscp->fs_cfsid;
10202 10191 rl_ent.rl_attrc = 0;
10203 10192 error = cachefs_rl_alloc(fscp->fs_cache, &rl_ent,
10204 10193 &cp->c_metadata.md_rlno);
10205 10194 if (error)
10206 10195 return (error);
10207 10196 cp->c_metadata.md_rltype = CACHEFS_RL_NONE;
10208 10197
10209 10198 /* hold the filegrp so the attrcache file is not gc */
10210 10199 error = filegrp_ffhold(fgp);
10211 10200 if (error) {
10212 10201 cachefs_rlent_moveto(fscp->fs_cache,
10213 10202 CACHEFS_RL_FREE, cp->c_metadata.md_rlno, 0);
10214 10203 cp->c_metadata.md_rlno = 0;
10215 10204 return (error);
10216 10205 }
10217 10206 }
10218 10207 cachefs_modified(cp);
10219 10208 return (0);
10220 10209 }
10221 10210
10222 10211 int
10223 10212 cachefs_vtype_aclok(vnode_t *vp)
10224 10213 {
10225 10214 vtype_t *vtp, oktypes[] = {VREG, VDIR, VFIFO, VNON};
10226 10215
10227 10216 if (vp->v_type == VNON)
10228 10217 return (0);
10229 10218
10230 10219 for (vtp = oktypes; *vtp != VNON; vtp++)
10231 10220 if (vp->v_type == *vtp)
10232 10221 break;
10233 10222
10234 10223 return (*vtp != VNON);
10235 10224 }
10236 10225
10237 10226 static int
10238 10227 cachefs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
10239 10228 caller_context_t *ct)
10240 10229 {
10241 10230 int error = 0;
10242 10231 fscache_t *fscp = C_TO_FSCACHE(VTOC(vp));
10243 10232
10244 10233 /* Assert cachefs compatibility if NFSv4 is in use */
10245 10234 CFS_BACKFS_NFSV4_ASSERT_FSCACHE(fscp);
10246 10235 CFS_BACKFS_NFSV4_ASSERT_CNODE(VTOC(vp));
10247 10236
10248 10237 if (cmd == _PC_FILESIZEBITS) {
10249 10238 u_offset_t maxsize = fscp->fs_offmax;
10250 10239 (*valp) = 0;
10251 10240 while (maxsize != 0) {
10252 10241 maxsize >>= 1;
10253 10242 (*valp)++;
10254 10243 }
10255 10244 (*valp)++;
10256 10245 } else
10257 10246 error = fs_pathconf(vp, cmd, valp, cr, ct);
10258 10247
10259 10248 return (error);
10260 10249 }
↓ open down ↓ |
2749 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX