Implement directory hashing to speed up directory traversals. Speed

improvements of at least 4 times in untarring and roughly 100 to 500 times
on file creation in big directories. Lookup of files was O(n*n) and is now
O(1) even for file creation. Free spaces in the directory are kept in a
seperate list for fast file creation.

The postmark benchmark gives:

UDF old:
pm>set transactions 2000
pm>set number 3000
pm>run
Creating files...Done
Performing transactions..........Done
Deleting files...Done
Time:
        1593 seconds total
        681 seconds of transactions (2 per second)

Files:
        3956 created (2 per second)
                Creation alone: 3000 files (4 per second)
                Mixed with transactions: 956 files (1 per second)
        990 read (1 per second)
        1010 appended (1 per second)
        3956 deleted (2 per second)
                Deletion alone: 2912 files (9 per second)
                Mixed with transactions: 1044 files (1 per second)

Data:
        5.26 megabytes read (3.38 kilobytes per second)
        21.93 megabytes written (14.10 kilobytes per second)
pm>



UDF new:
pm>set transactions 2000
pm>set number 3000
pm>run
Creating files...Done
Performing transactions..........Done
Deleting files...Done
Time:
        19 seconds total
        3 seconds of transactions (666 per second)

Files:
        3956 created (208 per second)
                Creation alone: 3000 files (230 per second)
                Mixed with transactions: 956 files (318 per second)
        990 read (330 per second)
        1010 appended (336 per second)
        3956 deleted (208 per second)
                Deletion alone: 2912 files (970 per second)
                Mixed with transactions: 1044 files (348 per second)

Data:
        5.26 megabytes read (283.66 kilobytes per second)
        21.93 megabytes written (1.15 megabytes per second)
