/* * Copyright (c) 1992, Sun Microsystems, Inc. * All rights reserved. */ #pragma ident "@(#)cachefs_dir.c 1.48 94/08/22 SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern struct seg *segkmap; caddr_t segmap_getmap(); int segmap_release(); /* forward declarations */ static int cachefs_getdents(struct cnode *, u_int, u_int *, u_int *, u_int, caddr_t, int *, cred_t *); static int cachefs_stuffdir(struct cnode *, u_int, caddr_t, u_int *, u_int *, cred_t *); static int cachefs_extenddir(struct cnode *, u_int *, cred_t *); /* * cachefs_dirlook() called mainly by lookup (and create), looks up the cached * directory for an entry and returns the information there. If the directory * entry doesn't exist return ENOENT, if it is incomplete, return EINVAL. */ int cachefs_dirlook(struct cnode *dcp, char *nm, struct fid *cookiep, u_int *flagp, u_int *d_offsetp, ino_t *file_nop, cred_t *cr) { int error; struct vattr va; int blockoff = 0; int offset = 0; caddr_t addr; vnode_t *dvp = dcp->c_frontvp; struct fscache *fscp = C_TO_FSCACHE(dcp); cachefscache_t *cachep = fscp->fs_cache; int nmlen; #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_dirlook: ENTER dcp %x nm %s\n", (int) dcp, nm); #endif ASSERT(CTOV(dcp)->v_type == VDIR); ASSERT(dcp->c_frontvp != NULL); ASSERT((dcp->c_flags & CN_NOCACHE) == 0); dvp = dcp->c_frontvp; va.va_mask = AT_SIZE; /* XXX should save dir size */ error = VOP_GETATTR(dvp, &va, 0, cr); if (error) goto out; nmlen = strlen(nm); while (blockoff < va.va_size) { offset = 0; addr = segmap_getmap(segkmap, dvp, (u_int)blockoff); while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) { struct c_dirent *dep; /*LINTED alignment okay*/ dep = (struct c_dirent *)(addr + offset); if ((dep->d_flag & CDE_VALID) && (nmlen == dep->d_namelen) && strcmp(dep->d_name, nm) == 0) { if (dep->d_flag & CDE_COMPLETE) { if (cookiep) *cookiep = dep->d_cookie; if (flagp) *flagp = dep->d_flag; error = 0; } else { error = EINVAL; } if (file_nop) *file_nop = dep->d_fileno; if (d_offsetp) *d_offsetp = offset + blockoff; (void) segmap_release(segkmap, addr, 0); goto out; } ASSERT(dep->d_length != 0); offset += dep->d_length; } (void) segmap_release(segkmap, addr, 0); addr = NULL; blockoff += MAXBSIZE; } error = ENOENT; out: if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR)) cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp, &dcp->c_metadata.md_cookie, dcp->c_fileno, cr->cr_uid); #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("c_dirlook: EXIT error = %d\n", error); #endif return (error); } /* * cachefs_direnter adds a new directory entry. Takes as input a fid, flags * fileno and a sync flag. Most of the time, the caller is content with the * write to the (front) directory being done async. The exception being - for * local files, we should make sure that the directory entry is made * synchronously. That is notified by the caller. * issync == 0 || issync == SM_ASYNC ! * Given an entry to enter y, we search thru the existing entries in the * directory, till we find an entry x such that * d_length(x) - DIRSIZ(x) >= CDE_SIZE(y). * Once we find this entry, we make * d_length(y) = d_length(x) - DIRSIZ(x) and * d_length(x) = DIRSIZ(x). * If we didn't find any entry x satisfying the space requirement, we allocate * a new block at the end of the directory. */ int cachefs_direnter(struct cnode *dcp, char *nm, struct fid *cookiep, u_int flag, ino_t fileno, off_t doffset, cred_t *cr, int issync) { struct vattr va; int offset, blockoff = 0; int error = 0; vnode_t *dvp = dcp->c_frontvp; struct c_dirent *dep; caddr_t addr; u_int esize = CDE_SIZE(nm); /* Size of the entry to be added */ u_char newblk = 0; u_int dirsize; #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("c_direnter: ENTER dcp %x nm %s dirflg %x\n", (int) dcp, nm, dcp->c_metadata.md_flags); #endif ASSERT(RW_WRITE_HELD(&dcp->c_statelock)); ASSERT(CTOV(dcp)->v_type == VDIR); ASSERT((dcp->c_flags & CN_NOCACHE) == 0); ASSERT(dcp->c_frontvp != NULL); ASSERT(issync == 0 || issync == SM_ASYNC); ASSERT(esize <= (sizeof (struct c_dirent) + MAXNAMELEN)); /* * Get the current EOF for the directory(data file) */ va.va_mask = AT_SIZE; error = VOP_GETATTR(dcp->c_frontvp, &va, 0, cr); if (error) goto out; /* ASSERT((va.va_size & (MAXBSIZE - 1)) == 0); */ while (blockoff < va.va_size) { offset = 0; addr = segmap_getmap(segkmap, dvp, (u_int)blockoff); while ((offset < MAXBSIZE) && ((blockoff + offset) < va.va_size)) { /*LINTED alignment okay*/ dep = (struct c_dirent *)(addr + offset); if (dep->d_length - C_DIRSIZ(dep) >= esize) { /* * Found an entry that we can slice off and * fit our new entry in ! * If the first entry in the block was not a * valid entry, things are easy, otherwise... */ if (offset != 0 || (dep->d_flag & CDE_VALID)) { u_int new_len = dep->d_length - C_DIRSIZ(dep); dep->d_length = C_DIRSIZ(dep); dep = (struct c_dirent *) /*LINTED alignment okay*/ ((caddr_t)dep + C_DIRSIZ(dep)); dep->d_length = new_len; } goto haveslot; } offset += dep->d_length; } (void) segmap_release(segkmap, addr, 0); blockoff += MAXBSIZE; } /* * If we got here, we didn't find any space in the already allocated * entries to fit this one in, so we extend the file by one more * MAXBSIZE chunk, and fit the entry there. */ dirsize = va.va_size; error = cachefs_extenddir(dcp, &dirsize, cr); if (error != 0) goto out; addr = segmap_getmap(segkmap, dvp, (u_int)va.va_size); /*LINTED alignment okay*/ dep = (struct c_dirent *)addr; dep->d_length = MAXBSIZE; haveslot: dep->d_flag = CDE_VALID | flag; if (cookiep) { dep->d_flag |= CDE_COMPLETE; dep->d_cookie = *cookiep; } dep->d_fileno = fileno; dep->d_namelen = strlen(nm); ASSERT(dep->d_namelen <= MAXNAMELEN); (void) bcopy((caddr_t)nm, dep->d_name, dep->d_namelen + 1); if (doffset == -1) { /* * here we were called by a directory op when * in non-strict consistency mode. We don't know * the correct backfs dir offset for this entry, * so we set the cnode flag to indicate this * fact to subsequent readdirs */ dep->d_offset = -1; dcp->c_metadata.md_flags |= MD_INVALREADDIR; } else { dep->d_offset = doffset; } dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC; /* * If this is the first entry in this (newly allocated) MAXBSIZE block, * we fill in the d_length here. In other cases, it is already filled in */ if (newblk) dep->d_length = MAXBSIZE; (void) segmap_release(segkmap, addr, SM_WRITE | issync); out: #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_direnter: EXIT error = %d\n", error); #endif return (error); } /* * Quite simple, if the deleted entry is the first in the MAXBSIZE block, * we simply mark it invalid. Otherwise, the deleted entries d_length is * just added to the previous entry. */ int cachefs_rmdirent(struct cnode *dcp, char *nm, cred_t *cr) { int blockoff = 0; int offset = 0; struct vattr va; int error = ENOENT; vnode_t *dvp = dcp->c_frontvp; int nmlen; #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_rmdirent: ENTER dcp %x nm %s\n", (int) dcp, nm); #endif if ((dcp->c_metadata.md_flags & MD_FILE) == 0) ASSERT((dcp->c_metadata.md_flags & MD_POPULATED) == 0); if (((dcp->c_metadata.md_flags & MD_POPULATED) == 0) || dvp == NULL) { error = 0; goto out; } ASSERT(CTOV(dcp)->v_type == VDIR); ASSERT((dcp->c_flags & CN_NOCACHE) == 0); ASSERT(dvp != NULL); va.va_mask = AT_SIZE; error = VOP_GETATTR(dvp, &va, 0, cr); if (error) goto out; nmlen = strlen(nm); while (blockoff < va.va_size) { caddr_t addr; u_int *last_len; offset = 0; last_len = NULL; addr = segmap_getmap(segkmap, dvp, (u_int)blockoff); while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) { struct c_dirent *dep; /*LINTED alignment okay*/ dep = (struct c_dirent *)(addr + offset); if ((dep->d_flag & CDE_VALID) && (nmlen == dep->d_namelen) && strcmp(dep->d_name, nm) == 0) { /* * Found the entry. If this was the first entry * in the MAXBSIZE block, Mark it invalid. Else * add it's length to the previous entry's * length. */ if (last_len == NULL) { ASSERT(offset == 0); dep->d_flag = 0; } else *last_len += dep->d_length; (void) segmap_release(segkmap, addr, SM_ASYNC | SM_WRITE); dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC; goto out; } last_len = &dep->d_length; offset += dep->d_length; } (void) segmap_release(segkmap, addr, 0); blockoff += MAXBSIZE; } error = ENOENT; out: #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_rmdirent: EXIT error = %d\n", error); #endif return (error); } /* * This function fills in the cookie and file no of the directory entry * at the offset specified by offset - In other words, makes the entry * "complete". */ void cachefs_dirent_mod(struct cnode *dcp, u_int offset, struct fid *cookiep, ino_t *file_nop) { struct c_dirent *dep; u_int blockoff = (offset & MAXBMASK); u_int off = (offset & MAXBOFFSET); caddr_t addr; vnode_t *dvp = dcp->c_frontvp; #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_dirent_mod: ENTER dcp %x offset %d\n", (int) dcp, offset); #endif ASSERT(CTOV(dcp)->v_type == VDIR); ASSERT((dcp->c_flags & CN_NOCACHE) == 0); ASSERT(dcp->c_frontvp != NULL); addr = segmap_getmap(segkmap, dvp, (u_int)blockoff); /*LINTED alignment okay*/ dep = (struct c_dirent *)(addr + off); if (cookiep) { dep->d_flag |= CDE_COMPLETE; dep->d_cookie = *cookiep; } if (file_nop != NULL) dep->d_fileno = *file_nop; (void) segmap_release(segkmap, addr, SM_ASYNC | SM_WRITE); dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC; #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_dirent_mod: EXIT\n"); #endif } /* * Called by cachefs_read_dir(). Gets a bunch if durectory entries into buf and * packs them into buf. */ static int cachefs_getdents(struct cnode *dcp, u_int beg_off, u_int *last_offp, u_int *cntp, u_int bufsize, caddr_t buf, int *eofp, cred_t *cr) { #define DIR_ENDOFF 0x7fffffff struct vattr va; struct c_dirent *dep; caddr_t addr = NULL; struct dirent *gdp; u_int blockoff; u_int off; int error; vnode_t *dvp = dcp->c_frontvp; #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf( "cachefs_getdents: ENTER dcp %x beg_off %d mdflags %x cflags %x\n", (int) dcp, beg_off, dcp->c_metadata.md_flags, dcp->c_flags); #endif /* * blockoff has the offset of the MAXBSIZE block that contains the * entry to start with. off contains the offset relative to the * begining of the MAXBSIZE block. */ if (eofp) *eofp = 0; /*LINTED alignment okay*/ gdp = (struct dirent *)buf; *cntp = bufsize; va.va_mask = AT_SIZE; error = VOP_GETATTR(dvp, &va, 0, cr); if (error) { *cntp = 0; *last_offp = 0; if (eofp) *eofp = 1; goto out; } if (beg_off == DIR_ENDOFF) { *cntp = 0; *last_offp = DIR_ENDOFF; if (eofp) *eofp = 1; goto out; } /* * locate the offset where we start reading. */ blockoff = off = 0; while (blockoff < va.va_size) { addr = segmap_getmap(segkmap, dvp, (u_int)blockoff); /*LINTED alignment okay*/ dep = (struct c_dirent *)addr; off = 0; while (off < MAXBSIZE && dep->d_offset <= beg_off) { off += dep->d_length; /*LINTED alignment okay*/ dep = (struct c_dirent *)(addr + off); } if (off < MAXBSIZE) { break; } (void) segmap_release(segkmap, addr, 0); addr = NULL; blockoff += MAXBSIZE; } if (blockoff >= va.va_size) { *cntp = 0; *last_offp = DIR_ENDOFF; if (eofp) *eofp = 1; goto out; } /* * Just load up the buffer with directory entries. */ for (;;) { u_int size; ASSERT((caddr_t)dep < (addr + MAXBSIZE)); if (dep->d_flag & CDE_VALID) { size = ((sizeof (struct dirent) - 1 + (dep->d_namelen + 1)) + 3) & ~3; ASSERT(size < MAXBSIZE); if (size > bufsize) break; gdp->d_reclen = size; ASSERT(dep->d_namelen <= MAXNAMELEN); ASSERT(dep->d_offset > (*last_offp)); gdp->d_ino = dep->d_fileno; gdp->d_off = dep->d_offset; bcopy((caddr_t)dep->d_name, (caddr_t)gdp->d_name, dep->d_namelen + 1); bufsize -= size; /*LINTED alignment okay*/ gdp = (struct dirent *)((caddr_t)gdp + gdp->d_reclen); *last_offp = dep->d_offset; } /* * Increment the offset. If we've hit EOF, fill in * the lastoff and current entries d_off field. */ off += dep->d_length; ASSERT(off <= MAXBSIZE); if ((blockoff + off) >= va.va_size) { *last_offp = DIR_ENDOFF; if (eofp) *eofp = 1; break; } /* * If off == MAXBSIZE, then we need to adjust or * window to the next MAXBSIZE block of the directory. * Adjust blockoff, off and map it in. Also, increment * the directory and buffer pointers. */ if (off == MAXBSIZE) { (void) segmap_release(segkmap, addr, 0); off = 0; blockoff += MAXBSIZE; addr = segmap_getmap(segkmap, dvp, (u_int)blockoff); } /*LINTED alignment okay*/ dep = (struct c_dirent *)(addr + off); } *cntp -= bufsize; out: /* * Release any maping that may exist. */ if (addr) (void) segmap_release(segkmap, addr, 0); #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("ccachefs_getdents: EXIT error = %d\n", error); #endif return (error); } /* * Called by cachefs_readdir(). Fills up directories from the backFS. */ int cachefs_read_dir(struct cnode *dcp, struct uio *uiop, int *eofp, cred_t *cr) { int error; u_int count; u_int size; caddr_t buf; u_int next = uiop->uio_offset; struct fscache *fscp = C_TO_FSCACHE(dcp); cachefscache_t *cachep = fscp->fs_cache; ASSERT(CTOV(dcp)->v_type == VDIR); ASSERT((dcp->c_flags & CN_NOCACHE) == 0); ASSERT(RW_READ_HELD(&dcp->c_rwlock)); #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_read_dir: ENTER dcp %x\n", (int) dcp); #endif ASSERT((dcp->c_metadata.md_flags & (MD_FILE|MD_POPULATED)) == (MD_FILE|MD_POPULATED)); size = uiop->uio_resid; buf = (caddr_t) cachefs_kmem_alloc(size, KM_SLEEP); error = cachefs_getdents(dcp, next, &next, &count, size, buf, eofp, cr); /*LINTED want count != 0*/ if (error == 0 && count > 0) { ASSERT(count <= size); error = uiomove(buf, (int)count, UIO_READ, uiop); if (error == 0) uiop->uio_offset = next; } out: (void) cachefs_kmem_free(buf, (u_int)size); if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR)) cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp, &dcp->c_metadata.md_cookie, dcp->c_fileno, cr->cr_uid); #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_read_dir: EXIT error = %d\n", error); #endif return (error); } /* * Populates the directory from the back filesystem. */ int cachefs_filldir(struct cnode *dcp, int type, cred_t *cr) { int error = 0; struct uio uio; struct iovec iov; caddr_t buf = NULL; int count; int eof = 0; u_int frontoff, frontsize; struct fscache *fscp = C_TO_FSCACHE(dcp); cachefscache_t *cachep = fscp->fs_cache; #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_filldir: ENTER dcp %x\n", (int) dcp); #endif ASSERT((dcp->c_metadata.md_flags & MD_POPULATED) == 0); ASSERT(dcp->c_metadata.md_flags & MD_FILE); ASSERT(dcp->c_frontvp != NULL); ASSERT(dcp->c_backvp != NULL); if (type != RW_WRITER) { if (rw_tryupgrade(&dcp->c_statelock) == 0) { error = EAGAIN; goto out1; } } frontoff = frontsize = 0; buf = (caddr_t)cachefs_kmem_alloc(MAXBSIZE, KM_SLEEP); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_fmode = 0; uio.uio_offset = 0; for (;;) { /* * Read in a buffer's worth of dirents and enter them in to the * directory. */ uio.uio_resid = MAXBSIZE; iov.iov_base = buf; iov.iov_len = MAXBSIZE; VOP_RWLOCK(dcp->c_backvp, 0); error = VOP_READDIR(dcp->c_backvp, &uio, cr, &eof); VOP_RWUNLOCK(dcp->c_backvp, 0); if (error) goto out; count = MAXBSIZE - uio.uio_resid; ASSERT(count >= 0); if (count > 0) if (error = cachefs_stuffdir(dcp, count, buf, &frontoff, &frontsize, cr)) goto out; if (eof || count == 0) break; } /* * Mark the directory as not empty. Also bang the flag that says that * this directory needs to be sync'ed on inactive. */ dcp->c_metadata.md_flags |= MD_POPULATED; dcp->c_metadata.md_flags &= ~MD_INVALREADDIR; dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC; out: if (error && error != EAGAIN) { cachefs_inval_object(dcp, cr); } if (type == RW_READER) rw_downgrade(&dcp->c_statelock); if (buf) cachefs_kmem_free(buf, (u_int)MAXBSIZE); out1: if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_FILLDIR)) cachefs_log_filldir(cachep, error, fscp->fs_cfsvfsp, &dcp->c_metadata.md_cookie, dcp->c_fileno, frontsize); #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_filldir: EXIT error = %d\n", error); #endif return (error); } /* * If the directory contains only the elements "." and "..", then this returns * TRUE (= 1) else, this returns FALSE (= 0). Used br cachefs_rmdir() */ cachefs_dirempty(struct cnode *dcp, cred_t *cr) { struct vattr va; int blockoff = 0; int offset; caddr_t addr; int error; vnode_t *dvp = dcp->c_frontvp; #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DIR) printf("cachefs_dirempty: ENTER dcp %x\n", (int) dcp); #endif ASSERT(CTOV(dcp)->v_type == VDIR); ASSERT((dcp->c_flags & CN_NOCACHE) == 0); ASSERT(dcp->c_frontvp != NULL); va.va_mask = AT_SIZE; error = VOP_GETATTR(dvp, &va, 0, cr); if (error) return (0); while (blockoff < va.va_size) { offset = 0; addr = segmap_getmap(segkmap, dvp, (u_int)blockoff); while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) { struct c_dirent *dep; /*LINTED alignment okay*/ dep = (struct c_dirent *)(addr + offset); if ((dep->d_flag & CDE_VALID) && ((strcmp(dep->d_name, ".") != 0) && (strcmp(dep->d_name, "..") != 0))) { (void) segmap_release(segkmap, addr, 0); return (0); } offset += dep->d_length; } (void) segmap_release(segkmap, addr, 0); addr = NULL; blockoff += MAXBSIZE; } return (1); } /* * Called by cachefs_filldir() to stuff a buffer of dir entries into * a front file. This is more efficient than repeated calls to * cachefs_direnter, and it also allows us to maintain entries in backfs * order (readdir requires that entry offsets be ascending). */ static int cachefs_stuffdir(struct cnode *dcp, u_int count, caddr_t buf, u_int *offsetp, u_int *fsizep, cred_t *cr) { int error; caddr_t addr; struct c_dirent *cdep, *last; struct dirent *dep; int inblk, entsize; u_int blockoff = (*offsetp & MAXBMASK); u_int off = (*offsetp & MAXBOFFSET); ASSERT(RW_WRITE_HELD(&dcp->c_statelock)); /*LINTED want count != 0*/ ASSERT(count > 0); if (*offsetp >= *fsizep) { error = cachefs_extenddir(dcp, fsizep, cr); if (error) return (error); } last = NULL; addr = segmap_getmap(segkmap, dcp->c_frontvp, blockoff); /*LINTED alignment okay*/ cdep = (struct c_dirent *)(addr+off); inblk = MAXBSIZE-off; if (*offsetp != 0) { ASSERT(cdep->d_length == inblk); inblk -= C_DIRSIZ(cdep); last = cdep; last->d_length -= inblk; off += last->d_length; /*LINTED alignment okay*/ cdep = (struct c_dirent *)(addr+off); } /*LINTED alignment okay*/ dep = (struct dirent *)buf; /*LINTED want count != 0*/ while (count > 0) { if (last) { ASSERT(dep->d_off > last->d_offset); } entsize = CDE_SIZE(dep->d_name); if (entsize > inblk) { segmap_release(segkmap, addr, SM_WRITE); error = cachefs_extenddir(dcp, fsizep, cr); if (error) return (error); if (last) { last->d_length += inblk; } blockoff += MAXBSIZE; addr = segmap_getmap(segkmap, dcp->c_frontvp, blockoff); off = 0; /*LINTED alignment okay*/ cdep = (struct c_dirent *)addr; inblk = MAXBSIZE; last = NULL; } cdep->d_length = entsize; cdep->d_fileno = dep->d_ino; cdep->d_namelen = strlen(dep->d_name); cdep->d_flag = CDE_VALID; (void) bcopy((caddr_t)dep->d_name, (caddr_t)cdep->d_name, cdep->d_namelen+1); cdep->d_offset = dep->d_off; inblk -= entsize; count -= dep->d_reclen; /*LINTED alignment okay*/ dep = (struct dirent *)(((caddr_t)dep) + dep->d_reclen); *offsetp = blockoff + off; off += entsize; last = cdep; /*LINTED alignment okay*/ cdep = (struct c_dirent *)(addr + off); } if (last) { last->d_length += inblk; } segmap_release(segkmap, addr, SM_WRITE); return (0); } static int cachefs_extenddir(struct cnode *dcp, u_int *cursize, cred_t *cr) { struct vattr va; cachefscache_t *cachep = C_TO_FSCACHE(dcp)->fs_cache; int error = 0; ASSERT(RW_WRITE_HELD(&dcp->c_statelock)); ASSERT(((*cursize) & (MAXBSIZE-1)) == 0); va.va_mask = AT_SIZE; va.va_size = *cursize + MAXBSIZE; error = cachefs_allocblocks(cachep, 1, cr); if (error) return (error); error = VOP_SETATTR(dcp->c_frontvp, &va, 0, cr); if (error) { cachefs_freeblocks(cachep, 1); return (error); } (dcp->c_metadata.md_frontblks)++; *cursize += MAXBSIZE; dcp->c_flags |= CN_UPDATED; return (0); }