cachefs_dir.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/pathname.h>
#include <sys/sysmacros.h>
#include <vm/faultcode.h>
/* forward declarations */
/*
* cachefs_dir_look() 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.
* Should only call this routine if the dir is populated.
* Returns ENOTDIR if dir gets nuked because of front file problems.
*/
int
{
int error;
int nmlen;
#ifdef CFSDEBUG
nm);
#endif
(void) cachefs_getfrontfile(dcp);
goto out;
}
if (error) {
goto out;
}
offset = 0;
error =
if (error)
goto out;
offset);
if (cookiep) {
cookiep);
}
if (flagp)
error = 0;
} else {
}
if (cidp)
if (d_offsetp)
goto out;
}
}
}
out:
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* creates a new directory and populates it with "." and ".."
*/
int
{
int error = 0;
int len;
#ifdef CFSDEBUG
(void *)cp);
#endif
if (error)
goto out;
}
#ifdef CFSDEBUG
if (error)
goto out;
#endif
/*
* Extend the directory by one MAXBSIZE chunk
*/
if (error != 0)
goto out;
if (error)
goto out;
/*
* Insert "." and ".."
*/
#ifdef INVALREADDIR
#else
#endif
out:
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* cachefs_dir_enter adds a new directory entry. Takes as input a fid,
* 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 !
*
* The new entry is inserted at the end, so that we can generate local offsets
* which are compatible with the backfs offsets (which are used when
* disconnected.
*/
int
int issync)
{
int offset;
int error = 0;
#ifdef CFSDEBUG
printf("c_dir_enter: ENTER dcp %p nm %s dirflg %x\n",
#endif
(void) cachefs_getfrontfile(dcp);
goto out;
}
/*
* Get the current EOF for the directory(data file)
*/
if (error) {
goto out;
}
/*
* Get the last block of the directory
*/
if (error)
goto out;
/*
* Find the last entry
*/
offset = 0;
for (;;) {
break;
}
/*
* It has room. If the entry is not valid, we can just use
* it. Otherwise, we need to adjust its length and offset
*/
#ifdef CFSDEBUG
printf("cachefs_dir_enter: looks like "
"we might fail the assert\n");
printf("addr %p, offset %x, "
"prev_offset %llx, dep->d_offset %llx\n",
offset = 0;
for (;;) {
printf("offset %x, prev_offset %llx\n",
printf("dep->d_offset %llx, "
"dep->d_length %x\n",
break;
}
}
}
#endif /* CFSDEBUG */
if (offset)
}
} else {
/*
* No room - so extend the file by one more
* MAXBSIZE chunk, and fit the entry there.
*/
if (error != 0)
goto out;
error =
if (error)
goto out;
}
/*
* Fill in the rest of the new entry
*/
if (cookiep) {
}
#ifdef INVALREADDIR
#endif
if (issync)
else
out:
#ifdef CFSDEBUG
#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
{
int offset = 0;
int nmlen;
#ifdef CFSDEBUG
printf("cachefs_dir_rmentry: ENTER dcp %p nm %s\n",
#endif
(void) cachefs_getfrontfile(dcp);
goto out;
}
if (error) {
goto out;
}
offset = 0;
error =
if (error)
goto out;
offset);
/*
* 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.
*/
} else
goto out;
}
}
}
out:
#ifdef CFSDEBUG
#endif
return (error);
}
#if 0
/*
* This function is used only in cachefs_lookup_back() routine but
* is inside #if 0 directive in this routine. So I am keeping this
* routine also in #if 0 directive.
*/
/*
* 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".
*/
int
{
int error = 0;
#ifdef CFSDEBUG
printf("cachefs_dir_modentry: ENTER dcp %p offset %lld\n",
#endif
(void) cachefs_getfrontfile(dcp);
return;
}
if (error)
goto out;
if (cookiep) {
}
if (cidp)
out:
#ifdef CFSDEBUG
printf("cachefs_dir_modentry: EXIT\n");
#endif
return (error);
}
#endif /* of #if 0 */
/*
* Called by cachefs_read_dir(). Gets a bunch if directory entries into buf and
* packs them into buf.
*/
static int
{
#define DIR_ENDOFF 0x7fffffffLL
int error;
#ifdef CFSDEBUG
printf("cachefs_dir_getentrys: "
"ENTER dcp %p beg_off %lld mdflags %x cflags %x\n",
#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;
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.
*/
error =
if (error)
goto out;
off = 0;
dep =
}
break;
}
*cntp = 0;
*last_offp = DIR_ENDOFF;
if (eofp)
*eofp = 1;
goto out;
}
/*
* Just load up the buffer with directory entries.
*/
for (;;) {
int this_reclen;
if (this_reclen > bufsize)
break;
/* use strncpy(9f) to zero out uninitialized bytes */
bufsize -= this_reclen;
}
/*
* Increment the offset. If we've hit EOF, fill in
* the lastoff and current entries d_off field.
*/
*last_offp = DIR_ENDOFF;
if (eofp)
*eofp = 1;
break;
}
/*
* If off == MAXBSIZE, then we need to adjust our
* window to the next MAXBSIZE block of the directory.
* Adjust blockoff, off and map it in. Also, increment
* the directory and buffer pointers.
*/
off = 0;
if (error)
goto out;
}
}
out:
/*
* Release any buffer and maping that may exist.
*/
if (fbp)
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* Called by cachefs_readdir(). Fills a directory request from the cache
*/
int
{
int error;
dirent64_t *de;
#ifdef CFSDEBUG
#endif
(MD_FILE|MD_POPULATED));
(void) cachefs_getfrontfile(dcp);
goto out;
}
if (fscp->fs_inum_size > 0) {
if (newinum == 0)
}
}
if (error == 0)
}
out:
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* Fully (including cookie) populates the directory from the back filesystem.
*/
int
{
int error = 0;
#ifdef CFSDEBUG
#endif
/* XXX for now return success if async populate is scheduled */
goto out;
/* get the back vp */
if (error) {
goto out;
}
}
/* get the front file vp */
(void) cachefs_getfrontfile(dcp);
goto out;
}
/* if dir was modified, toss old contents */
goto out;
}
}
if (error == 0)
if (error != 0)
goto out;
/*
* Mark the directory as not empty. Also bang the flag that says that
* this directory needs to be sync'ed on inactive.
*/
/*LINTED alignment okay*/
out:
if (error) {
#ifdef CFSDEBUG
printf("c_dir_fill: invalidating %llu\n",
#endif
}
return (error);
}
/*
* Does work of populating directory.
* Must be called while holding dcp->c_statelock
*/
static int
{
int error = 0;
int count;
int eof = 0;
#ifdef DEBUG
int loop_count = 0;
#endif
#ifdef CFSDEBUG
#endif
uio.uio_loffset = 0;
for (;;) {
#ifdef DEBUG
loop_count++;
#endif
/*
* Read in a buffer's worth of dirents and enter them in to the
* directory.
*/
if (error)
goto out;
/*LINTED alignment okay*/
if (count > 0) {
goto out;
}
break;
}
/* keep us from caching an empty directory */
goto out;
}
out:
*frontsize);
if (buf)
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* If the directory contains only the elements "." and "..", then this returns
* 0, otherwise returns an error.
*/
int
{
u_offset_t blockoff = 0;
int offset;
int error;
#ifdef CFSDEBUG
#endif
(void) cachefs_getfrontfile(dcp);
return (ENOTDIR);
if (error)
return (ENOTDIR);
offset = 0;
error =
if (error)
return (error);
offset);
return (0);
}
}
}
return (EEXIST);
}
/*
* Called by cachefs_dir_fill() to stuff a buffer of dir entries into
* a front file. This is more efficient than repeated calls to
* cachefs_dir_enter, and it also allows us to maintain entries in backfs
* order (readdir requires that entry offsets be ascending).
*/
static int
{
int error = 0;
/*LINTED alignment okay*/
/*LINTED want count != 0*/
if (error)
return (error);
}
if (error)
return (error);
if (*offsetp != 0) {
}
/*LINTED want count != 0*/
while (count > 0) {
if (last) {
}
if (last) {
}
if (error)
return (error);
if (error)
return (error);
off = 0;
}
}
if (last) {
}
return (error);
}
static int
{
int error = 0;
if (error)
return (error);
if (error) {
return (error);
}
if (incr_frontblks)
}
if (incr_frontblks)
return (0);
}
int
{
int error = 0;
#ifdef CFSDEBUG
printf("cachefs_async_populate_dir: ENTER dvp %p\n",
(void *)dvp);
#endif
/* if dir was modified, toss old contents */
goto out;
} else {
}
}
if (error != 0)
goto out;
/*
* I don't like to break lock here but cachefs_dir_complete()
* needs it.
*/
if (error != 0)
goto out;
/* if went nocache while lock was dropped, get out */
} else {
/* allocfile and allocblocks have already happened. */
}
out:
#ifdef CFSDEBUG
#endif
return (error);
}
static int
{
int offset;
int error = 0;
/*
* note: caller better not hold a c_statelock if acltoo is set.
*/
if (error)
goto out;
goto out;
/*
* We cannot hold any page locks across the below VOP
* operations. We thus copy the directory entries into a
* staging buffer, and release the page lock on the directory
* by calling fbrelse(). Once any necessary cnodes have
* been created, we'll reacquire the page lock with fbread()
* and copy the staging buffer back into the frontvp at
* blockoff.
*/
for (offset = 0;
continue;
if (error) {
/* lookup on .. in / on coc gets ENOENT */
error = 0;
continue;
}
goto out;
}
TRUE);
if (error) {
#ifdef CFSDEBUG
printf("\t%s: getcookie error\n",
#endif /* CFSDEBUG */
goto out;
}
if ((! acltoo) ||
(! cachefs_vtype_aclok(entry_vp)) ||
continue;
}
if (error != 0)
goto out;
continue;
}
}
/*
* We must now re-lock the page corresponding to the frontvp,
* and copy our staging buffer onto it.
*/
goto out;
}
out:
return (error);
}