The directory cookies of open directories are now correctly maintained and

removed when no longer used (the same bug is still present in devfs, btw,
and can potentially cause crashes).
Cleanup of the cookie code, it's now using util/list.h.
Now issues B_STAT_CHANGED notifications when directory entries changed.
rootfs_rename() was completely broken for "inter-directory" movements.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12850 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-05-26 19:56:41 +00:00
parent b602e341ca
commit 0f04de87a7
+87 -106
View File
@@ -34,7 +34,7 @@ struct rootfs_stream {
mode_t type; mode_t type;
struct stream_dir { struct stream_dir {
struct rootfs_vnode *dir_head; struct rootfs_vnode *dir_head;
struct rootfs_cookie *jar_head; struct list cookies;
} dir; } dir;
struct stream_symlink { struct stream_symlink {
char *path; char *path;
@@ -64,12 +64,10 @@ struct rootfs {
}; };
// dircookie, dirs are only types of streams supported by rootfs // dircookie, dirs are only types of streams supported by rootfs
struct rootfs_cookie { struct rootfs_dir_cookie {
struct rootfs_cookie *next; struct list_link link;
struct rootfs_cookie *prev; struct rootfs_vnode *current;
struct rootfs_vnode *ptr; int32 state; // iteration state
int oflags;
int state; // iteration state
}; };
// directory iteration states // directory iteration states
@@ -110,7 +108,8 @@ rootfs_vnode_compare_func(void *_v, const void *_key)
static struct rootfs_vnode * static struct rootfs_vnode *
rootfs_create_vnode(struct rootfs *fs, struct rootfs_vnode *parent, const char *name, int type) rootfs_create_vnode(struct rootfs *fs, struct rootfs_vnode *parent,
const char *name, int type)
{ {
struct rootfs_vnode *vnode; struct rootfs_vnode *vnode;
@@ -133,6 +132,9 @@ rootfs_create_vnode(struct rootfs *fs, struct rootfs_vnode *parent, const char *
vnode->gid = parent ? parent->gid : getegid(); vnode->gid = parent ? parent->gid : getegid();
// inherit group from parent if possible // inherit group from parent if possible
if (S_ISDIR(type))
list_init(&vnode->stream.dir.cookies);
return vnode; return vnode;
} }
@@ -148,51 +150,23 @@ rootfs_delete_vnode(struct rootfs *fs, struct rootfs_vnode *v, bool force_delete
// remove it from the global hash table // remove it from the global hash table
hash_remove(fs->vnode_list_hash, v); hash_remove(fs->vnode_list_hash, v);
if (v->name != NULL) free(v->name);
free(v->name);
free(v); free(v);
return 0; return 0;
} }
static void
insert_cookie_in_jar(struct rootfs_vnode *dir, struct rootfs_cookie *cookie)
{
cookie->next = dir->stream.dir.jar_head;
dir->stream.dir.jar_head = cookie;
cookie->prev = NULL;
}
#if 0
static void
remove_cookie_from_jar(struct rootfs_vnode *dir, struct rootfs_cookie *cookie)
{
// ToDo: why is this function not called?
if (cookie->next)
cookie->next->prev = cookie->prev;
if (cookie->prev)
cookie->prev->next = cookie->next;
if (dir->stream.dir.jar_head == cookie)
dir->stream.dir.jar_head = cookie->next;
cookie->prev = cookie->next = NULL;
}
#endif
/* makes sure none of the dircookies point to the vnode passed in */ /* makes sure none of the dircookies point to the vnode passed in */
static void static void
update_dircookies(struct rootfs_vnode *dir, struct rootfs_vnode *v) update_dir_cookies(struct rootfs_vnode *dir, struct rootfs_vnode *vnode)
{ {
struct rootfs_cookie *cookie; struct rootfs_dir_cookie *cookie = NULL;
for (cookie = dir->stream.dir.jar_head; cookie; cookie = cookie->next) { while ((cookie = list_get_next_item(&dir->stream.dir.cookies, cookie)) != NULL) {
if (cookie->ptr == v) if (cookie->current == vnode)
cookie->ptr = v->dir_next; cookie->current = vnode->dir_next;
} }
} }
@@ -200,34 +174,53 @@ update_dircookies(struct rootfs_vnode *dir, struct rootfs_vnode *v)
static struct rootfs_vnode * static struct rootfs_vnode *
rootfs_find_in_dir(struct rootfs_vnode *dir, const char *path) rootfs_find_in_dir(struct rootfs_vnode *dir, const char *path)
{ {
struct rootfs_vnode *v; struct rootfs_vnode *vnode;
if (!strcmp(path, ".")) if (!strcmp(path, "."))
return dir; return dir;
if (!strcmp(path, "..")) if (!strcmp(path, ".."))
return dir->parent; return dir->parent;
for (v = dir->stream.dir.dir_head; v; v = v->dir_next) { for (vnode = dir->stream.dir.dir_head; vnode; vnode = vnode->dir_next) {
if (strcmp(v->name, path) == 0) if (strcmp(vnode->name, path) == 0)
return v; return vnode;
} }
return NULL; return NULL;
} }
static status_t static status_t
rootfs_insert_in_dir(struct rootfs_vnode *dir, struct rootfs_vnode *v) rootfs_insert_in_dir(struct rootfs *fs, struct rootfs_vnode *dir,
struct rootfs_vnode *vnode)
{ {
v->dir_next = dir->stream.dir.dir_head; // make sure the directory stays sorted alphabetically
dir->stream.dir.dir_head = v;
struct rootfs_vnode *node = dir->stream.dir.dir_head, *last = NULL;
while (node && strcmp(node->name, vnode->name) < 0) {
last = node;
node = node->dir_next;
}
if (last == NULL) {
// the new vnode is the first entry in the list
vnode->dir_next = dir->stream.dir.dir_head;
dir->stream.dir.dir_head = vnode;
} else {
// insert after that node
vnode->dir_next = last->dir_next;
last->dir_next = vnode;
}
vnode->parent = dir;
dir->modification_time = time(NULL); dir->modification_time = time(NULL);
notify_stat_changed(fs->id, dir->id, B_STAT_MODIFICATION_TIME);
return B_OK; return B_OK;
} }
static status_t static status_t
rootfs_remove_from_dir(struct rootfs_vnode *dir, struct rootfs_vnode *findit) rootfs_remove_from_dir(struct rootfs *fs, struct rootfs_vnode *dir,
struct rootfs_vnode *findit)
{ {
struct rootfs_vnode *v; struct rootfs_vnode *v;
struct rootfs_vnode *last_v; struct rootfs_vnode *last_v;
@@ -235,7 +228,7 @@ rootfs_remove_from_dir(struct rootfs_vnode *dir, struct rootfs_vnode *findit)
for (v = dir->stream.dir.dir_head, last_v = NULL; v; last_v = v, v = v->dir_next) { for (v = dir->stream.dir.dir_head, last_v = NULL; v; last_v = v, v = v->dir_next) {
if (v == findit) { if (v == findit) {
/* make sure all dircookies dont point to this vnode */ /* make sure all dircookies dont point to this vnode */
update_dircookies(dir, v); update_dir_cookies(dir, v);
if (last_v) if (last_v)
last_v->dir_next = v->dir_next; last_v->dir_next = v->dir_next;
@@ -244,6 +237,7 @@ rootfs_remove_from_dir(struct rootfs_vnode *dir, struct rootfs_vnode *findit)
v->dir_next = NULL; v->dir_next = NULL;
dir->modification_time = time(NULL); dir->modification_time = time(NULL);
notify_stat_changed(fs->id, dir->id, B_STAT_MODIFICATION_TIME);
return B_OK; return B_OK;
} }
} }
@@ -263,7 +257,7 @@ rootfs_is_dir_empty(struct rootfs_vnode *dir)
static status_t static status_t
remove_node(struct rootfs *fs, struct rootfs_vnode *directory, struct rootfs_vnode *vnode) remove_node(struct rootfs *fs, struct rootfs_vnode *directory, struct rootfs_vnode *vnode)
{ {
rootfs_remove_from_dir(directory, vnode); rootfs_remove_from_dir(fs, directory, vnode);
notify_entry_removed(fs->id, directory->id, vnode->name, vnode->id); notify_entry_removed(fs->id, directory->id, vnode->name, vnode->id);
// schedule this vnode to be removed when it's ref goes to zero // schedule this vnode to be removed when it's ref goes to zero
@@ -516,8 +510,6 @@ rootfs_open(fs_volume _fs, fs_vnode _v, int oflags, fs_cookie *_cookie)
// allow to open the file, but it can't be done anything with it // allow to open the file, but it can't be done anything with it
*_cookie = NULL; *_cookie = NULL;
// initialize the cookie, because rootfs_free_cookie() relies on it
return B_OK; return B_OK;
} }
@@ -538,14 +530,6 @@ rootfs_close(fs_volume _fs, fs_vnode _v, fs_cookie _cookie)
static status_t static status_t
rootfs_free_cookie(fs_volume _fs, fs_vnode _v, fs_cookie _cookie) rootfs_free_cookie(fs_volume _fs, fs_vnode _v, fs_cookie _cookie)
{ {
struct rootfs_cookie *cookie = _cookie;
#ifdef TRACE_ROOTFS
struct rootfs_vnode *v = _v;
TRACE(("rootfs_freecookie: entry vnode %p, cookie %p\n", v, cookie));
#endif
free(cookie);
return B_OK; return B_OK;
} }
@@ -601,14 +585,10 @@ rootfs_create_dir(fs_volume _fs, fs_vnode _dir, const char *name, int mode, vnod
status = B_NO_MEMORY; status = B_NO_MEMORY;
goto err; goto err;
} }
vnode->parent = dir;
rootfs_insert_in_dir(dir, vnode);
rootfs_insert_in_dir(fs, dir, vnode);
hash_insert(fs->vnode_list_hash, vnode); hash_insert(fs->vnode_list_hash, vnode);
vnode->stream.dir.dir_head = NULL;
vnode->stream.dir.jar_head = NULL;
notify_entry_created(fs->id, dir->id, name, vnode->id); notify_entry_created(fs->id, dir->id, name, vnode->id);
mutex_unlock(&fs->lock); mutex_unlock(&fs->lock);
@@ -638,24 +618,23 @@ rootfs_open_dir(fs_volume _fs, fs_vnode _v, fs_cookie *_cookie)
{ {
struct rootfs *fs = (struct rootfs *)_fs; struct rootfs *fs = (struct rootfs *)_fs;
struct rootfs_vnode *vnode = (struct rootfs_vnode *)_v; struct rootfs_vnode *vnode = (struct rootfs_vnode *)_v;
struct rootfs_cookie *cookie; struct rootfs_dir_cookie *cookie;
TRACE(("rootfs_open: vnode %p\n", vnode)); TRACE(("rootfs_open: vnode %p\n", vnode));
if (!S_ISDIR(vnode->stream.type)) if (!S_ISDIR(vnode->stream.type))
return B_BAD_VALUE; return B_BAD_VALUE;
cookie = malloc(sizeof(struct rootfs_cookie)); cookie = malloc(sizeof(struct rootfs_dir_cookie));
if (cookie == NULL) if (cookie == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
mutex_lock(&fs->lock); mutex_lock(&fs->lock);
cookie->ptr = vnode->stream.dir.dir_head; cookie->current = vnode->stream.dir.dir_head;
//cookie->oflags = oflags;
cookie->state = ITERATION_STATE_BEGIN; cookie->state = ITERATION_STATE_BEGIN;
insert_cookie_in_jar(vnode, cookie); list_add_item(&vnode->stream.dir.cookies, cookie);
*_cookie = cookie; *_cookie = cookie;
mutex_unlock(&fs->lock); mutex_unlock(&fs->lock);
@@ -664,11 +643,24 @@ rootfs_open_dir(fs_volume _fs, fs_vnode _v, fs_cookie *_cookie)
} }
static status_t
rootfs_free_dir_cookie(fs_volume _fs, fs_vnode _v, fs_cookie _cookie)
{
struct rootfs_dir_cookie *cookie = _cookie;
struct rootfs_vnode *vnode = _v;
list_remove_item(&vnode->stream.dir.cookies, cookie);
free(cookie);
return B_OK;
}
static status_t static status_t
rootfs_read_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num) rootfs_read_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num)
{ {
struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode; struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode;
struct rootfs_cookie *cookie = _cookie; struct rootfs_dir_cookie *cookie = _cookie;
struct rootfs *fs = _fs; struct rootfs *fs = _fs;
status_t status = B_OK; status_t status = B_OK;
struct rootfs_vnode *childNode = NULL; struct rootfs_vnode *childNode = NULL;
@@ -694,7 +686,7 @@ rootfs_read_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, struct dirent
nextState = cookie->state + 1; nextState = cookie->state + 1;
break; break;
default: default:
childNode = cookie->ptr; childNode = cookie->current;
if (childNode) { if (childNode) {
name = childNode->name; name = childNode->name;
nextChildNode = childNode->dir_next; nextChildNode = childNode->dir_next;
@@ -722,7 +714,7 @@ rootfs_read_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, struct dirent
if (status < B_OK) if (status < B_OK)
goto err; goto err;
cookie->ptr = nextChildNode; cookie->current = nextChildNode;
cookie->state = nextState; cookie->state = nextState;
status = B_OK; status = B_OK;
@@ -736,13 +728,13 @@ err:
static status_t static status_t
rootfs_rewind_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie) rootfs_rewind_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie)
{ {
struct rootfs *fs = _fs; struct rootfs_dir_cookie *cookie = _cookie;
struct rootfs_vnode *vnode = _vnode; struct rootfs_vnode *vnode = _vnode;
struct rootfs_cookie *cookie = _cookie; struct rootfs *fs = _fs;
mutex_lock(&fs->lock); mutex_lock(&fs->lock);
cookie->ptr = vnode->stream.dir.dir_head; cookie->current = vnode->stream.dir.dir_head;
cookie->state = ITERATION_STATE_BEGIN; cookie->state = ITERATION_STATE_BEGIN;
mutex_unlock(&fs->lock); mutex_unlock(&fs->lock);
@@ -824,9 +816,8 @@ rootfs_symlink(fs_volume _fs, fs_vnode _dir, const char *name, const char *path,
status = B_NO_MEMORY; status = B_NO_MEMORY;
goto err; goto err;
} }
vnode->parent = dir;
rootfs_insert_in_dir(dir, vnode);
rootfs_insert_in_dir(fs, dir, vnode);
hash_insert(fs->vnode_list_hash, vnode); hash_insert(fs->vnode_list_hash, vnode);
vnode->stream.symlink.path = strdup(path); vnode->stream.symlink.path = strdup(path);
@@ -914,33 +905,23 @@ rootfs_rename(fs_volume _fs, fs_vnode _fromDir, const char *fromName, fs_vnode _
remove_node(fs, toDirectory, targetVnode); remove_node(fs, toDirectory, targetVnode);
} }
if (fromDirectory == toDirectory) { // change the name on this node
// rename to a different name in the same directory if (nameBuffer == NULL) {
// we can just copy it
// change the name on this node strcpy(vnode->name, toName);
if (nameBuffer == NULL) {
// we can just copy it
strcpy(vnode->name, toName);
} else {
free(vnode->name);
vnode->name = nameBuffer;
}
/* no need to remove and add it unless the dir is sorting */
#if 0
// remove it from the dir
rootfs_remove_from_dir(fromDirectory, vnode);
// add it back to the dir with the new name
rootfs_insert_in_dir(toDirectory, vnode);
#endif
} else { } else {
// different target directory from source free(vnode->name);
vnode->name = nameBuffer;
rootfs_remove_from_dir(fromDirectory, vnode);
rootfs_insert_in_dir(toDirectory, vnode);
} }
// remove it from the dir
rootfs_remove_from_dir(fs, fromDirectory, vnode);
// Add it back to the dir with the new name.
// We need to do this even in the same directory,
// so that it keeps sorted correctly.
rootfs_insert_in_dir(fs, toDirectory, vnode);
notify_entry_moved(fs->id, fromDirectory->id, fromName, toDirectory->id, toName, vnode->id); notify_entry_moved(fs->id, fromDirectory->id, fromName, toDirectory->id, toName, vnode->id);
status = B_OK; status = B_OK;
@@ -1095,8 +1076,8 @@ file_system_module_info gRootFileSystem = {
&rootfs_create_dir, &rootfs_create_dir,
&rootfs_remove_dir, &rootfs_remove_dir,
&rootfs_open_dir, &rootfs_open_dir,
&rootfs_close, // we are using the same operations for directories &rootfs_close, // same as for files - it does nothing, anyway
&rootfs_free_cookie, // and files here - that's intended, not by accident &rootfs_free_dir_cookie,
&rootfs_read_dir, &rootfs_read_dir,
&rootfs_rewind_dir, &rootfs_rewind_dir,