This commit is contained in:
reinoud 2008-07-17 19:10:22 +00:00
parent f2e25e0cba
commit 2ac28d554b
5 changed files with 597 additions and 226 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: udf.h,v 1.17 2008/07/07 18:45:26 reinoud Exp $ */
/* $NetBSD: udf.h,v 1.18 2008/07/17 19:10:22 reinoud Exp $ */
/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
@ -64,13 +64,14 @@ extern int udf_verbose;
#define UDF_DEBUG_EXTATTR 0x002000
#define UDF_DEBUG_ALLOC 0x004000
#define UDF_DEBUG_ADWLK 0x008000
#define UDF_DEBUG_PARANOIDADWLK 0x010000
#define UDF_DEBUG_DIRHASH 0x010000
#define UDF_DEBUG_NOTIMPL 0x020000
#define UDF_DEBUG_SHEDULE 0x040000
#define UDF_DEBUG_ECCLINE 0x080000
#define UDF_DEBUG_SYNC 0x100000
#define UDF_DEBUG_PARANOIA 0x200000
#define UDF_DEBUG_NODEDUMP 0x400000
#define UDF_DEBUG_PARANOIDADWLK 0x400000
#define UDF_DEBUG_NODEDUMP 0x800000
/* initial value of udf_verbose */
#define UDF_DEBUGGING 0
@ -115,6 +116,10 @@ extern int udf_verbose;
#define UDF_ECCBUF_HASHSIZE (1<<UDF_ECCBUF_HASHBITS)
#define UDF_ECCBUF_HASHMASK (UDF_ECCBUF_HASHSIZE -1)
#define UDF_DIRHASH_HASHBITS 5
#define UDF_DIRHASH_HASHSIZE (1<<UDF_DIRHASH_HASHBITS)
#define UDF_DIRHASH_HASHMASK (UDF_DIRHASH_HASHSIZE -1)
#define UDF_ECCLINE_MAXFREE 10 /* picked */
#define UDF_ECCLINE_MAXBUSY 100 /* picked */
@ -200,6 +205,8 @@ MALLOC_DECLARE(M_UDFVOLD);
MALLOC_DECLARE(M_UDFTEMP);
struct pool udf_node_pool;
struct pool udf_dirhash_pool;
struct pool udf_dirhash_entry_pool;
struct udf_node;
struct udf_strategy;
@ -343,6 +350,23 @@ struct udf_mount {
void *strategy_private;
};
/* dirent's d_namlen is to avoid useless costly fid->dirent translations */
struct udf_dirhash_entry {
uint32_t hashvalue;
uint64_t offset;
uint32_t d_namlen;
uint32_t fid_size;
LIST_ENTRY(udf_dirhash_entry) next;
};
struct udf_dirhash {
LIST_HEAD(, udf_dirhash_entry) entries[UDF_DIRHASH_HASHSIZE];
LIST_HEAD(, udf_dirhash_entry) free_entries;
};
/*
* UDF node describing a file/directory.
*
@ -370,7 +394,7 @@ struct udf_node {
int needs_indirect; /* has missing indr. */
struct long_ad ext_loc[UDF_MAX_ALLOC_EXTENTS];
uint64_t last_diroffset; /* speeding up lookup*/
struct udf_dirhash *dir_hash;
/* misc */
uint32_t i_flags; /* associated flags */
@ -389,23 +413,26 @@ struct udf_node {
/* misc. flags stored in i_flags (XXX needs cleaning up) */
#define IN_ACCESS 0x0001 /* Inode access time update request */
#define IN_CHANGE 0x0002 /* Inode change time update request */
#define IN_UPDATE 0x0004 /* Inode was written to; update mtime*/
#define IN_MODIFY 0x0008 /* Modification time update request */
#define IN_MODIFIED 0x0010 /* node has been modified */
#define IN_ACCESSED 0x0020 /* node has been accessed */
#define IN_RENAME 0x0040 /* node is being renamed. XXX ?? */
#define IN_DELETED 0x0080 /* node is unlinked, no FID reference*/
#define IN_LOCKED 0x0100 /* node is locked by condvar */
#define IN_SYNCED 0x0200 /* node is being used by sync */
#define IN_CALLBACK_ULK 0x0400 /* node will be unlocked by callback */
#define IN_NODE_REBUILD 0x0800 /* node is rebuild */
#define IN_ACCESS 0x0001 /* Inode access time update request */
#define IN_CHANGE 0x0002 /* Inode change time update request */
#define IN_UPDATE 0x0004 /* Inode was written to; update mtime*/
#define IN_MODIFY 0x0008 /* Modification time update request */
#define IN_MODIFIED 0x0010 /* node has been modified */
#define IN_ACCESSED 0x0020 /* node has been accessed */
#define IN_RENAME 0x0040 /* node is being renamed. XXX ?? */
#define IN_DELETED 0x0080 /* node is unlinked, no FID reference*/
#define IN_LOCKED 0x0100 /* node is locked by condvar */
#define IN_SYNCED 0x0200 /* node is being used by sync */
#define IN_CALLBACK_ULK 0x0400 /* node will be unlocked by callback */
#define IN_NODE_REBUILD 0x0800 /* node is rebuild */
#define IN_DIRHASH_COMPLETE 0x1000 /* dirhash is complete */
#define IN_DIRHASH_BROKEN 0x2000 /* dirhash is broken on readin */
#define IN_FLAGBITS \
"\10\1IN_ACCESS\2IN_CHANGE\3IN_UPDATE\4IN_MODIFY\5IN_MODIFIED" \
"\6IN_ACCESSED\7IN_RENAME\10IN_DELETED\11IN_LOCKED\12IN_SYNCED" \
"\13IN_CALLBACK_ULK\14IN_NODE_REBUILD"
"\13IN_CALLBACK_ULK\14IN_NODE_REBUILD\15IN_DIRHASH_COMPLETE" \
"\16IN_DIRHASH_BROKEN"
#endif /* !_FS_UDF_UDF_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: udf_subr.c,v 1.58 2008/07/17 15:37:59 reinoud Exp $ */
/* $NetBSD: udf_subr.c,v 1.59 2008/07/17 19:10:22 reinoud Exp $ */
/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
@ -29,7 +29,7 @@
#include <sys/cdefs.h>
#ifndef lint
__KERNEL_RCSID(0, "$NetBSD: udf_subr.c,v 1.58 2008/07/17 15:37:59 reinoud Exp $");
__KERNEL_RCSID(0, "$NetBSD: udf_subr.c,v 1.59 2008/07/17 19:10:22 reinoud Exp $");
#endif /* not lint */
@ -3851,25 +3851,307 @@ udf_setownership(struct udf_node *udf_node, uid_t uid, gid_t gid)
/* --------------------------------------------------------------------- */
/*
* Directory read and manipulation functions.
*
* Note that if the file is found, the cached diroffset position *before* the
* advance is remembered. Thus if the same filename is lookup again just after
* this lookup its immediately found.
* UDF dirhash implementation
*/
int
udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen,
struct long_ad *icb_loc)
static uint32_t
udf_dirhash_hash(const char *str, int namelen)
{
struct udf_node *dir_node = VTOI(vp);
uint32_t hash = 5381;
int i, c;
for (i = 0; i < namelen; i++) {
c = *str++;
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
return hash;
}
static void
udf_dirhash_init(struct udf_node *dir_node)
{
struct udf_dirhash *dirh;
uint32_t hashline;
if (dir_node->dir_hash == NULL) {
dirh = pool_get(&udf_dirhash_pool, PR_WAITOK);
dir_node->dir_hash = dirh;
memset(dirh, 0, sizeof(struct udf_dirhash));
for (hashline = 0; hashline < UDF_DIRHASH_HASHSIZE; hashline++)
LIST_INIT(&dirh->entries[hashline]);
}
}
static void
udf_dirhash_destroy(struct udf_node *dir_node)
{
struct udf_dirhash *dirh;
struct udf_dirhash_entry *dirh_e;
uint32_t hashline;
if (dir_node->dir_hash == NULL)
return;
dirh = dir_node->dir_hash;
for (hashline = 0; hashline < UDF_DIRHASH_HASHSIZE; hashline++) {
dirh_e = LIST_FIRST(&dirh->entries[hashline]);
while (dirh_e) {
LIST_REMOVE(dirh_e, next);
pool_put(&udf_dirhash_entry_pool, dirh_e);
dirh_e = LIST_FIRST(&dirh->entries[hashline]);
}
}
pool_put(&udf_dirhash_pool, dirh);
dir_node->dir_hash = NULL;
}
static void
udf_dirhash_enter(struct udf_node *dir_node, struct fileid_desc *fid,
struct dirent *dirent, uint64_t offset, uint32_t fid_size, int new)
{
struct udf_dirhash *dirh;
struct udf_dirhash_entry *dirh_e;
uint32_t hashvalue, hashline;
/* make sure we have a dirhash to add to; might be a fresh dir */
udf_dirhash_init(dir_node);
dirh = dir_node->dir_hash;
/* finished build? */
if (!new && (dir_node->i_flags & IN_DIRHASH_COMPLETE))
return;
/* calculate our hash */
hashvalue = udf_dirhash_hash(dirent->d_name, dirent->d_namlen);
hashline = hashvalue & UDF_DIRHASH_HASHMASK;
/* lookup and insert entry if not there yet */
LIST_FOREACH(dirh_e, &dirh->entries[hashline], next) {
/* check for hash collision */
if (dirh_e->hashvalue != hashvalue)
continue;
if (dirh_e->offset != offset)
continue;
/* got it already */
KASSERT(dirh_e->d_namlen == dirent->d_namlen);
KASSERT(dirh_e->fid_size == fid_size);
return;
}
DPRINTF(DIRHASH, ("dirhash enter %"PRIu64", %d, %d for `%*.*s`\n",
offset, fid_size, dirent->d_namlen,
dirent->d_namlen, dirent->d_namlen, dirent->d_name));
/* check if entry is in free space list */
LIST_FOREACH(dirh_e, &dirh->free_entries, next) {
if (dirh_e->offset == offset) {
DPRINTF(DIRHASH, ("\tremoving free entry\n"));
LIST_REMOVE(dirh_e, next);
break;
}
}
/* add to the hashline */
dirh_e = pool_get(&udf_dirhash_entry_pool, PR_WAITOK);
memset(dirh_e, 0, sizeof(struct udf_dirhash_entry));
dirh_e->hashvalue = hashvalue;
dirh_e->offset = offset;
dirh_e->d_namlen = dirent->d_namlen;
dirh_e->fid_size = fid_size;
LIST_INSERT_HEAD(&dirh->entries[hashline], dirh_e, next);
}
static void
udf_dirhash_enter_freed(struct udf_node *dir_node, uint64_t offset,
uint32_t fid_size)
{
struct udf_dirhash *dirh;
struct udf_dirhash_entry *dirh_e;
/* make sure we have a dirhash to add to; might be a fresh dir */
udf_dirhash_init(dir_node);
dirh = dir_node->dir_hash;
KASSERT(dirh);
#ifdef DEBUG
/* check for double entry of free space */
LIST_FOREACH(dirh_e, &dirh->free_entries, next)
KASSERT(dirh_e->offset != offset);
#endif
DPRINTF(DIRHASH, ("dirhash enter FREED %"PRIu64", %d\n",
offset, fid_size));
dirh_e = pool_get(&udf_dirhash_entry_pool, PR_WAITOK);
memset(dirh_e, 0, sizeof(struct udf_dirhash_entry));
dirh_e->hashvalue = 0; /* not relevant */
dirh_e->offset = offset;
dirh_e->d_namlen = 0; /* not relevant */
dirh_e->fid_size = fid_size;
/* XXX it might be preferable to append them at the tail */
LIST_INSERT_HEAD(&dirh->free_entries, dirh_e, next);
}
static void
udf_dirhash_remove(struct udf_node *dir_node, struct dirent *dirent,
uint64_t offset, uint32_t fid_size)
{
struct udf_dirhash *dirh;
struct udf_dirhash_entry *dirh_e;
uint32_t hashvalue, hashline;
DPRINTF(DIRHASH, ("dirhash remove %"PRIu64", %d for `%*.*s`\n",
offset, fid_size,
dirent->d_namlen, dirent->d_namlen, dirent->d_name));
/* make sure we have a dirhash to add to */
dirh = dir_node->dir_hash;
KASSERT(dirh);
/* calculate our hash */
hashvalue = udf_dirhash_hash(dirent->d_name, dirent->d_namlen);
hashline = hashvalue & UDF_DIRHASH_HASHMASK;
/* lookup entry */
LIST_FOREACH(dirh_e, &dirh->entries[hashline], next) {
/* check for hash collision */
if (dirh_e->hashvalue != hashvalue)
continue;
if (dirh_e->offset != offset)
continue;
/* got it! */
KASSERT(dirh_e->d_namlen == dirent->d_namlen);
KASSERT(dirh_e->fid_size == fid_size);
LIST_REMOVE(dirh_e, next);
udf_dirhash_enter_freed(dir_node, offset, fid_size);
return;
}
/* not found! */
panic("dirhash_remove couldn't find entry in hash table\n");
}
/* BUGALERT: don't use result longer than needed, never past the node lock */
/* call with NULL *result initially and it will return nonzero if again */
static int
udf_dirhash_lookup(struct udf_node *dir_node, const char *d_name, int d_namlen,
struct udf_dirhash_entry **result)
{
struct udf_dirhash *dirh;
struct udf_dirhash_entry *dirh_e;
uint32_t hashvalue, hashline;
KASSERT(VOP_ISLOCKED(dir_node->vnode));
/* make sure we have a dirhash to look into */
dirh = dir_node->dir_hash;
KASSERT(dirh);
/* start where we were */
if (*result) {
KASSERT(dir_node->dir_hash);
dirh_e = LIST_NEXT(*result, next);
if (dirh_e) {
hashvalue = dirh_e->hashvalue;
d_namlen = dirh_e->d_namlen;
}
} else {
/* calculate our hash */
hashvalue = udf_dirhash_hash(d_name, d_namlen);
hashline = hashvalue & UDF_DIRHASH_HASHMASK;
/* lookup all entries that match */
dirh_e = LIST_FIRST(&dirh->entries[hashline]);
}
for (; dirh_e; dirh_e = LIST_NEXT(dirh_e, next)) {
/* check for hash collision */
if (dirh_e->hashvalue != hashvalue)
continue;
if (dirh_e->d_namlen != d_namlen)
continue;
/* might have an entry in the cache */
*result = dirh_e;
return 1;
}
*result = NULL;
return 0;
}
/* BUGALERT: don't use result longer than needed, never past the node lock */
/* call with NULL *result initially and it will return nonzero if again */
static int
udf_dirhash_lookup_freed(struct udf_node *dir_node, uint32_t min_fidsize,
struct udf_dirhash_entry **result)
{
struct udf_dirhash *dirh;
struct udf_dirhash_entry *dirh_e;
KASSERT(VOP_ISLOCKED(dir_node->vnode));
/* make sure we have a dirhash to look into */
udf_dirhash_init(dir_node);
dirh = dir_node->dir_hash;
/* start where we were */
if (*result) {
KASSERT(dir_node->dir_hash);
dirh_e = LIST_NEXT(*result, next);
} else {
/* lookup all entries that match */
dirh_e = LIST_FIRST(&dirh->free_entries);
}
for (; dirh_e; dirh_e = LIST_NEXT(dirh_e, next)) {
/* check for minimum size */
if (dirh_e->fid_size < min_fidsize)
continue;
/* might be a candidate */
*result = dirh_e;
return 1;
}
*result = NULL;
return 0;
}
static int
udf_dirhash_fill(struct udf_node *dir_node)
{
struct vnode *dvp = dir_node->vnode;
struct udf_dirhash *dirh;
struct file_entry *fe = dir_node->fe;
struct extfile_entry *efe = dir_node->efe;
struct fileid_desc *fid;
struct dirent *dirent;
uint64_t file_size, diroffset, pre_diroffset;
uint64_t file_size, pre_diroffset, diroffset;
uint32_t lb_size;
int found, error;
int error;
/* already filled/read in? */
if (dir_node->i_flags & IN_DIRHASH_BROKEN)
return EIO;
if (dir_node->i_flags & IN_DIRHASH_COMPLETE)
return 0;
/* make sure we have a dirhash to add to */
udf_dirhash_init(dir_node);
dirh = dir_node->dir_hash;
KASSERT(dirh);
/* get directory filesize */
if (fe) {
@ -3883,52 +4165,110 @@ udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen,
lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size);
fid = malloc(lb_size, M_UDFTEMP, M_WAITOK);
found = 0;
diroffset = dir_node->last_diroffset;
/*
* if the directory is trunced or if we have never visited it yet,
* start at the end.
*/
if ((diroffset >= file_size) || (diroffset == 0)) {
diroffset = dir_node->last_diroffset = file_size;
}
/* allocate temporary space for dirent */
dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK);
while (!found) {
/* if at the end, go trough zero */
if (diroffset >= file_size)
diroffset = 0;
error = 0;
diroffset = 0;
while (diroffset < file_size) {
/* transfer a new fid/dirent */
pre_diroffset = diroffset;
error = udf_read_fid_stream(dvp, &diroffset, fid, dirent);
if (error) {
/* TODO what to do? continue but not add? */
dir_node->i_flags |= IN_DIRHASH_BROKEN;
break;
}
if ((fid->file_char & UDF_FILE_CHAR_DEL)) {
/* register deleted extent for reuse */
udf_dirhash_enter_freed(dir_node, pre_diroffset,
udf_fidsize(fid));
} else {
/* append to the dirhash */
udf_dirhash_enter(dir_node, fid, dirent, pre_diroffset,
udf_fidsize(fid), 0);
}
}
dir_node->i_flags |= IN_DIRHASH_COMPLETE;
free(fid, M_UDFTEMP);
free(dirent, M_UDFTEMP);
return error;
}
/* --------------------------------------------------------------------- */
/*
* Directory read and manipulation functions.
*
* Note that if the file is found, the cached diroffset position *before* the
* advance is remembered. Thus if the same filename is lookup again just after
* this lookup its immediately found.
*/
int
udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen,
struct long_ad *icb_loc, int *found)
{
struct udf_node *dir_node = VTOI(vp);
struct udf_dirhash_entry *dirh_ep;
struct fileid_desc *fid;
struct dirent *dirent;
uint64_t diroffset;
uint32_t lb_size;
int hit, error;
/* set default return */
*found = 0;
/* fillup with complete dir readin if needed */
error = udf_dirhash_fill(dir_node);
if (error)
return error;
/* allocate temporary space for fid */
lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size);
fid = malloc(lb_size, M_UDFTEMP, M_WAITOK);
dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK);
DPRINTF(DIRHASH, ("dirhash_lookup looking for `%*.*s`\n",
namelen, namelen, name));
/* search our dirhash hits */
memset(icb_loc, 0, sizeof(*icb_loc));
dirh_ep = NULL;
for (;;) {
hit = udf_dirhash_lookup(dir_node, name, namelen, &dirh_ep);
/* if no hit, abort the search */
if (!hit)
break;
/* check this hit */
diroffset = dirh_ep->offset;
/* transfer a new fid/dirent */
error = udf_read_fid_stream(vp, &diroffset, fid, dirent);
if (error)
break;
/* skip deleted entries */
if ((fid->file_char & UDF_FILE_CHAR_DEL) == 0) {
if ((strlen(dirent->d_name) == namelen) &&
(strncmp(dirent->d_name, name, namelen) == 0)) {
found = 1;
*icb_loc = fid->icb;
/* remember where we were before the advance */
diroffset = pre_diroffset;
}
}
DPRINTF(DIRHASH, ("dirhash_lookup\tchecking `%*.*s`\n",
dirent->d_namlen, dirent->d_namlen, dirent->d_name));
if (diroffset == dir_node->last_diroffset) {
/* we have cycled */
/* see if its our entry */
KASSERT(dirent->d_namlen == namelen);
if (strncmp(dirent->d_name, name, namelen) == 0) {
*found = 1;
*icb_loc = fid->icb;
break;
}
}
free(fid, M_UDFTEMP);
free(dirent, M_UDFTEMP);
dir_node->last_diroffset = diroffset;
return found;
return error;
}
/* --------------------------------------------------------------------- */
@ -4089,6 +4429,8 @@ int
udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node,
struct udf_node *udf_node, struct componentname *cnp)
{
struct vnode *dvp = dir_node->vnode;
struct udf_dirhash_entry *dirh_ep;
struct file_entry *fe = dir_node->fe;
struct extfile_entry *efe = dir_node->efe;
struct fileid_desc *fid;
@ -4098,7 +4440,7 @@ udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node,
int found, error;
char const *name = cnp->cn_nameptr;
int namelen = cnp->cn_namelen;
int refcnt;
int hit, refcnt;
/* get directory filesize */
if (fe) {
@ -4110,51 +4452,38 @@ udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node,
/* allocate temporary space for fid */
lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size);
fid = malloc(lb_size, M_UDFTEMP, M_WAITOK);
fid = malloc(lb_size, M_UDFTEMP, M_WAITOK);
dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK);
/* search our dirhash hits */
found = 0;
diroffset = dir_node->last_diroffset;
dirh_ep = NULL;
for (;;) {
hit = udf_dirhash_lookup(dir_node, name, namelen, &dirh_ep);
/* if no hit, abort the search */
if (!hit)
break;
/*
* if the directory is trunced or if we have never visited it yet,
* start at the end.
*/
if ((diroffset >= file_size) || (diroffset == 0)) {
diroffset = dir_node->last_diroffset = file_size;
}
dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK);
while (!found) {
/* if at the end, go trough zero */
if (diroffset >= file_size)
diroffset = 0;
/* check this hit */
diroffset = dirh_ep->offset;
/* transfer a new fid/dirent */
error = udf_read_fid_stream(dir_node->vnode, &diroffset,
fid, dirent);
error = udf_read_fid_stream(dvp, &diroffset, fid, dirent);
if (error)
break;
/* skip deleted entries */
if ((fid->file_char & UDF_FILE_CHAR_DEL) == 0) {
if ((strlen(dirent->d_name) == namelen) &&
(strncmp(dirent->d_name, name, namelen) == 0)) {
found = 1;
}
}
if (diroffset == dir_node->last_diroffset) {
/* we have cycled */
/* see if its our entry */
KASSERT(dirent->d_namlen == namelen);
if (strncmp(dirent->d_name, name, namelen) == 0) {
found = 1;
break;
}
}
if (!found) {
free(fid, M_UDFTEMP);
free(dirent, M_UDFTEMP);
dir_node->last_diroffset = diroffset;
return ENOENT;
}
if (!found)
error = ENOENT;
if (error)
goto error_out;
/* mark deleted */
fid->file_char |= UDF_FILE_CHAR_DEL;
@ -4163,7 +4492,7 @@ udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node,
#endif
(void) udf_validate_tag_and_crc_sums((union dscrptr *) fid);
/* roll back last advance from udf_read_fid_stream */
/* get size of fid and compensate for the read_fid_stream advance */
fidsize = udf_fidsize(fid);
diroffset -= fidsize;
@ -4172,58 +4501,60 @@ udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node,
fid, fidsize, diroffset,
UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED,
FSCRED, NULL, NULL);
if (error == 0) {
/* get reference count of attached node */
if (udf_node->fe) {
refcnt = udf_rw16(udf_node->fe->link_cnt);
} else {
KASSERT(udf_node->efe);
refcnt = udf_rw16(udf_node->efe->link_cnt);
}
if (error)
goto error_out;
/* get reference count of attached node */
if (udf_node->fe) {
refcnt = udf_rw16(udf_node->fe->link_cnt);
} else {
KASSERT(udf_node->efe);
refcnt = udf_rw16(udf_node->efe->link_cnt);
}
#ifdef UDF_COMPLETE_DELETE
/* substract reference counter in attached node */
refcnt -= 1;
if (udf_node->fe) {
udf_node->fe->link_cnt = udf_rw16(refcnt);
} else {
udf_node->efe->link_cnt = udf_rw16(refcnt);
}
/* prevent writeout when refcnt == 0 */
if (refcnt == 0)
udf_node->i_flags |= IN_DELETED;
if (fid->file_char & UDF_FILE_CHAR_DIR) {
int drefcnt;
/* substract reference counter in directory node */
/* note subtract 2 (?) for its was also backreferenced */
if (dir_node->fe) {
drefcnt = udf_rw16(dir_node->fe->link_cnt);
drefcnt -= 1;
dir_node->fe->link_cnt = udf_rw16(drefcnt);
} else {
KASSERT(dir_node->efe);
drefcnt = udf_rw16(dir_node->efe->link_cnt);
drefcnt -= 1;
dir_node->efe->link_cnt = udf_rw16(drefcnt);
}
}
udf_node->i_flags |= IN_MODIFIED;
dir_node->i_flags |= IN_MODIFIED;
#endif
/* if it is/was a hardlink adjust the file count */
if (refcnt > 0)
udf_adjust_filecount(udf_node, -1);
/* XXX we could restart at the deleted entry */
diroffset = 0;
/* substract reference counter in attached node */
refcnt -= 1;
if (udf_node->fe) {
udf_node->fe->link_cnt = udf_rw16(refcnt);
} else {
udf_node->efe->link_cnt = udf_rw16(refcnt);
}
/* prevent writeout when refcnt == 0 */
if (refcnt == 0)
udf_node->i_flags |= IN_DELETED;
if (fid->file_char & UDF_FILE_CHAR_DIR) {
int drefcnt;
/* substract reference counter in directory node */
/* note subtract 2 (?) for its was also backreferenced */
if (dir_node->fe) {
drefcnt = udf_rw16(dir_node->fe->link_cnt);
drefcnt -= 1;
dir_node->fe->link_cnt = udf_rw16(drefcnt);
} else {
KASSERT(dir_node->efe);
drefcnt = udf_rw16(dir_node->efe->link_cnt);
drefcnt -= 1;
dir_node->efe->link_cnt = udf_rw16(drefcnt);
}
}
udf_node->i_flags |= IN_MODIFIED;
dir_node->i_flags |= IN_MODIFIED;
#endif
/* if it is/was a hardlink adjust the file count */
if (refcnt > 0)
udf_adjust_filecount(udf_node, -1);
/* remove from the dirhash */
udf_dirhash_remove(dir_node, dirent, diroffset,
udf_fidsize(fid));
error_out:
free(fid, M_UDFTEMP);
free(dirent, M_UDFTEMP);
dir_node->last_diroffset = diroffset;
return error;
}
@ -4243,6 +4574,7 @@ udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node,
struct udf_node *udf_node, struct vattr *vap, struct componentname *cnp)
{
struct vnode *dvp = dir_node->vnode;
struct udf_dirhash_entry *dirh_ep;
struct fileid_desc *fid;
struct icb_tag *icbtag;
struct charspec osta_charspec;
@ -4251,17 +4583,17 @@ udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node,
uint64_t fid_pos, end_fid_pos, chosen_fid_pos;
uint32_t chosen_size, chosen_size_diff;
int lb_size, lb_rest, fidsize, this_fidsize, size_diff;
int file_char, refcnt, icbflags, addr_type, error;
int file_char, refcnt, icbflags, addr_type, hit, error;
lb_size = udf_rw32(ump->logical_vol->lb_size);
udf_osta_charset(&osta_charspec);
if (dir_node->fe) {
dir_size = udf_rw64(dir_node->fe->inf_len);
icbtag = &dir_node->fe->icbtag;
icbtag = &dir_node->fe->icbtag;
} else {
dir_size = udf_rw64(dir_node->efe->inf_len);
icbtag = &dir_node->efe->icbtag;
icbtag = &dir_node->efe->icbtag;
}
icbflags = udf_rw16(icbtag->flags);
@ -4269,10 +4601,10 @@ udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node,
if (udf_node->fe) {
unique_id = udf_rw64(udf_node->fe->unique_id);
refcnt = udf_rw16(udf_node->fe->link_cnt);
refcnt = udf_rw16(udf_node->fe->link_cnt);
} else {
unique_id = udf_rw64(udf_node->efe->unique_id);
refcnt = udf_rw16(udf_node->efe->link_cnt);
refcnt = udf_rw16(udf_node->efe->link_cnt);
}
if (refcnt > 0) {
@ -4280,7 +4612,6 @@ udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node,
udf_adjust_filecount(udf_node, 1);
}
/* determine file characteristics */
file_char = 0; /* visible non deleted file and not stream metadata */
if (vap->va_type == VDIR)
@ -4297,73 +4628,61 @@ udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node,
fidsize = (fidsize + 3) & ~3; /* multiple of 4 */
/* find position that will fit the FID */
diroffset = dir_node->last_diroffset;
/*
* if the directory is trunced or if we have never visited it yet,
* start at the end.
*/
if ((diroffset >= dir_size) || (diroffset == 0)) {
diroffset = dir_node->last_diroffset = dir_size;
}
chosen_fid_pos = diroffset;
chosen_fid_pos = dir_size;
chosen_size = 0;
chosen_size_diff = UINT_MAX;
for (;;) {
/* if at the end, go trough zero */
if (diroffset >= dir_size)
diroffset = 0;
/* shut up gcc */
dirent.d_namlen = 0;
/* get fid/dirent */
fid_pos = diroffset;
error = udf_read_fid_stream(dvp, &diroffset, fid, &dirent);
if (error)
/* search our dirhash hits */
error = 0;
dirh_ep = NULL;
for (;;) {
hit = udf_dirhash_lookup_freed(dir_node, fidsize, &dirh_ep);
/* if no hit, abort the search */
if (!hit)
break;
this_fidsize = udf_fidsize(fid);
/* check this hit for size */
this_fidsize = dirh_ep->fid_size;
/* reuse deleted entries */
if ((fid->file_char & UDF_FILE_CHAR_DEL)) {
size_diff = this_fidsize - fidsize;
end_fid_pos = fid_pos + this_fidsize;
lb_rest = lb_size - (end_fid_pos % lb_size);
/* check this hit */
fid_pos = dirh_ep->offset;
end_fid_pos = fid_pos + this_fidsize;
size_diff = this_fidsize - fidsize;
lb_rest = lb_size - (end_fid_pos % lb_size);
#ifndef UDF_COMPLETE_DELETE
/* only reuse entries that are wiped */
/* check if the len + loc are marked zero */
if (udf_rw32(fid->icb.len != 0))
break;
if (udf_rw32(fid->icb.loc.lb_num) != 0)
break;
if (udf_rw16(fid->icb.loc.part_num != 0))
break;
#endif
/* select if not splitting the tag and its smaller */
if ((size_diff >= 0) &&
(size_diff < chosen_size_diff) &&
(lb_rest >= sizeof(struct desc_tag)))
{
/* UDF 2.3.4.2+3 specifies rules for iu size */
if ((size_diff == 0) || (size_diff >= 32)) {
chosen_fid_pos = fid_pos;
chosen_size = this_fidsize;
chosen_size_diff = size_diff;
}
/* transfer a new fid/dirent */
error = udf_read_fid_stream(vp, &fid_pos, fid, dirent);
if (error)
goto error_out;
/* only reuse entries that are wiped */
/* check if the len + loc are marked zero */
if (udf_rw32(fid->icb.len != 0))
continue;
if (udf_rw32(fid->icb.loc.lb_num) != 0)
continue;
if (udf_rw16(fid->icb.loc.part_num != 0))
continue;
#endif /* UDF_COMPLETE_DELETE */
/* select if not splitting the tag and its smaller */
if ((size_diff >= 0) &&
(size_diff < chosen_size_diff) &&
(lb_rest >= sizeof(struct desc_tag)))
{
/* UDF 2.3.4.2+3 specifies rules for iu size */
if ((size_diff == 0) || (size_diff >= 32)) {
chosen_fid_pos = fid_pos;
chosen_size = this_fidsize;
chosen_size_diff = size_diff;
}
}
}
if (diroffset == dir_node->last_diroffset) {
/* we have cycled */
break;
}
}
/* unlikely */
if (error) {
free(fid, M_TEMP);
return error;
}
/* extend directory if no other candidate found */
if (chosen_size == 0) {
@ -4424,10 +4743,8 @@ udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node,
UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED,
FSCRED, NULL, NULL);
if (error) {
free(fid, M_TEMP);
return error;
}
if (error)
goto error_out;
/* add reference counter in attached node */
if (udf_node->fe) {
@ -4459,16 +4776,21 @@ udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node,
}
}
/* update our last position so we dont have to cycle again and again */
dir_node->last_diroffset = diroffset;
/* append to the dirhash */
dirent.d_namlen = cnp->cn_namelen;
memcpy(dirent.d_name, cnp->cn_nameptr, cnp->cn_namelen);
udf_dirhash_enter(dir_node, fid, &dirent, chosen_fid_pos,
udf_fidsize(fid), 1);
/* note updates */
udf_node->i_flags |= IN_CHANGE | IN_MODIFY; /* | IN_CREATE? */
/* VN_KNOTE(udf_node, ...) */
udf_update(udf_node->vnode, NULL, NULL, NULL, 0);
error_out:
free(fid, M_TEMP);
return 0;
return error;
}
/* --------------------------------------------------------------------- */
@ -4923,6 +5245,9 @@ udf_dispose_node(struct udf_node *udf_node)
/* TODO extended attributes and streamdir */
/* remove dirhash if present */
udf_dirhash_destroy(udf_node);
/* remove from our hash lookup table */
udf_deregister_node(udf_node);
@ -5419,6 +5744,7 @@ udf_update(struct vnode *vp, struct timespec *acc,
/* --------------------------------------------------------------------- */
/*
* Read one fid and process it into a dirent and advance to the next (*fid)
* has to be allocated a logical block in size, (*dirent) struct dirent length

View File

@ -1,4 +1,4 @@
/* $NetBSD: udf_subr.h,v 1.8 2008/07/07 18:45:27 reinoud Exp $ */
/* $NetBSD: udf_subr.h,v 1.9 2008/07/17 19:10:22 reinoud Exp $ */
/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
@ -150,7 +150,7 @@ void udf_add_app_regid(struct udf_mount *ump, struct regid *regid);
/* directory operations and helpers */
void udf_osta_charset(struct charspec *charspec);
int udf_read_fid_stream(struct vnode *vp, uint64_t *offset, struct fileid_desc *fid, struct dirent *dirent);
int udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen, struct long_ad *icb_loc);
int udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen, struct long_ad *icb_loc, int *found);
int udf_create_node(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, struct componentname *cnp);
void udf_delete_node(struct udf_node *udf_node);

