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:
+87
-106
@@ -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,
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user