1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 #pragma ident "%Z%%M% %I% %E% SMI"
26
27 #include <sys/param.h>
28 #include <sys/types.h>
29 #include <sys/systm.h>
30 #include <sys/cred.h>
31 #include <sys/proc.h>
32 #include <sys/user.h>
33 #include <sys/vfs.h>
34 #include <sys/vnode.h>
35 #include <sys/pathname.h>
36 #include <sys/uio.h>
37 #include <sys/tiuser.h>
38 #include <sys/sysmacros.h>
39 #include <sys/kmem.h>
40 #include <sys/ioctl.h>
41 #include <sys/statvfs.h>
42 #include <sys/errno.h>
43 #include <sys/debug.h>
44 #include <sys/cmn_err.h>
45 #include <sys/utsname.h>
46 #include <sys/modctl.h>
47 #include <sys/dirent.h>
48 #include <sys/fbuf.h>
49 #include <rpc/types.h>
50 #include <vm/seg.h>
51 #include <vm/faultcode.h>
52 #include <vm/hat.h>
53 #include <vm/seg_map.h>
54 #include <sys/fs/cachefs_fs.h>
55 #include <sys/fs/cachefs_dir.h>
56 #include <sys/fs/cachefs_log.h>
57
58 /* forward declarations */
59 static int cachefs_dir_getentrys(struct cnode *, u_offset_t, u_offset_t *,
60 uint_t *, uint_t, caddr_t, int *);
61 static int cachefs_dir_stuff(cnode_t *dcp, uint_t count, caddr_t buf,
62 vnode_t *frontvp, u_offset_t *offsetp, u_offset_t *fsizep);
63 static int cachefs_dir_extend(cnode_t *, u_offset_t *, int incr_frontblks);
64 static int cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
65 vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize);
66 static int cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp,
67 vnode_t *frontvp, cred_t *cr, int acltoo);
68
69
70
71 /*
72 * cachefs_dir_look() called mainly by lookup (and create), looks up the cached
73 * directory for an entry and returns the information there. If the directory
74 * entry doesn't exist return ENOENT, if it is incomplete, return EINVAL.
75 * Should only call this routine if the dir is populated.
76 * Returns ENOTDIR if dir gets nuked because of front file problems.
77 */
78 int
cachefs_dir_look(cnode_t * dcp,char * nm,fid_t * cookiep,uint_t * flagp,u_offset_t * d_offsetp,cfs_cid_t * cidp)79 cachefs_dir_look(cnode_t *dcp, char *nm, fid_t *cookiep, uint_t *flagp,
80 u_offset_t *d_offsetp, cfs_cid_t *cidp)
81 {
82 int error;
83 struct vattr va;
84 u_offset_t blockoff = 0LL;
85 uint_t offset = 0; /* offset inside the block of size MAXBSIZE */
86 vnode_t *dvp;
87 struct fscache *fscp = C_TO_FSCACHE(dcp);
88 cachefscache_t *cachep = fscp->fs_cache;
89 int nmlen;
90 struct fbuf *fbp;
91
92 #ifdef CFSDEBUG
93 CFS_DEBUG(CFSDEBUG_DIR)
94 printf("cachefs_dir_look: ENTER dcp %p nm %s\n", (void *)dcp,
95 nm);
96 #endif
97 ASSERT(CTOV(dcp)->v_type == VDIR);
98 ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
99 ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
100
101 if (dcp->c_frontvp == NULL)
102 (void) cachefs_getfrontfile(dcp);
103 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
104 error = ENOTDIR;
105 goto out;
106 }
107
108 dvp = dcp->c_frontvp;
109 va.va_mask = AT_SIZE; /* XXX should save dir size */
110 error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
111 if (error) {
112 cachefs_inval_object(dcp);
113 error = ENOTDIR;
114 goto out;
115 }
116
117 ASSERT(va.va_size != 0LL);
118 nmlen = (int)strlen(nm);
119 while (blockoff < va.va_size) {
120 offset = 0;
121 error =
122 fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
123 if (error)
124 goto out;
125
126 while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
127 struct c_dirent *dep;
128
129 dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
130 offset);
131 if ((dep->d_flag & CDE_VALID) &&
132 (nmlen == dep->d_namelen) &&
133 strcmp(dep->d_name, nm) == 0) {
134 if (dep->d_flag & CDE_COMPLETE) {
135 if (cookiep) {
136 CACHEFS_FID_COPY(&dep->d_cookie,
137 cookiep);
138 }
139 if (flagp)
140 *flagp = dep->d_flag;
141 error = 0;
142 } else {
143 error = EINVAL;
144 }
145 if (cidp)
146 *cidp = dep->d_id;
147 if (d_offsetp)
148 *d_offsetp = offset + blockoff;
149 fbrelse(fbp, S_OTHER);
150 goto out;
151 }
152 ASSERT(dep->d_length != 0);
153 offset += dep->d_length;
154 }
155 fbrelse(fbp, S_OTHER);
156 blockoff += MAXBSIZE;
157 }
158 error = ENOENT;
159
160 out:
161 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
162 cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
163 &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
164 #ifdef CFSDEBUG
165 CFS_DEBUG(CFSDEBUG_DIR)
166 printf("c_dir_look: EXIT error = %d\n", error);
167 #endif
168 return (error);
169 }
170
171 /*
172 * creates a new directory and populates it with "." and ".."
173 */
174 int
cachefs_dir_new(cnode_t * dcp,cnode_t * cp)175 cachefs_dir_new(cnode_t *dcp, cnode_t *cp)
176 {
177 int error = 0;
178 struct c_dirent *dep;
179 u_offset_t size;
180 int len;
181 struct fbuf *fbp;
182 #ifdef CFSDEBUG
183 struct vattr va;
184
185 CFS_DEBUG(CFSDEBUG_DIR)
186 printf("c_dir_new: ENTER dcp %p cp %p\n", (void *)dcp,
187 (void *)cp);
188 #endif
189
190 ASSERT(MUTEX_HELD(&cp->c_statelock));
191 ASSERT(CTOV(cp)->v_type == VDIR);
192 ASSERT((cp->c_flags & CN_ASYNC_POPULATE) == 0);
193 ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
194
195 if (cp->c_frontvp == NULL) {
196 error = cachefs_getfrontfile(cp);
197 if (error)
198 goto out;
199 }
200
201 #ifdef CFSDEBUG
202 va.va_mask = AT_SIZE;
203 error = VOP_GETATTR(cp->c_frontvp, &va, 0, kcred, NULL);
204 if (error)
205 goto out;
206 ASSERT(va.va_size == 0);
207 #endif
208
209 /*
210 * Extend the directory by one MAXBSIZE chunk
211 */
212 size = 0LL;
213 error = cachefs_dir_extend(cp, &size, 1);
214 if (error != 0)
215 goto out;
216 error = fbread(cp->c_frontvp, (offset_t)0, MAXBSIZE, S_OTHER, &fbp);
217 if (error)
218 goto out;
219
220 /*
221 * Insert "." and ".."
222 */
223 len = (int)CDE_SIZE(".");
224 dep = (struct c_dirent *)fbp->fb_addr;
225 dep->d_length = len;
226 dep->d_offset = (offset_t)len;
227 dep->d_flag = CDE_VALID | CDE_COMPLETE;
228 CACHEFS_FID_COPY(&cp->c_cookie, &dep->d_cookie);
229 dep->d_id = cp->c_id;
230 dep->d_namelen = 1;
231 bcopy(".", dep->d_name, 2);
232
233 dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + len);
234 dep->d_length = MAXBSIZE - len;
235 dep->d_offset = MAXBSIZE;
236 dep->d_flag = CDE_VALID | CDE_COMPLETE;
237 CACHEFS_FID_COPY(&dcp->c_cookie, &dep->d_cookie);
238 dep->d_id = dcp->c_id;
239 dep->d_namelen = 2;
240 bcopy("..", dep->d_name, 3);
241
242 (void) fbdwrite(fbp);
243 #ifdef INVALREADDIR
244 cp->c_metadata.md_flags |= MD_POPULATED | MD_INVALREADDIR;
245 #else
246 cp->c_metadata.md_flags |= MD_POPULATED;
247 #endif
248 cp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
249 out:
250 #ifdef CFSDEBUG
251 CFS_DEBUG(CFSDEBUG_DIR)
252 printf("cachefs_dir_new: EXIT error = %d\n", error);
253 #endif
254 return (error);
255 }
256
257 /*
258 * cachefs_dir_enter adds a new directory entry. Takes as input a fid,
259 * fileno and a sync flag. Most of the time, the caller is content with the
260 * write to the (front) directory being done async. The exception being - for
261 * local files, we should make sure that the directory entry is made
262 * synchronously. That is notified by the caller.
263 * issync == 0 || issync == SM_ASYNC !
264 *
265 * The new entry is inserted at the end, so that we can generate local offsets
266 * which are compatible with the backfs offsets (which are used when
267 * disconnected.
268 */
269 int
cachefs_dir_enter(cnode_t * dcp,char * nm,fid_t * cookiep,cfs_cid_t * cidp,int issync)270 cachefs_dir_enter(cnode_t *dcp, char *nm, fid_t *cookiep, cfs_cid_t *cidp,
271 int issync)
272 {
273 struct vattr va;
274 int offset;
275 u_offset_t blockoff = 0LL;
276 u_offset_t prev_offset;
277 int error = 0;
278 vnode_t *dvp;
279 struct c_dirent *dep;
280 uint_t esize;
281 u_offset_t dirsize;
282 struct fbuf *fbp;
283
284 #ifdef CFSDEBUG
285 CFS_DEBUG(CFSDEBUG_DIR)
286 printf("c_dir_enter: ENTER dcp %p nm %s dirflg %x\n",
287 (void *)dcp, nm, dcp->c_metadata.md_flags);
288 #endif
289
290 ASSERT(MUTEX_HELD(&dcp->c_statelock));
291 ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
292 ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
293 ASSERT(CTOV(dcp)->v_type == VDIR);
294 ASSERT(issync == 0 || issync == SM_ASYNC);
295 ASSERT(strlen(nm) <= MAXNAMELEN);
296
297 if (dcp->c_frontvp == NULL)
298 (void) cachefs_getfrontfile(dcp);
299 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
300 error = ENOTDIR;
301 goto out;
302 }
303 dvp = dcp->c_frontvp;
304
305 /*
306 * Get the current EOF for the directory(data file)
307 */
308 va.va_mask = AT_SIZE;
309 error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
310 if (error) {
311 cachefs_inval_object(dcp);
312 error = ENOTDIR;
313 goto out;
314 }
315
316 /*
317 * Get the last block of the directory
318 */
319 dirsize = va.va_size;
320 ASSERT(dirsize != 0LL);
321 ASSERT(!(dirsize & MAXBOFFSET));
322 ASSERT(dirsize <= MAXOFF_T);
323 blockoff = dirsize - MAXBSIZE;
324 error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
325 if (error)
326 goto out;
327
328 /*
329 * Find the last entry
330 */
331 offset = 0;
332 prev_offset = blockoff;
333 for (;;) {
334 dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + offset);
335 if (offset + dep->d_length == MAXBSIZE)
336 break;
337 prev_offset = dep->d_offset;
338 offset += dep->d_length;
339 ASSERT(offset < MAXBSIZE);
340 }
341 esize = C_DIRSIZ(dep);
342
343 if (dep->d_length - esize >= CDE_SIZE(nm)) {
344 /*
345 * It has room. If the entry is not valid, we can just use
346 * it. Otherwise, we need to adjust its length and offset
347 */
348 #ifdef CFSDEBUG
349 CFS_DEBUG(CFSDEBUG_DIR) {
350 if (prev_offset >= dep->d_offset) {
351 printf("cachefs_dir_enter: looks like "
352 "we might fail the assert\n");
353 printf("addr %p, offset %x, "
354 "prev_offset %llx, dep->d_offset %llx\n",
355 (void *)fbp->fb_addr, offset, prev_offset,
356 dep->d_offset);
357 offset = 0;
358 prev_offset = blockoff;
359 for (;;) {
360 dep = (struct c_dirent *)
361 ((uintptr_t)fbp->fb_addr + offset);
362 printf("offset %x, prev_offset %llx\n",
363 offset, prev_offset);
364 printf("dep->d_offset %llx, "
365 "dep->d_length %x\n",
366 dep->d_offset, dep->d_length);
367 if (offset + dep->d_length == MAXBSIZE)
368 break;
369 prev_offset = dep->d_offset;
370 offset += dep->d_length;
371 }
372 }
373 }
374 #endif /* CFSDEBUG */
375
376 if (offset)
377 ASSERT(prev_offset < dep->d_offset);
378 if (dep->d_flag & CDE_VALID) {
379 dep->d_length = esize;
380 dep->d_offset = prev_offset + (u_offset_t)esize;
381 dep = (struct c_dirent *)((uintptr_t)dep + esize);
382 }
383 dep->d_length = (int)((offset_t)MAXBSIZE -
384 ((uintptr_t)dep - (uintptr_t)fbp->fb_addr));
385 } else {
386 /*
387 * No room - so extend the file by one more
388 * MAXBSIZE chunk, and fit the entry there.
389 */
390 fbrelse(fbp, S_OTHER);
391 error = cachefs_dir_extend(dcp, &dirsize, 1);
392 if (error != 0)
393 goto out;
394 error =
395 fbread(dvp, (offset_t)va.va_size, MAXBSIZE, S_OTHER, &fbp);
396 if (error)
397 goto out;
398 dep = (struct c_dirent *)fbp->fb_addr;
399 dep->d_length = MAXBSIZE;
400 }
401
402 /*
403 * Fill in the rest of the new entry
404 */
405 dep->d_offset = dirsize;
406 dep->d_flag = CDE_VALID;
407 if (cookiep) {
408 dep->d_flag |= CDE_COMPLETE;
409 CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
410 }
411 dep->d_id = *cidp;
412 dep->d_namelen = (ushort_t)strlen(nm);
413 (void) bcopy(nm, dep->d_name, dep->d_namelen + 1);
414
415 #ifdef INVALREADDIR
416 dcp->c_metadata.md_flags |= MD_INVALREADDIR;
417 #endif
418 dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
419 if (issync)
420 (void) fbwrite(fbp);
421 else
422 (void) fbdwrite(fbp);
423 out:
424 #ifdef CFSDEBUG
425 CFS_DEBUG(CFSDEBUG_DIR)
426 printf("cachefs_dir_enter: EXIT error = %d\n", error);
427 #endif
428 return (error);
429 }
430
431 /*
432 * Quite simple, if the deleted entry is the first in the MAXBSIZE block,
433 * we simply mark it invalid. Otherwise, the deleted entries d_length is
434 * just added to the previous entry.
435 */
436 int
cachefs_dir_rmentry(cnode_t * dcp,char * nm)437 cachefs_dir_rmentry(cnode_t *dcp, char *nm)
438 {
439 u_offset_t blockoff = 0LL;
440 int offset = 0;
441 struct vattr va;
442 int error = ENOENT;
443 vnode_t *dvp;
444 int nmlen;
445 struct fbuf *fbp;
446
447 #ifdef CFSDEBUG
448 CFS_DEBUG(CFSDEBUG_DIR)
449 printf("cachefs_dir_rmentry: ENTER dcp %p nm %s\n",
450 (void *)dcp, nm);
451 #endif
452 ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
453 ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
454
455 if (dcp->c_frontvp == NULL)
456 (void) cachefs_getfrontfile(dcp);
457 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
458 error = ENOTDIR;
459 goto out;
460 }
461 dvp = dcp->c_frontvp;
462
463 ASSERT(CTOV(dcp)->v_type == VDIR);
464 ASSERT((dcp->c_flags & CN_NOCACHE) == 0);
465 ASSERT(dvp != NULL);
466 va.va_mask = AT_SIZE;
467 error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
468 if (error) {
469 cachefs_inval_object(dcp);
470 error = ENOTDIR;
471 goto out;
472 }
473 ASSERT(va.va_size != 0LL);
474
475 nmlen = (int)strlen(nm);
476 while (blockoff < va.va_size) {
477 uint_t *last_len;
478
479 offset = 0;
480 last_len = NULL;
481 error =
482 fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
483 if (error)
484 goto out;
485 while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
486 struct c_dirent *dep;
487
488 dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
489 offset);
490 if ((dep->d_flag & CDE_VALID) &&
491 (nmlen == dep->d_namelen) &&
492 strcmp(dep->d_name, nm) == 0) {
493 /*
494 * Found the entry. If this was the first entry
495 * in the MAXBSIZE block, Mark it invalid. Else
496 * add it's length to the previous entry's
497 * length.
498 */
499 if (last_len == NULL) {
500 ASSERT(offset == 0);
501 dep->d_flag = 0;
502 } else
503 *last_len += dep->d_length;
504 (void) fbdwrite(fbp);
505 dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
506 goto out;
507 }
508 last_len = &dep->d_length;
509 offset += dep->d_length;
510 }
511 fbrelse(fbp, S_OTHER);
512 blockoff += MAXBSIZE;
513 }
514 error = ENOENT;
515
516 out:
517 #ifdef CFSDEBUG
518 CFS_DEBUG(CFSDEBUG_DIR)
519 printf("cachefs_dir_rmentry: EXIT error = %d\n", error);
520 #endif
521 return (error);
522 }
523
524 #if 0
525 /*
526 * This function is used only in cachefs_lookup_back() routine but
527 * is inside #if 0 directive in this routine. So I am keeping this
528 * routine also in #if 0 directive.
529 */
530
531 /*
532 * This function fills in the cookie and file no of the directory entry
533 * at the offset specified by offset - In other words, makes the entry
534 * "complete".
535 */
536 int
537 cachefs_dir_modentry(cnode_t *dcp, u_offset_t offset, fid_t *cookiep,
538 cfs_cid_t *cidp)
539 {
540 struct c_dirent *dep;
541 u_offset_t blockoff = (offset & (offset_t)MAXBMASK);
542 uint_t off = (uint_t)(offset & (offset_t)MAXBOFFSET);
543 struct fbuf *fbp;
544 vnode_t *dvp;
545 int error = 0;
546
547 #ifdef CFSDEBUG
548 CFS_DEBUG(CFSDEBUG_DIR)
549 printf("cachefs_dir_modentry: ENTER dcp %p offset %lld\n",
550 (void *)dcp, offset);
551 #endif
552 ASSERT(CTOV(dcp)->v_type == VDIR);
553 ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
554 ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
555
556 if (dcp->c_frontvp == NULL)
557 (void) cachefs_getfrontfile(dcp);
558 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
559 return;
560 }
561 dvp = dcp->c_frontvp;
562
563 error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
564 if (error)
565 goto out;
566 dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
567 if (cookiep) {
568 dep->d_flag |= CDE_COMPLETE;
569 CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
570 }
571 if (cidp)
572 dep->d_id = *cidp;
573 (void) fbdwrite(fbp);
574 dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
575
576 out:
577 #ifdef CFSDEBUG
578 CFS_DEBUG(CFSDEBUG_DIR)
579 printf("cachefs_dir_modentry: EXIT\n");
580 #endif
581 return (error);
582 }
583
584 #endif /* of #if 0 */
585
586 /*
587 * Called by cachefs_read_dir(). Gets a bunch if directory entries into buf and
588 * packs them into buf.
589 */
590 static int
cachefs_dir_getentrys(struct cnode * dcp,u_offset_t beg_off,u_offset_t * last_offp,uint_t * cntp,uint_t bufsize,caddr_t buf,int * eofp)591 cachefs_dir_getentrys(struct cnode *dcp, u_offset_t beg_off,
592 u_offset_t *last_offp, uint_t *cntp, uint_t bufsize,
593 caddr_t buf, int *eofp)
594 {
595
596 #define DIR_ENDOFF 0x7fffffffLL
597
598 struct vattr va;
599 struct c_dirent *dep;
600 struct fbuf *fbp = NULL;
601 struct dirent64 *gdp;
602 u_offset_t blockoff;
603 uint_t off;
604 int error;
605 vnode_t *dvp = dcp->c_frontvp;
606
607 #ifdef CFSDEBUG
608 CFS_DEBUG(CFSDEBUG_DIR)
609 printf("cachefs_dir_getentrys: "
610 "ENTER dcp %p beg_off %lld mdflags %x cflags %x\n",
611 dcp, beg_off, dcp->c_metadata.md_flags, dcp->c_flags);
612 #endif
613
614 ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
615
616 /*
617 * blockoff has the offset of the MAXBSIZE block that contains the
618 * entry to start with. off contains the offset relative to the
619 * begining of the MAXBSIZE block.
620 */
621 if (eofp)
622 *eofp = 0;
623 gdp = (struct dirent64 *)buf;
624 *cntp = bufsize;
625 va.va_mask = AT_SIZE;
626 error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
627 if (error) {
628 *cntp = 0;
629 *last_offp = 0;
630 if (eofp)
631 *eofp = 1;
632 goto out;
633 }
634 ASSERT(va.va_size != 0LL);
635
636 if (beg_off == DIR_ENDOFF) {
637 *cntp = 0;
638 *last_offp = DIR_ENDOFF;
639 if (eofp)
640 *eofp = 1;
641 goto out;
642 }
643
644 /*
645 * locate the offset where we start reading.
646 */
647 for (blockoff = 0; blockoff < va.va_size; blockoff += MAXBSIZE) {
648 error =
649 fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
650 if (error)
651 goto out;
652 dep = (struct c_dirent *)fbp->fb_addr;
653 off = 0;
654 while (off < MAXBSIZE && dep->d_offset <= beg_off) {
655 off += dep->d_length;
656 dep =
657 (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
658 }
659 if (off < MAXBSIZE)
660 break;
661 fbrelse(fbp, S_OTHER);
662 fbp = NULL;
663 }
664
665 if (blockoff >= va.va_size) {
666 *cntp = 0;
667 *last_offp = DIR_ENDOFF;
668 if (eofp)
669 *eofp = 1;
670 goto out;
671 }
672
673 /*
674 * Just load up the buffer with directory entries.
675 */
676 for (;;) {
677 uint_t size;
678 int this_reclen;
679
680 ASSERT((uintptr_t)dep < ((uintptr_t)fbp->fb_addr + MAXBSIZE));
681 if (dep->d_flag & CDE_VALID) {
682 this_reclen = DIRENT64_RECLEN(dep->d_namelen);
683 size = C_DIRSIZ(dep);
684 ASSERT(size < MAXBSIZE);
685 if (this_reclen > bufsize)
686 break;
687 ASSERT(dep->d_namelen <= MAXNAMELEN);
688 ASSERT(dep->d_offset > (*last_offp));
689 gdp->d_ino = dep->d_id.cid_fileno;
690 gdp->d_off = dep->d_offset;
691
692 /* use strncpy(9f) to zero out uninitialized bytes */
693
694 ASSERT(strlen(dep->d_name) + 1 <=
695 DIRENT64_NAMELEN(this_reclen));
696 (void) strncpy(gdp->d_name, dep->d_name,
697 DIRENT64_NAMELEN(this_reclen));
698
699 gdp->d_reclen = (ushort_t)this_reclen;
700 bufsize -= this_reclen;
701 gdp = (struct dirent64 *)((uintptr_t)gdp +
702 gdp->d_reclen);
703 *last_offp = dep->d_offset;
704 }
705
706 /*
707 * Increment the offset. If we've hit EOF, fill in
708 * the lastoff and current entries d_off field.
709 */
710 off += dep->d_length;
711 ASSERT(off <= MAXBSIZE);
712 if ((blockoff + off) >= va.va_size) {
713 *last_offp = DIR_ENDOFF;
714 if (eofp)
715 *eofp = 1;
716 break;
717 }
718 /*
719 * If off == MAXBSIZE, then we need to adjust our
720 * window to the next MAXBSIZE block of the directory.
721 * Adjust blockoff, off and map it in. Also, increment
722 * the directory and buffer pointers.
723 */
724 if (off == MAXBSIZE) {
725 fbrelse(fbp, S_OTHER);
726 fbp = NULL;
727 off = 0;
728 blockoff += MAXBSIZE;
729 error = fbread(dvp, (offset_t)blockoff, MAXBSIZE,
730 S_OTHER, &fbp);
731 if (error)
732 goto out;
733 }
734 dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
735 }
736 *cntp -= bufsize;
737 out:
738 /*
739 * Release any buffer and maping that may exist.
740 */
741 if (fbp)
742 (void) fbrelse(fbp, S_OTHER);
743 #ifdef CFSDEBUG
744 CFS_DEBUG(CFSDEBUG_DIR)
745 printf("ccachefs_dir_getentrys: EXIT error = %d\n", error);
746 #endif
747 return (error);
748 }
749
750 /*
751 * Called by cachefs_readdir(). Fills a directory request from the cache
752 */
753 int
cachefs_dir_read(struct cnode * dcp,struct uio * uiop,int * eofp)754 cachefs_dir_read(struct cnode *dcp, struct uio *uiop, int *eofp)
755 {
756 int error;
757 uint_t count;
758 uint_t size;
759 caddr_t buf;
760 u_offset_t next = uiop->uio_loffset;
761 struct fscache *fscp = C_TO_FSCACHE(dcp);
762 cachefscache_t *cachep = fscp->fs_cache;
763 caddr_t chrp, end;
764 dirent64_t *de;
765
766 ASSERT(CTOV(dcp)->v_type == VDIR);
767 ASSERT(RW_READ_HELD(&dcp->c_rwlock));
768
769 ASSERT(next <= MAXOFF_T);
770 #ifdef CFSDEBUG
771 CFS_DEBUG(CFSDEBUG_DIR)
772 printf("cachefs_dir_read: ENTER dcp %p\n", (void *)dcp);
773 #endif
774 ASSERT((dcp->c_metadata.md_flags & (MD_FILE|MD_POPULATED)) ==
775 (MD_FILE|MD_POPULATED));
776 ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
777
778 if (dcp->c_frontvp == NULL)
779 (void) cachefs_getfrontfile(dcp);
780 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
781 error = ENOTDIR;
782 goto out;
783 }
784
785 size = (uint_t)uiop->uio_resid;
786 buf = cachefs_kmem_alloc(size, KM_SLEEP);
787 error = cachefs_dir_getentrys(dcp, next, &next, &count, size,
788 buf, eofp);
789 if (error == 0 && (int)count > 0) {
790 ASSERT(count <= size);
791 if (fscp->fs_inum_size > 0) {
792 ino64_t newinum;
793
794 mutex_exit(&dcp->c_statelock);
795 mutex_enter(&fscp->fs_fslock);
796 end = (caddr_t)((uintptr_t)buf + count);
797 for (chrp = buf; chrp < end; chrp += de->d_reclen) {
798 de = (dirent64_t *)chrp;
799
800 newinum = cachefs_inum_real2fake(fscp,
801 de->d_ino);
802 if (newinum == 0)
803 newinum = cachefs_fileno_conflict(fscp,
804 de->d_ino);
805 de->d_ino = newinum;
806 }
807 mutex_exit(&fscp->fs_fslock);
808 mutex_enter(&dcp->c_statelock);
809 }
810 error = uiomove(buf, count, UIO_READ, uiop);
811 if (error == 0)
812 uiop->uio_loffset = next;
813 }
814 (void) cachefs_kmem_free(buf, size);
815 out:
816 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
817 cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
818 &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
819 #ifdef CFSDEBUG
820 CFS_DEBUG(CFSDEBUG_DIR)
821 printf("cachefs_dir_read: EXIT error = %d\n", error);
822 #endif
823 return (error);
824 }
825
826 /*
827 * Fully (including cookie) populates the directory from the back filesystem.
828 */
829 int
cachefs_dir_fill(cnode_t * dcp,cred_t * cr)830 cachefs_dir_fill(cnode_t *dcp, cred_t *cr)
831 {
832 int error = 0;
833 u_offset_t frontsize;
834 struct fscache *fscp = C_TO_FSCACHE(dcp);
835
836 #ifdef CFSDEBUG
837 CFS_DEBUG(CFSDEBUG_DIR)
838 printf("cachefs_dir_fill: ENTER dcp %p\n", (void *)dcp);
839 #endif
840 ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
841 ASSERT(MUTEX_HELD(&dcp->c_statelock));
842
843 /* XXX for now return success if async populate is scheduled */
844 if (dcp->c_flags & CN_ASYNC_POPULATE)
845 goto out;
846
847 /* get the back vp */
848 if (dcp->c_backvp == NULL) {
849 error = cachefs_getbackvp(fscp, dcp);
850 if (error) {
851 goto out;
852 }
853 }
854
855 /* get the front file vp */
856 if (dcp->c_frontvp == NULL)
857 (void) cachefs_getfrontfile(dcp);
858 if (dcp->c_flags & CN_NOCACHE) {
859 error = ENOTDIR;
860 goto out;
861 }
862
863 /* if dir was modified, toss old contents */
864 if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
865 cachefs_inval_object(dcp);
866 if (dcp->c_flags & CN_NOCACHE) {
867 error = ENOTDIR;
868 goto out;
869 }
870 }
871
872 error = cachefs_dir_fill_common(dcp, cr,
873 dcp->c_frontvp, dcp->c_backvp, &frontsize);
874 if (error == 0)
875 error = cachefs_dir_complete(fscp, dcp->c_backvp,
876 dcp->c_frontvp, cr, 0);
877 if (error != 0)
878 goto out;
879
880 /*
881 * Mark the directory as not empty. Also bang the flag that says that
882 * this directory needs to be sync'ed on inactive.
883 */
884 dcp->c_metadata.md_flags |= MD_POPULATED;
885 dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
886 dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
887 /*LINTED alignment okay*/
888 dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
889
890 out:
891 if (error) {
892 #ifdef CFSDEBUG
893 CFS_DEBUG(CFSDEBUG_INVALIDATE)
894 printf("c_dir_fill: invalidating %llu\n",
895 (u_longlong_t)dcp->c_id.cid_fileno);
896 #endif
897 cachefs_inval_object(dcp);
898 }
899
900 return (error);
901 }
902
903 /*
904 * Does work of populating directory.
905 * Must be called while holding dcp->c_statelock
906 */
907
908 static int
cachefs_dir_fill_common(cnode_t * dcp,cred_t * cr,vnode_t * frontvp,vnode_t * backvp,u_offset_t * frontsize)909 cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
910 vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize)
911 {
912 int error = 0;
913 struct uio uio;
914 struct iovec iov;
915 caddr_t buf = NULL;
916 int count;
917 int eof = 0;
918 u_offset_t frontoff;
919 struct fscache *fscp = C_TO_FSCACHE(dcp);
920 cachefscache_t *cachep = fscp->fs_cache;
921 #ifdef DEBUG
922 int loop_count = 0;
923 #endif
924 #ifdef CFSDEBUG
925 CFS_DEBUG(CFSDEBUG_DIR)
926 printf("cachefs_dir_fill_common: ENTER dcp %p\n", (void *)dcp);
927 #endif
928
929 ASSERT(MUTEX_HELD(&dcp->c_statelock));
930
931 frontoff = *frontsize = 0LL;
932
933 buf = cachefs_kmem_alloc(MAXBSIZE, KM_SLEEP);
934 uio.uio_iov = &iov;
935 uio.uio_iovcnt = 1;
936 uio.uio_segflg = UIO_SYSSPACE;
937 uio.uio_fmode = 0;
938 uio.uio_extflg = UIO_COPY_CACHED;
939 uio.uio_loffset = 0;
940 for (;;) {
941 #ifdef DEBUG
942 loop_count++;
943 #endif
944 /*
945 * Read in a buffer's worth of dirents and enter them in to the
946 * directory.
947 */
948 uio.uio_resid = MAXBSIZE;
949 iov.iov_base = buf;
950 iov.iov_len = MAXBSIZE;
951 (void) VOP_RWLOCK(backvp, V_WRITELOCK_FALSE, NULL);
952 error = VOP_READDIR(backvp, &uio, cr, &eof, NULL, 0);
953 VOP_RWUNLOCK(backvp, V_WRITELOCK_FALSE, NULL);
954 if (error)
955 goto out;
956
957 /*LINTED alignment okay*/
958 count = MAXBSIZE - (int)uio.uio_resid;
959 ASSERT(count >= 0);
960 if (count > 0) {
961 if (error = cachefs_dir_stuff(dcp, count, buf,
962 frontvp, &frontoff, frontsize))
963 goto out;
964 ASSERT((*frontsize) != 0LL);
965 }
966 if (eof || count == 0)
967 break;
968 }
969
970 if (*frontsize == 0LL) {
971 /* keep us from caching an empty directory */
972 error = EINVAL;
973 goto out;
974 }
975
976 out:
977 if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_FILLDIR))
978 cachefs_log_filldir(cachep, error, fscp->fs_cfsvfsp,
979 &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno,
980 *frontsize);
981 if (buf)
982 cachefs_kmem_free(buf, (uint_t)MAXBSIZE);
983
984 #ifdef CFSDEBUG
985 CFS_DEBUG(CFSDEBUG_DIR)
986 printf("cachefs_dir_fill: EXIT error = %d\n", error);
987 #endif
988 return (error);
989 }
990
991 /*
992 * If the directory contains only the elements "." and "..", then this returns
993 * 0, otherwise returns an error.
994 */
995 int
cachefs_dir_empty(cnode_t * dcp)996 cachefs_dir_empty(cnode_t *dcp)
997 {
998 struct vattr va;
999 u_offset_t blockoff = 0;
1000 int offset;
1001 struct fbuf *fbp;
1002 int error;
1003 vnode_t *dvp = dcp->c_frontvp;
1004
1005 #ifdef CFSDEBUG
1006 CFS_DEBUG(CFSDEBUG_DIR)
1007 printf("cachefs_dir_empty: ENTER dcp %p\n", (void *)dcp);
1008 #endif
1009 ASSERT(CTOV(dcp)->v_type == VDIR);
1010 ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
1011 ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
1012
1013 if (dcp->c_frontvp == NULL)
1014 (void) cachefs_getfrontfile(dcp);
1015 if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0)
1016 return (ENOTDIR);
1017
1018 va.va_mask = AT_SIZE;
1019 error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
1020 if (error)
1021 return (ENOTDIR);
1022
1023 ASSERT(va.va_size != 0LL);
1024 while (blockoff < va.va_size) {
1025 offset = 0;
1026 error =
1027 fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
1028 if (error)
1029 return (error);
1030 while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
1031 struct c_dirent *dep;
1032
1033 dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
1034 offset);
1035 if ((dep->d_flag & CDE_VALID) &&
1036 ((strcmp(dep->d_name, ".") != 0) &&
1037 (strcmp(dep->d_name, "..") != 0))) {
1038 (void) fbrelse(fbp, S_OTHER);
1039 return (0);
1040 }
1041 offset += dep->d_length;
1042 }
1043 (void) fbrelse(fbp, S_OTHER);
1044 blockoff += MAXBSIZE;
1045 }
1046 return (EEXIST);
1047 }
1048
1049 /*
1050 * Called by cachefs_dir_fill() to stuff a buffer of dir entries into
1051 * a front file. This is more efficient than repeated calls to
1052 * cachefs_dir_enter, and it also allows us to maintain entries in backfs
1053 * order (readdir requires that entry offsets be ascending).
1054 */
1055 static int
cachefs_dir_stuff(cnode_t * dcp,uint_t count,caddr_t buf,vnode_t * frontvp,u_offset_t * offsetp,u_offset_t * fsizep)1056 cachefs_dir_stuff(cnode_t *dcp, uint_t count, caddr_t buf,
1057 vnode_t *frontvp, u_offset_t *offsetp, u_offset_t *fsizep)
1058 {
1059 int error = 0;
1060 struct fbuf *fbp;
1061 struct c_dirent *cdep, *last;
1062 struct dirent64 *dep;
1063 int inblk, entsize;
1064 u_offset_t blockoff = (*offsetp & (offset_t)MAXBMASK);
1065 /*LINTED alignment okay*/
1066 uint_t off = (uint_t)(*offsetp & (offset_t)MAXBOFFSET);
1067
1068 /*LINTED want count != 0*/
1069 ASSERT(count > 0);
1070
1071 if (*offsetp >= *fsizep) {
1072 error = cachefs_dir_extend(dcp, fsizep, 0);
1073 if (error)
1074 return (error);
1075 }
1076
1077 ASSERT(*fsizep != 0LL);
1078 last = NULL;
1079 error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
1080 if (error)
1081 return (error);
1082 cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
1083 inblk = MAXBSIZE-off;
1084 if (*offsetp != 0) {
1085 ASSERT(cdep->d_length == inblk);
1086 inblk -= C_DIRSIZ(cdep);
1087 last = cdep;
1088 last->d_length -= inblk;
1089 off += last->d_length;
1090 cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
1091 }
1092 dep = (struct dirent64 *)buf;
1093 /*LINTED want count != 0*/
1094 while (count > 0) {
1095 if (last) {
1096 ASSERT(dep->d_off > last->d_offset);
1097 }
1098 entsize = (int)CDE_SIZE(dep->d_name);
1099 if (entsize > inblk) {
1100 if (last) {
1101 last->d_length += inblk;
1102 }
1103 (void) fbwrite(fbp);
1104 error = cachefs_dir_extend(dcp, fsizep, 0);
1105 if (error)
1106 return (error);
1107 ASSERT(*fsizep != 0LL);
1108 blockoff += MAXBSIZE;
1109 error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE,
1110 S_OTHER, &fbp);
1111 if (error)
1112 return (error);
1113 off = 0;
1114 cdep = (struct c_dirent *)fbp->fb_addr;
1115 inblk = MAXBSIZE;
1116 last = NULL;
1117 }
1118 cdep->d_length = entsize;
1119 cdep->d_id.cid_fileno = dep->d_ino;
1120 cdep->d_id.cid_flags = 0;
1121 cdep->d_namelen = (ushort_t)strlen(dep->d_name);
1122 cdep->d_flag = CDE_VALID;
1123 bcopy(dep->d_name, cdep->d_name, cdep->d_namelen+1);
1124 cdep->d_offset = dep->d_off;
1125 inblk -= entsize;
1126 count -= dep->d_reclen;
1127 dep = (struct dirent64 *)((uintptr_t)dep + dep->d_reclen);
1128 *offsetp = blockoff + (u_offset_t)off;
1129 off += entsize;
1130 last = cdep;
1131 cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
1132 }
1133 if (last) {
1134 last->d_length += inblk;
1135 }
1136 (void) fbwrite(fbp);
1137 return (error);
1138 }
1139
1140 static int
cachefs_dir_extend(cnode_t * dcp,u_offset_t * cursize,int incr_frontblks)1141 cachefs_dir_extend(cnode_t *dcp, u_offset_t *cursize, int incr_frontblks)
1142 {
1143 struct vattr va;
1144 cachefscache_t *cachep = C_TO_FSCACHE(dcp)->fs_cache;
1145 int error = 0;
1146 struct fscache *fscp = VFS_TO_FSCACHE(CTOV(dcp)->v_vfsp);
1147
1148 ASSERT(MUTEX_HELD(&dcp->c_statelock));
1149 ASSERT(((*cursize) & (MAXBSIZE-1)) == 0);
1150
1151 va.va_mask = AT_SIZE;
1152 va.va_size = (u_offset_t)(*cursize + MAXBSIZE);
1153 error = cachefs_allocblocks(cachep, 1, dcp->c_metadata.md_rltype);
1154 if (error)
1155 return (error);
1156 error = VOP_SETATTR(dcp->c_frontvp, &va, 0, kcred, NULL);
1157 if (error) {
1158 cachefs_freeblocks(cachep, 1, dcp->c_metadata.md_rltype);
1159 return (error);
1160 }
1161 if (incr_frontblks)
1162 dcp->c_metadata.md_frontblks++;
1163 if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
1164 dcp->c_size += MAXBSIZE;
1165 dcp->c_attr.va_size = dcp->c_size;
1166 }
1167 *cursize += MAXBSIZE;
1168 ASSERT(*cursize != 0LL);
1169 if (incr_frontblks)
1170 dcp->c_flags |= CN_UPDATED;
1171 return (0);
1172 }
1173
1174 int
cachefs_async_populate_dir(struct cachefs_populate_req * pop,cred_t * cr,vnode_t * backvp,vnode_t * frontvp)1175 cachefs_async_populate_dir(struct cachefs_populate_req *pop, cred_t *cr,
1176 vnode_t *backvp, vnode_t *frontvp)
1177 {
1178 vnode_t *dvp = pop->cpop_vp;
1179 struct cnode *dcp = VTOC(dvp);
1180 u_offset_t frontsize;
1181 int error = 0;
1182
1183 #ifdef CFSDEBUG
1184 CFS_DEBUG(CFSDEBUG_DIR)
1185 printf("cachefs_async_populate_dir: ENTER dvp %p\n",
1186 (void *)dvp);
1187 #endif
1188 ASSERT(MUTEX_HELD(&dcp->c_statelock));
1189 ASSERT(dvp->v_type == VDIR);
1190 ASSERT((dcp->c_metadata.md_flags & MD_POPULATED) == 0);
1191 ASSERT(dcp->c_frontvp == frontvp);
1192 ASSERT(dcp->c_backvp == backvp);
1193 ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
1194
1195 /* if dir was modified, toss old contents */
1196 if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
1197 cachefs_inval_object(dcp);
1198 if (dcp->c_flags & CN_NOCACHE) {
1199 error = ENOTDIR;
1200 goto out;
1201 } else {
1202 dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
1203 }
1204 }
1205
1206
1207 error = cachefs_dir_fill_common(dcp, cr, frontvp, backvp, &frontsize);
1208 if (error != 0)
1209 goto out;
1210 ASSERT(frontsize != 0LL);
1211 mutex_exit(&dcp->c_statelock);
1212 /*
1213 * I don't like to break lock here but cachefs_dir_complete()
1214 * needs it.
1215 */
1216 error = cachefs_dir_complete(C_TO_FSCACHE(dcp), backvp,
1217 frontvp, cr, 1);
1218 mutex_enter(&dcp->c_statelock);
1219 if (error != 0)
1220 goto out;
1221 /* if went nocache while lock was dropped, get out */
1222 if ((dcp->c_flags & CN_NOCACHE) || (dcp->c_frontvp == NULL)) {
1223 error = EINVAL;
1224 } else {
1225 /* allocfile and allocblocks have already happened. */
1226 dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
1227 }
1228
1229 out:
1230
1231 #ifdef CFSDEBUG
1232 CFS_DEBUG(CFSDEBUG_DIR)
1233 printf("cachefs_async_populate_dir: EXIT error = %d\n", error);
1234 #endif
1235
1236 return (error);
1237 }
1238
1239 static int
cachefs_dir_complete(fscache_t * fscp,vnode_t * backvp,vnode_t * frontvp,cred_t * cr,int acltoo)1240 cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp, vnode_t *frontvp,
1241 cred_t *cr, int acltoo)
1242 {
1243 struct c_dirent *dep;
1244 caddr_t buf = kmem_alloc(MAXBSIZE, KM_SLEEP);
1245 struct vattr va;
1246 u_offset_t blockoff;
1247 int offset;
1248 u_offset_t dir_size;
1249 struct fbuf *fbp;
1250 cnode_t *cp;
1251 fid_t cookie;
1252 vnode_t *entry_vp;
1253 int error = 0;
1254
1255 /*
1256 * note: caller better not hold a c_statelock if acltoo is set.
1257 */
1258
1259 va.va_mask = AT_SIZE;
1260 error = VOP_GETATTR(frontvp, &va, 0, cr, NULL);
1261 if (error)
1262 goto out;
1263
1264 ASSERT(va.va_size != 0LL);
1265 dir_size = va.va_size;
1266 ASSERT(dir_size <= MAXOFF_T);
1267
1268 for (blockoff = 0; blockoff < dir_size; blockoff += MAXBSIZE) {
1269 if (error = fbread(frontvp, (offset_t)blockoff,
1270 MAXBSIZE, S_OTHER, &fbp))
1271 goto out;
1272
1273 /*
1274 * We cannot hold any page locks across the below VOP
1275 * operations. We thus copy the directory entries into a
1276 * staging buffer, and release the page lock on the directory
1277 * by calling fbrelse(). Once any necessary cnodes have
1278 * been created, we'll reacquire the page lock with fbread()
1279 * and copy the staging buffer back into the frontvp at
1280 * blockoff.
1281 */
1282 bcopy(fbp->fb_addr, buf, MAXBSIZE);
1283 fbrelse(fbp, S_OTHER);
1284
1285 for (offset = 0;
1286 offset < MAXBSIZE &&
1287 (blockoff + (u_offset_t)offset) < dir_size;
1288 offset += dep->d_length) {
1289
1290 dep = (struct c_dirent *)((uintptr_t)buf + offset);
1291 ASSERT(dep->d_length != 0);
1292 if ((dep->d_flag & (CDE_VALID | CDE_COMPLETE)) !=
1293 CDE_VALID)
1294 continue;
1295
1296 error = VOP_LOOKUP(backvp, dep->d_name,
1297 &entry_vp, (struct pathname *)NULL, 0,
1298 (vnode_t *)NULL, cr, NULL, NULL, NULL);
1299 if (error) {
1300 /* lookup on .. in / on coc gets ENOENT */
1301 if (error == ENOENT) {
1302 error = 0;
1303 continue;
1304 }
1305 goto out;
1306 }
1307
1308 error = cachefs_getcookie(entry_vp, &cookie, NULL, cr,
1309 TRUE);
1310 if (error) {
1311 #ifdef CFSDEBUG
1312 CFS_DEBUG(CFSDEBUG_DIR)
1313 printf("\t%s: getcookie error\n",
1314 dep->d_name);
1315 #endif /* CFSDEBUG */
1316 VN_RELE(entry_vp);
1317 goto out;
1318 }
1319 CACHEFS_FID_COPY(&cookie, &dep->d_cookie);
1320 dep->d_flag |= CDE_COMPLETE;
1321
1322 if ((! acltoo) ||
1323 (! cachefs_vtype_aclok(entry_vp)) ||
1324 (fscp->fs_info.fi_mntflags & CFS_NOACL)) {
1325 VN_RELE(entry_vp);
1326 continue;
1327 }
1328
1329 error = cachefs_cnode_make(&dep->d_id, fscp, &cookie,
1330 NULL, entry_vp, cr, 0, &cp);
1331 VN_RELE(entry_vp);
1332 if (error != 0)
1333 goto out;
1334
1335 ASSERT(cp != NULL);
1336 mutex_enter(&cp->c_statelock);
1337
1338 if ((cp->c_flags & CN_NOCACHE) ||
1339 (cp->c_metadata.md_flags & MD_ACL)) {
1340 mutex_exit(&cp->c_statelock);
1341 VN_RELE(CTOV(cp));
1342 continue;
1343 }
1344
1345 (void) cachefs_cacheacl(cp, NULL);
1346 mutex_exit(&cp->c_statelock);
1347 VN_RELE(CTOV(cp));
1348 }
1349
1350 /*
1351 * We must now re-lock the page corresponding to the frontvp,
1352 * and copy our staging buffer onto it.
1353 */
1354 if (error = fbread(frontvp, (offset_t)blockoff,
1355 MAXBSIZE, S_OTHER, &fbp))
1356 goto out;
1357
1358 bcopy(buf, fbp->fb_addr, MAXBSIZE);
1359 (void) fbdwrite(fbp);
1360 }
1361
1362 out:
1363 kmem_free(buf, MAXBSIZE);
1364 return (error);
1365 }
1366