View File

@ -1,4 +1,4 @@
/* $NetBSD: udf_vfsops.c,v 1.39 2008/06/28 01:34:05 rumble Exp $ */
/* $NetBSD: udf_vfsops.c,v 1.40 2008/07/17 19:10:22 reinoud Exp $ */
/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
@ -28,7 +28,7 @@
#include <sys/cdefs.h>
#ifndef lint
__KERNEL_RCSID(0, "$NetBSD: udf_vfsops.c,v 1.39 2008/06/28 01:34:05 rumble Exp $");
__KERNEL_RCSID(0, "$NetBSD: udf_vfsops.c,v 1.40 2008/07/17 19:10:22 reinoud Exp $");
#endif /* not lint */
@ -141,26 +141,36 @@ udf_init(void)
malloc_type_attach(M_UDFVOLD);
malloc_type_attach(M_UDFTEMP);
/* init hashtables and pools */
/* init node pools */
size = sizeof(struct udf_node);
pool_init(&udf_node_pool, size, 0, 0, 0, "udf_node_pool", NULL,
IPL_NONE);
pool_init(&udf_node_pool, size, 0, 0, 0,
"udf_node_pool", NULL, IPL_NONE);
/* init dirhash pools */
size = sizeof(struct udf_dirhash);
pool_init(&udf_dirhash_pool, size, 0, 0, 0,
"udf_dirhash_pool", NULL, IPL_NONE);
size = sizeof(struct udf_dirhash_entry);
pool_init(&udf_dirhash_entry_pool, size, 0, 0, 0,
"udf_dirhash_entry_pool", NULL, IPL_NONE);
}
void
udf_reinit(void)
{
/* recreate hashtables */
/* reinit pool? */
/* nothing to do */
}
void
udf_done(void)
{
/* remove hashtables and pools */
/* remove pools */
pool_destroy(&udf_node_pool);
pool_destroy(&udf_dirhash_pool);
pool_destroy(&udf_dirhash_entry_pool);
malloc_type_detach(M_UDFMNT);
malloc_type_detach(M_UDFVOLD);

View File

@ -1,4 +1,4 @@
/* $NetBSD: udf_vnops.c,v 1.29 2008/07/17 11:00:29 reinoud Exp $ */
/* $NetBSD: udf_vnops.c,v 1.30 2008/07/17 19:10:22 reinoud Exp $ */
/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
@ -32,7 +32,7 @@
#include <sys/cdefs.h>
#ifndef lint
__KERNEL_RCSID(0, "$NetBSD: udf_vnops.c,v 1.29 2008/07/17 11:00:29 reinoud Exp $");
__KERNEL_RCSID(0, "$NetBSD: udf_vnops.c,v 1.30 2008/07/17 19:10:22 reinoud Exp $");
#endif /* not lint */
@ -697,7 +697,10 @@ udf_lookup(void *v)
/* get our node */
name = "..";
namelen = 2;
found = udf_lookup_name_in_dir(dvp, name, namelen, &icb_loc);
error = udf_lookup_name_in_dir(dvp, name, namelen,
&icb_loc, &found);
if (error)
goto out;
if (!found)
error = ENOENT;
@ -710,7 +713,8 @@ udf_lookup(void *v)
error = udf_get_node(ump, &icb_loc, &res_node);
if (!error) {
DPRINTF(LOOKUP, ("\tnode retrieved/created OK\n"));
DPRINTF(LOOKUP,
("\tnode retrieved/created OK\n"));
*vpp = res_node->vnode;
}
}
@ -723,7 +727,10 @@ udf_lookup(void *v)
/* lookup filename in the directory; location icb_loc */
name = cnp->cn_nameptr;
namelen = cnp->cn_namelen;
found = udf_lookup_name_in_dir(dvp, name, namelen, &icb_loc);
error = udf_lookup_name_in_dir(dvp, name, namelen,
&icb_loc, &found);
if (error)
goto out;
if (!found) {
DPRINTF(LOOKUP, ("\tNOT found\n"));
/*
@ -767,6 +774,7 @@ udf_lookup(void *v)
}
}
out:
/*
* Store result in the cache if requested. If we are creating a file,
* the file might not be found and thus putting it into the namecache