From 7be577a36329d6829047ae29d6c78a597e505941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 28 Jul 2002 18:41:07 +0000 Subject: [PATCH] Beginnings of symbolic link support. Cleanup of the code in many places. Fixed bad bugs in dir_vnode_to_path(), and entry_ref_to_vnode(). Fixed some other bugs. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@509 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/kernel/core/fs/bootfs.c | 36 ++-- src/kernel/core/fs/devfs.c | 55 +++--- src/kernel/core/fs/rootfs.c | 329 +++++++++++++++++++------------ src/kernel/core/fs/vfs.c | 377 ++++++++++++++++++++++++------------ 4 files changed, 519 insertions(+), 278 deletions(-) diff --git a/src/kernel/core/fs/bootfs.c b/src/kernel/core/fs/bootfs.c index 077e691663..e3953262a3 100755 --- a/src/kernel/core/fs/bootfs.c +++ b/src/kernel/core/fs/bootfs.c @@ -34,6 +34,11 @@ static char *bootdir = NULL; static off_t bootdir_len = 0; static region_id bootdir_region = -1; +typedef enum { + STREAM_TYPE_FILE = S_IFREG, + STREAM_TYPE_DIR = S_IFDIR, +} stream_type; + struct bootfs_stream { stream_type type; union { @@ -484,41 +489,38 @@ bootfs_sync(fs_cookie fs) static int -bootfs_lookup(fs_cookie _fs, fs_vnode _dir, const char *name, vnode_id *id) +bootfs_lookup(fs_cookie _fs, fs_vnode _dir, const char *name, vnode_id *_id, int *_type) { struct bootfs *fs = (struct bootfs *)_fs; struct bootfs_vnode *dir = (struct bootfs_vnode *)_dir; - struct bootfs_vnode *v; - struct bootfs_vnode *v1; - int err; + struct bootfs_vnode *vnode, *vdummy; + int status; TRACE(("bootfs_lookup: entry dir 0x%x, name '%s'\n", dir, name)); if (dir->stream.type != STREAM_TYPE_DIR) - return ENOTDIR; + return B_NOT_A_DIRECTORY; mutex_lock(&fs->lock); // look it up - v = bootfs_find_in_dir(dir, name); - if (!v) { - err = ENOENT; + vnode = bootfs_find_in_dir(dir, name); + if (!vnode) { + status = B_ENTRY_NOT_FOUND; goto err; } - err = vfs_get_vnode(fs->id, v->id, (fs_vnode *)&v1); - if(err < 0) { + status = vfs_get_vnode(fs->id, vnode->id, (fs_vnode *)&vdummy); + if (status < 0) goto err; - } - *id = v->id; - - err = B_NO_ERROR; + *_id = vnode->id; + *_type = dir->stream.type; err: mutex_unlock(&fs->lock); - return err; + return status; } @@ -989,7 +991,7 @@ err: static int -bootfs_write_stat(fs_cookie _fs, fs_vnode _v, struct stat *stat, int stat_mask) +bootfs_write_stat(fs_cookie _fs, fs_vnode _v, const struct stat *stat, int stat_mask) { struct bootfs *fs = _fs; struct bootfs_vnode *v = _v; @@ -1026,6 +1028,8 @@ static struct fs_calls bootfs_calls = { &bootfs_ioctl, &bootfs_fsync, + NULL, // read_link + NULL, // symlink &bootfs_unlink, &bootfs_rename, diff --git a/src/kernel/core/fs/devfs.c b/src/kernel/core/fs/devfs.c index fd11736c69..f7b0234e0f 100755 --- a/src/kernel/core/fs/devfs.c +++ b/src/kernel/core/fs/devfs.c @@ -34,6 +34,12 @@ # define INSANE(x) #endif +typedef enum { + STREAM_TYPE_DIR = S_IFDIR, + STREAM_TYPE_DEVICE = S_IFCHR, + STREAM_TYPE_SYMLINK = S_IFLNK +} stream_type; + struct devfs_part_map { off_t offset; off_t size; @@ -57,6 +63,9 @@ struct devfs_stream { device_hooks *calls; struct devfs_part_map *part_map; } dev; + struct stream_symlink { + char *path; + } symlink; } u; }; @@ -502,25 +511,24 @@ devfs_sync(fs_cookie fs) static int -devfs_lookup(fs_cookie _fs, fs_vnode _dir, const char *name, vnode_id *id) +devfs_lookup(fs_cookie _fs, fs_vnode _dir, const char *name, vnode_id *_id, int *_type) { struct devfs *fs = (struct devfs *)_fs; struct devfs_vnode *dir = (struct devfs_vnode *)_dir; - struct devfs_vnode *vnode; - struct devfs_vnode *vdummy; + struct devfs_vnode *vnode, *vdummy; int err; TRACE(("devfs_lookup: entry dir %p, name '%s'\n", dir, name)); if (dir->stream.type != STREAM_TYPE_DIR) - return ENOTDIR; + return B_NOT_A_DIRECTORY; mutex_lock(&fs->lock); // look it up vnode = devfs_find_in_dir(dir, name); if (!vnode) { - err = ERR_NOT_FOUND; + err = B_ENTRY_NOT_FOUND; goto err; } @@ -528,7 +536,8 @@ devfs_lookup(fs_cookie _fs, fs_vnode _dir, const char *name, vnode_id *id) if (err < 0) goto err; - *id = vnode->id; + *_id = vnode->id; + *_type = vnode->stream.type; err: mutex_unlock(&fs->lock); @@ -967,33 +976,33 @@ devfs_unlink(fs_cookie _fs, fs_vnode _dir, const char *name) { struct devfs *fs = _fs; struct devfs_vnode *dir = _dir; - struct devfs_vnode *v; - int res = B_NO_ERROR; + struct devfs_vnode *vnode; + int status = B_NO_ERROR; mutex_lock(&fs->lock); - v = devfs_find_in_dir( dir, name ); - if(!v) { - res = ERR_NOT_FOUND; + vnode = devfs_find_in_dir(dir, name); + if (!vnode) { + status = B_ENTRY_NOT_FOUND; goto err; } // you can unlink partitions only - if( v->stream.type != STREAM_TYPE_DEVICE || !v->stream.u.dev.part_map ) { - res = EROFS; + if (vnode->stream.type != STREAM_TYPE_DEVICE || !vnode->stream.u.dev.part_map) { + status = EROFS; goto err; } - res = devfs_remove_from_dir( v->parent, v ); - if( res ) + status = devfs_remove_from_dir(vnode->parent, vnode); + if (status < 0) goto err; - res = vfs_remove_vnode( fs->id, v->id ); + status = vfs_remove_vnode(fs->id, vnode->id); err: mutex_unlock(&fs->lock); - return res; + return status; } @@ -1012,20 +1021,16 @@ devfs_read_stat(fs_cookie _fs, fs_vnode _v, struct stat *stat) TRACE(("devfs_rstat: vnode %p (%Ld), stat %p\n", vnode, vnode->id, stat)); stat->st_ino = vnode->id; - stat->st_mode = DEFFILEMODE; stat->st_size = 0; - - if (vnode->stream.type == STREAM_TYPE_DIR) - stat->st_mode |= S_IFDIR; - else - stat->st_mode |= S_IFCHR; + // ToDo: or should this be just DEFFILEMODE (0666 instead of 0644)? + stat->st_mode = vnode->stream.type | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; return 0; } static int -devfs_write_stat(fs_cookie _fs, fs_vnode _v, struct stat *stat, int stat_mask) +devfs_write_stat(fs_cookie _fs, fs_vnode _v, const struct stat *stat, int stat_mask) { #if DEVFS_TRACE struct devfs_vnode *v = _v; @@ -1062,6 +1067,8 @@ static struct fs_calls devfs_calls = { &devfs_ioctl, &devfs_fsync, + NULL, // read_link + NULL, // symlink &devfs_unlink, &devfs_rename, diff --git a/src/kernel/core/fs/rootfs.c b/src/kernel/core/fs/rootfs.c index 915cf2417f..390d5a885b 100755 --- a/src/kernel/core/fs/rootfs.c +++ b/src/kernel/core/fs/rootfs.c @@ -27,12 +27,21 @@ #define TRACE(x) #endif +typedef enum { + STREAM_TYPE_DIR = S_IFDIR, + STREAM_TYPE_SYMLINK = S_IFLNK, +} stream_type; + struct rootfs_stream { - // only type of stream supported by rootfs + stream_type type; struct stream_dir { struct rootfs_vnode *dir_head; struct rootfs_cookie *jar_head; } dir; + struct stream_symlink { + char *path; + int length; + } symlink; }; struct rootfs_vnode { @@ -67,13 +76,13 @@ struct rootfs_cookie { static unsigned int rootfs_vnode_hash_func(void *_v, const void *_key, unsigned int range) { - struct rootfs_vnode *v = _v; + struct rootfs_vnode *vnode = _v; const vnode_id *key = _key; - if(v != NULL) - return v->id % range; - else - return (*key) % range; + if (vnode != NULL) + return vnode->id % range; + + return (*key) % range; } @@ -83,10 +92,10 @@ rootfs_vnode_compare_func(void *_v, const void *_key) struct rootfs_vnode *v = _v; const vnode_id *key = _key; - if(v->id == *key) + if (v->id == *key) return 0; - else - return -1; + + return -1; } @@ -124,14 +133,18 @@ rootfs_delete_vnode(struct rootfs *fs, struct rootfs_vnode *v, bool force_delete return 0; } -static void insert_cookie_in_jar(struct rootfs_vnode *dir, struct rootfs_cookie *cookie) + +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; } -static void remove_cookie_from_jar(struct rootfs_vnode *dir, struct rootfs_cookie *cookie) + +static void +remove_cookie_from_jar(struct rootfs_vnode *dir, struct rootfs_cookie *cookie) { if(cookie->next) cookie->next->prev = cookie->prev; @@ -148,48 +161,52 @@ static void update_dircookies(struct rootfs_vnode *dir, struct rootfs_vnode *v) { struct rootfs_cookie *cookie; - for(cookie = dir->stream.dir.jar_head; cookie; cookie = cookie->next) { - if(cookie->ptr == v) { + for (cookie = dir->stream.dir.jar_head; cookie; cookie = cookie->next) { + if(cookie->ptr == v) cookie->ptr = v->dir_next; - } } } -static struct rootfs_vnode *rootfs_find_in_dir(struct rootfs_vnode *dir, const char *path) + +static struct rootfs_vnode * +rootfs_find_in_dir(struct rootfs_vnode *dir, const char *path) { struct rootfs_vnode *v; - if(!strcmp(path, ".")) + if (!strcmp(path, ".")) return dir; - if(!strcmp(path, "..")) + if (!strcmp(path, "..")) return dir->parent; - for(v = dir->stream.dir.dir_head; v; v = v->dir_next) { - if(strcmp(v->name, path) == 0) { + for (v = dir->stream.dir.dir_head; v; v = v->dir_next) { + if (strcmp(v->name, path) == 0) return v; - } } return NULL; } -static int rootfs_insert_in_dir(struct rootfs_vnode *dir, struct rootfs_vnode *v) + +static int +rootfs_insert_in_dir(struct rootfs_vnode *dir, struct rootfs_vnode *v) { v->dir_next = dir->stream.dir.dir_head; dir->stream.dir.dir_head = v; return 0; } -static int rootfs_remove_from_dir(struct rootfs_vnode *dir, struct rootfs_vnode *findit) + +static int +rootfs_remove_from_dir(struct rootfs_vnode *dir, struct rootfs_vnode *findit) { struct rootfs_vnode *v; struct rootfs_vnode *last_v; - for(v = dir->stream.dir.dir_head, last_v = NULL; v; last_v = v, v = v->dir_next) { - if(v == findit) { + for (v = dir->stream.dir.dir_head, last_v = NULL; v; last_v = v, v = v->dir_next) { + if (v == findit) { /* make sure all dircookies dont point to this vnode */ update_dircookies(dir, v); - if(last_v) + if (last_v) last_v->dir_next = v->dir_next; else dir->stream.dir.dir_head = v->dir_next; @@ -228,9 +245,8 @@ rootfs_mount(fs_id id, const char *device, void *args, fs_cookie *_fs, vnode_id fs->next_vnode_id = 0; err = mutex_init(&fs->lock, "rootfs_mutex"); - if(err < 0) { + if (err < 0) goto err1; - } fs->vnode_list_hash = hash_init(ROOTFS_HASH_SIZE, (addr)&v->all_next - (addr)v, &rootfs_vnode_compare_func, &rootfs_vnode_hash_func); @@ -313,38 +329,38 @@ rootfs_sync(fs_cookie fs) static int -rootfs_lookup(fs_cookie _fs, fs_vnode _dir, const char *name, vnode_id *id) +rootfs_lookup(fs_cookie _fs, fs_vnode _dir, const char *name, vnode_id *_id, int *_type) { struct rootfs *fs = (struct rootfs *)_fs; struct rootfs_vnode *dir = (struct rootfs_vnode *)_dir; - struct rootfs_vnode *v; + struct rootfs_vnode *vnode,*vdummy; struct rootfs_vnode *v1; - int err; + int status; TRACE(("rootfs_lookup: entry dir 0x%x, name '%s'\n", dir, name)); + if (dir->stream.type != STREAM_TYPE_DIR) + return B_NOT_A_DIRECTORY; mutex_lock(&fs->lock); // look it up - v = rootfs_find_in_dir(dir, name); - if(!v) { - err = ENOENT; + vnode = rootfs_find_in_dir(dir, name); + if (!vnode) { + status = B_ENTRY_NOT_FOUND; goto err; } - err = vfs_get_vnode(fs->id, v->id, (fs_vnode *)&v1); - if(err < 0) { + status = vfs_get_vnode(fs->id, vnode->id, (fs_vnode *)&vdummy); + if (status < 0) goto err; - } - *id = v->id; - - err = B_NO_ERROR; + *_id = vnode->id; + *_type = vnode->stream.type; err: mutex_unlock(&fs->lock); - return err; + return status; } @@ -361,23 +377,23 @@ rootfs_get_vnode_name(fs_cookie _fs, fs_vnode _vnode, char *buffer, size_t buffe static int -rootfs_get_vnode(fs_cookie _fs, vnode_id id, fs_vnode *v, bool r) +rootfs_get_vnode(fs_cookie _fs, vnode_id id, fs_vnode *_vnode, bool reenter) { struct rootfs *fs = (struct rootfs *)_fs; - TRACE(("rootfs_getvnode: asking for vnode 0x%x 0x%x, r %d\n", id, r)); + TRACE(("rootfs_getvnode: asking for vnode 0x%x 0x%x, r %d\n", id, reenter)); - if (!r) + if (!reenter) mutex_lock(&fs->lock); - *v = hash_lookup(fs->vnode_list_hash, &id); + *_vnode = hash_lookup(fs->vnode_list_hash, &id); - if (!r) + if (!reenter) mutex_unlock(&fs->lock); - TRACE(("rootfs_getnvnode: looked it up at 0x%x\n", *v)); + TRACE(("rootfs_getnvnode: looked it up at 0x%x\n", *_vnode)); - if (*v) + if (*_vnode) return B_NO_ERROR; return ENOENT; @@ -385,42 +401,39 @@ rootfs_get_vnode(fs_cookie _fs, vnode_id id, fs_vnode *v, bool r) static int -rootfs_put_vnode(fs_cookie _fs, fs_vnode _v, bool r) +rootfs_put_vnode(fs_cookie _fs, fs_vnode _vnode, bool reenter) { #if ROOTFS_TRACE - struct rootfs_vnode *v = (struct rootfs_vnode *)_v; + struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode; - TRACE(("rootfs_putvnode: entry on vnode 0x%x 0x%x, r %d\n", v->id, r)); + TRACE(("rootfs_putvnode: entry on vnode 0x%x 0x%x, r %d\n", vnode->id, reenter)); #endif return 0; // whatever } static int -rootfs_remove_vnode(fs_cookie _fs, fs_vnode _v, bool r) +rootfs_remove_vnode(fs_cookie _fs, fs_vnode _vnode, bool reenter) { struct rootfs *fs = (struct rootfs *)_fs; - struct rootfs_vnode *v = (struct rootfs_vnode *)_v; - int err; + struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode; - TRACE(("rootfs_removevnode: remove 0x%x (0x%x 0x%x), r %d\n", v, v->id, r)); + TRACE(("rootfs_removevnode: remove 0x%x (0x%x 0x%x), r %d\n", vnode, vnode->id, reenter)); - if(!r) + if (!reenter) mutex_lock(&fs->lock); - if(v->dir_next) { + if (vnode->dir_next) { // can't remove node if it's linked to the dir - panic("rootfs_removevnode: vnode %p asked to be removed is present in dir\n", v); + panic("rootfs_removevnode: vnode %p asked to be removed is present in dir\n", vnode); } - rootfs_delete_vnode(fs, v, false); + rootfs_delete_vnode(fs, vnode, false); - err = 0; - - if (!r) + if (!reenter) mutex_unlock(&fs->lock); - return err; + return 0; } @@ -503,54 +516,51 @@ rootfs_create_dir(fs_cookie _fs, fs_vnode _dir, const char *name, int perms, vno { struct rootfs *fs = _fs; struct rootfs_vnode *dir = _dir; - struct rootfs_vnode *new_vnode; - struct rootfs_stream *s; - int err = 0; + struct rootfs_vnode *vnode; bool created_vnode = false; + int status = 0; - TRACE(("rootfs_create: dir 0x%x, name = '%s', stream_type = %d\n", dir, name, st)); + TRACE(("rootfs_create_dir: dir 0x%x, name = '%s', stream_type = %d\n", dir, name, st)); mutex_lock(&fs->lock); - new_vnode = rootfs_find_in_dir(dir, name); - if (new_vnode == NULL) { - dprintf("rootfs_create: creating new vnode\n"); - new_vnode = rootfs_create_vnode(fs); - if (new_vnode == NULL) { - err = ENOMEM; - goto err; - } - created_vnode = true; - new_vnode->name = kstrdup(name); - if (new_vnode->name == NULL) { - err = ENOMEM; - goto err1; - } - new_vnode->parent = dir; - rootfs_insert_in_dir(dir, new_vnode); - - hash_insert(fs->vnode_list_hash, new_vnode); - - s = &new_vnode->stream; - } else { - // we found the vnode - err = ERR_VFS_ALREADY_EXISTS; + vnode = rootfs_find_in_dir(dir, name); + if (vnode != NULL) { + status = B_FILE_EXISTS; goto err; } - new_vnode->stream.dir.dir_head = NULL; - new_vnode->stream.dir.jar_head = NULL; + dprintf("rootfs_create: creating new vnode\n"); + vnode = rootfs_create_vnode(fs); + if (vnode == NULL) { + status = B_NO_MEMORY; + goto err; + } + created_vnode = true; + vnode->name = kstrdup(name); + if (vnode->name == NULL) { + status = B_NO_MEMORY; + goto err1; + } + vnode->stream.type = STREAM_TYPE_DIR; + vnode->parent = dir; + rootfs_insert_in_dir(dir, vnode); + + hash_insert(fs->vnode_list_hash, vnode); + + vnode->stream.dir.dir_head = NULL; + vnode->stream.dir.jar_head = NULL; mutex_unlock(&fs->lock); return 0; err1: if (created_vnode) - rootfs_delete_vnode(fs, new_vnode, false); + rootfs_delete_vnode(fs, vnode, false); err: mutex_unlock(&fs->lock); - return err; + return status; } @@ -563,9 +573,12 @@ rootfs_open_dir(fs_cookie _fs, fs_vnode _v, file_cookie *_cookie) TRACE(("rootfs_open: vnode 0x%x\n", vnode)); + if (vnode->stream.type != STREAM_TYPE_DIR) + return B_BAD_VALUE; + cookie = kmalloc(sizeof(struct rootfs_cookie)); if (cookie == NULL) - return ENOMEM; + return B_NO_MEMORY; mutex_lock(&fs->lock); @@ -593,6 +606,7 @@ rootfs_read_dir(fs_cookie _fs, fs_vnode _vnode, file_cookie _cookie, struct dire mutex_lock(&fs->lock); if (cookie->ptr == NULL) { + // we're at the end of the directory *_num = 0; status = B_OK; goto err; @@ -666,44 +680,119 @@ rootfs_write_page(fs_cookie _fs, fs_vnode _v, iovecs *vecs, off_t pos) } +static int +rootfs_read_link(fs_cookie _fs, fs_vnode _link, char *buffer, size_t bufferSize) +{ + struct rootfs *fs = _fs; + struct rootfs_vnode *link = _link; + + if (link->stream.type != STREAM_TYPE_SYMLINK) + return B_BAD_VALUE; + + if (bufferSize < link->stream.symlink.length) + return B_NAME_TOO_LONG; + + memcpy(buffer, link->stream.symlink.path, link->stream.symlink.length + 1); + return link->stream.symlink.length; +} + + +static int +rootfs_symlink(fs_cookie _fs, fs_vnode _dir, const char *name, const char *path) +{ + struct rootfs *fs = _fs; + struct rootfs_vnode *dir = _dir; + struct rootfs_vnode *vnode; + bool created_vnode = false; + int status = 0; + + TRACE(("rootfs_symlink: dir 0x%x, name = '%s', path = %s\n", dir, name, path)); + + mutex_lock(&fs->lock); + + vnode = rootfs_find_in_dir(dir, name); + if (vnode != NULL) { + status = B_FILE_EXISTS; + goto err; + } + + dprintf("rootfs_create: creating new symlink\n"); + vnode = rootfs_create_vnode(fs); + if (vnode == NULL) { + status = B_NO_MEMORY; + goto err; + } + created_vnode = true; + vnode->name = kstrdup(name); + if (vnode->name == NULL) { + status = B_NO_MEMORY; + goto err1; + } + vnode->stream.type = STREAM_TYPE_SYMLINK; + vnode->parent = dir; + rootfs_insert_in_dir(dir, vnode); + + hash_insert(fs->vnode_list_hash, vnode); + + vnode->stream.symlink.path = kstrdup(path); + if (vnode->stream.symlink.path == NULL) { + status = ENOMEM; + goto err1; + } + vnode->stream.symlink.length = strlen(path); + + mutex_unlock(&fs->lock); + return 0; + +err1: + if (created_vnode) + rootfs_delete_vnode(fs, vnode, false); +err: + mutex_unlock(&fs->lock); + return status; +} + + static int rootfs_unlink(fs_cookie _fs, fs_vnode _dir, const char *name) { struct rootfs *fs = _fs; struct rootfs_vnode *dir = _dir; - struct rootfs_vnode *v; - int err; + struct rootfs_vnode *vnode; + int status; TRACE(("rootfs_unlink: dir 0x%x (0x%x 0x%x), name '%s'\n", dir, dir->id, name)); mutex_lock(&fs->lock); - v = rootfs_find_in_dir(dir, name); - if(!v) { - err = ERR_VFS_PATH_NOT_FOUND; + vnode = rootfs_find_in_dir(dir, name); + if (!vnode) { + status = B_ENTRY_NOT_FOUND; goto err; } - // do some checking to see if we can delete it - if(!rootfs_is_dir_empty(v)) { - err = ERR_VFS_DIR_NOT_EMPTY; + // if the node is a directory, only delete it if it's empty + if (vnode->stream.type == STREAM_TYPE_DIR && !rootfs_is_dir_empty(vnode)) { + status = B_DIRECTORY_NOT_EMPTY; goto err; } - rootfs_remove_from_dir(dir, v); + rootfs_remove_from_dir(dir, vnode); // schedule this vnode to be removed when it's ref goes to zero - vfs_remove_vnode(fs->id, v->id); + vfs_remove_vnode(fs->id, vnode->id); - err = 0; + status = 0; err: mutex_unlock(&fs->lock); - return err; + return status; } -static int rootfs_rename(fs_cookie _fs, fs_vnode _olddir, const char *oldname, fs_vnode _newdir, const char *newname) + +static int +rootfs_rename(fs_cookie _fs, fs_vnode _olddir, const char *oldname, fs_vnode _newdir, const char *newname) { struct rootfs *fs = _fs; struct rootfs_vnode *olddir = _olddir; @@ -717,23 +806,23 @@ static int rootfs_rename(fs_cookie _fs, fs_vnode _olddir, const char *oldname, f mutex_lock(&fs->lock); v1 = rootfs_find_in_dir(olddir, oldname); - if(!v1) { + if (!v1) { err = ERR_VFS_PATH_NOT_FOUND; goto err; } v2 = rootfs_find_in_dir(newdir, newname); - if(olddir == newdir) { + if (olddir == newdir) { // rename to a different name in the same dir - if(v2) { + if (v2) { // target node exists err = ERR_VFS_ALREADY_EXISTS; goto err; } // change the name on this node - if(strlen(oldname) >= strlen(newname)) { + if (strlen(oldname) >= strlen(newname)) { // reuse the old name buffer strcpy(v1->name, newname); } else { @@ -773,25 +862,25 @@ err: return err; } -static int rootfs_read_stat(fs_cookie _fs, fs_vnode _v, struct stat *stat) + +static int +rootfs_read_stat(fs_cookie _fs, fs_vnode _v, struct stat *stat) { - struct rootfs_vnode *v = _v; + struct rootfs_vnode *vnode = _v; - TRACE(("rootfs_rstat: vnode 0x%x (0x%x 0x%x), stat 0x%x\n", v, v->id, stat)); - -//dprintf("rootfs_rstat\n"); + TRACE(("rootfs_rstat: vnode 0x%x (0x%x 0x%x), stat 0x%x\n", vnode, vnode->id, stat)); // stream exists, but we know to return size 0, since we can only hold directories - stat->st_ino = v->id; + stat->st_ino = vnode->id; stat->st_size = 0; - stat->st_mode = (S_IFDIR | DEFFILEMODE); + stat->st_mode = vnode->stream.type | DEFFILEMODE; return 0; } static int -rootfs_write_stat(fs_cookie _fs, fs_vnode _v, struct stat *stat, int stat_mask) +rootfs_write_stat(fs_cookie _fs, fs_vnode _v, const struct stat *stat, int stat_mask) { #if ROOTFS_TRACE struct rootfs *fs = _fs; @@ -829,6 +918,8 @@ static struct fs_calls rootfs_calls = { &rootfs_ioctl, &rootfs_fsync, + &rootfs_read_link, + &rootfs_symlink, &rootfs_unlink, &rootfs_rename, diff --git a/src/kernel/core/fs/vfs.c b/src/kernel/core/fs/vfs.c index 929610332b..b8429cb416 100755 --- a/src/kernel/core/fs/vfs.c +++ b/src/kernel/core/fs/vfs.c @@ -527,31 +527,42 @@ find_fs(const char *fs_name) static status_t entry_ref_to_vnode(fs_id fsID,vnode_id directoryID,const char *name,struct vnode **_vnode) { - struct vnode *directory; + struct vnode *directory, *vnode; vnode_id id; int status; + int type; status = get_vnode(fsID,directoryID,&directory,false); if (status < 0) return status; - status = FS_CALL(directory,fs_lookup)(directory->mount->cookie,directory->private_node,name,&id); - dec_vnode_ref_count(directory, false); + status = FS_CALL(directory,fs_lookup)(directory->mount->cookie, + directory->private_node, name, &id, &type); + put_vnode(directory); if (status < 0) return status; - return get_vnode(fsID,id,_vnode,false); + mutex_lock(&vfs_vnode_mutex); + vnode = lookup_vnode(fsID, id); + mutex_unlock(&vfs_vnode_mutex); + + if (vnode == NULL) { + // fs_lookup() should have left the vnode referenced, so chances + // are good that this will never happen + panic("entry_ref_to_vnode: could not lookup vnode (fsid 0x%x vnid 0x%Lx)\n", fsID, id); + return B_ENTRY_NOT_FOUND; + } + + *_vnode = vnode; + return B_OK; } static int path_to_vnode(char *path, struct vnode **_vnode, bool kernel) { - char *nextPath; struct vnode *vnode; - struct vnode *nextVnode; - vnode_id vnid; int err = 0; if (!path) @@ -573,6 +584,11 @@ path_to_vnode(char *path, struct vnode **_vnode, bool kernel) } for (;;) { + struct vnode *nextVnode; + vnode_id vnodeID; + char *nextPath; + int type; + PRINT(("path_to_vnode: top of loop. p = %p, *p = %c, p = '%s'\n", path, *path, path)); // done? @@ -581,7 +597,8 @@ path_to_vnode(char *path, struct vnode **_vnode, bool kernel) break; } - // walk to find the next component + // walk to find the next path component ("path" will point to a single + // path component), and filter out multiple slashes for (nextPath = path + 1;*nextPath != '\0' && *nextPath != '/';nextPath++); if (*nextPath == '/') { @@ -602,30 +619,35 @@ path_to_vnode(char *path, struct vnode **_vnode, bool kernel) } } - // tell the filesystem to parse this path - err = FS_CALL(vnode,fs_lookup)(vnode->mount->cookie, vnode->private_node, path, &vnid); + // tell the filesystem to get the vnode of this path component + err = FS_CALL(vnode,fs_lookup)(vnode->mount->cookie, vnode->private_node, path, &vnodeID, &type); if (err < 0) { - dec_vnode_ref_count(vnode, false); + put_vnode(vnode); goto out; } - // lookup the vnode, the call to fs_lookup should have caused a get_vnode to be called - // from inside the filesystem, thus the vnode would have to be in the list and it's - // ref count incremented at this point - mutex_lock(&vfs_vnode_mutex); - nextVnode = lookup_vnode(vnode->fs_id, vnid); - mutex_unlock(&vfs_vnode_mutex); + if (S_ISLNK(type)) { + // ToDo: we have to resolve the link here + nextVnode = NULL; + } else { + // lookup the vnode, the call to fs_lookup should have caused a get_vnode to be called + // from inside the filesystem, thus the vnode would have to be in the list and it's + // ref count incremented at this point + mutex_lock(&vfs_vnode_mutex); + nextVnode = lookup_vnode(vnode->fs_id, vnodeID); + mutex_unlock(&vfs_vnode_mutex); + } if (!nextVnode) { // pretty screwed up here - panic("path_to_vnode: could not lookup vnode (fsid 0x%x vnid 0x%Lx)\n", vnode->fs_id, vnid); + panic("path_to_vnode: could not lookup vnode (fsid 0x%x vnid 0x%Lx)\n", vnode->fs_id, vnodeID); err = ERR_VFS_PATH_NOT_FOUND; - dec_vnode_ref_count(vnode, false); + put_vnode(vnode); goto out; } // decrease the ref count on the old dir we just looked up into - dec_vnode_ref_count(vnode, false); + put_vnode(vnode); path = nextPath; vnode = nextVnode; @@ -634,7 +656,7 @@ path_to_vnode(char *path, struct vnode **_vnode, bool kernel) if (vnode->covered_by) { nextVnode = vnode->covered_by; inc_vnode_ref_count(nextVnode); - dec_vnode_ref_count(vnode, false); + put_vnode(vnode); vnode = nextVnode; } } @@ -662,11 +684,12 @@ path_to_dir_vnode(char *path, struct vnode **_vnode, char *filename, bool kernel strcpy(path, "."); } else { // replace the filename portion of the path with a '.' - strcpy(filename, p+1); + strcpy(filename, ++p); - if (p[1] != '\0'){ - p[1] = '.'; - p[2] = '\0'; + // ToDo: this could write behind the array limit + if (p[0] != '\0'){ + p[0] = '.'; + p[1] = '\0'; } } return path_to_vnode(path, _vnode, kernel); @@ -712,10 +735,24 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize) // the name buffer is also used for fs_read_dir() char nameBuffer[sizeof(struct dirent) + B_FILE_NAME_LENGTH]; char *name = &((struct dirent *)nameBuffer)->d_name[0]; - vnode_id parentId, id; + struct vnode *parentVnode; + vnode_id parentID, id; + int type; // lookup the parent vnode - status = FS_CALL(vnode,fs_lookup)(vnode->mount->cookie,vnode->private_node,"..",&parentId); + status = FS_CALL(vnode,fs_lookup)(vnode->mount->cookie,vnode->private_node,"..",&parentID,&type); + if (status < B_OK) + goto out; + + mutex_lock(&vfs_vnode_mutex); + parentVnode = lookup_vnode(vnode->fs_id, parentID); + mutex_unlock(&vfs_vnode_mutex); + + if (parentVnode == NULL) { + panic("dir_vnode_to_path: could not lookup vnode (fsid 0x%x vnid 0x%Lx)\n", vnode->fs_id, parentID); + status = B_ENTRY_NOT_FOUND; + goto out; + } // Does the file system support getting the name of a vnode? // If so, get it here... @@ -728,27 +765,25 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize) // release the current vnode, we only need its parent from now on put_vnode(vnode); + vnode = parentVnode; if (status < B_OK) - return status; + goto out; // ToDo: add an explicit check for loops in about 10 levels to do // real loop detection // don't go deeper as 'maxLevel' to prevent circular loops - if (maxLevel-- < 0) - return ELOOP; - - if (parentId == vnode->id) { - // we have reached the root level directory of this file system - break; + if (maxLevel-- < 0) { + status = ELOOP; + goto out; } - // Get the parent vnode. The parent of the vnode may have already - // changed, though, which would result in an incorrect path - status = get_vnode(vnode->mount->id,parentId,&vnode,0); - if (status < B_OK) - return status; + if (parentID == id) { + // we have reached the root level directory of this file system + // which means we have constructed the full path + break; + } if (!FS_CALL(vnode,fs_get_vnode_name)) { // If we don't got the vnode's name yet, we have to search for it @@ -771,10 +806,8 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize) FS_CALL(vnode,fs_close_dir)(vnode->mount->cookie,vnode->private_node,cookie); } - if (status < B_OK) { - put_vnode(vnode); - return status; - } + if (status < B_OK) + goto out; } // add the name infront of the current path @@ -782,8 +815,8 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize) length = strlen(name); insert -= length; if (insert <= 0) { - put_vnode(vnode); - return ENOBUFS; + status = ENOBUFS; + goto out; } memcpy(path + insert, name, length); path[--insert] = '/'; @@ -791,13 +824,54 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize) // add the mountpoint length = strlen(vnode->mount->mount_point); - if (bufferSize - (sizeof(path) - insert) < length + 1) - return ENOBUFS; + if (bufferSize - (sizeof(path) - insert) < length + 1) { + status = ENOBUFS; + goto out; + } memcpy(buffer, vnode->mount->mount_point, length); if (insert != sizeof(path)) memcpy(buffer + length, path + insert, sizeof(path) - insert); +out: + put_vnode(vnode); + return status; +} + + +static status_t +check_path(char *to) +{ + int32 length = 0; + + // check length of every path component + + while (*to) { + char *begin; + if (*to == '/') + to++, length++; + + begin = to; + while (*to != '/' && *to) + to++, length++; + + if (to - begin > B_FILE_NAME_LENGTH) + return B_NAME_TOO_LONG; + } + + if (length == 0) + return B_ENTRY_NOT_FOUND; + + // complete path if there is a slash at the end + + if (*(to - 1) == '/') { + if (length > SYS_MAX_PATH_LEN - 2) + return B_NAME_TOO_LONG; + + to[0] = '.'; + to[1] = '\0'; + } + return B_OK; } @@ -810,14 +884,13 @@ int vfs_get_vnode(fs_id fsID, vnode_id vnodeID, fs_vnode *_fsNode) { struct vnode *vnode; - int err; - err = get_vnode(fsID, vnodeID, &vnode, true); - if (err < 0) - return err; + int status = get_vnode(fsID, vnodeID, &vnode, true); + if (status < 0) + return status; *_fsNode = vnode->private_node; - return B_NO_ERROR; + return B_OK; } @@ -833,7 +906,7 @@ vfs_put_vnode(fs_id fsID, vnode_id vnodeID) if (vnode) dec_vnode_ref_count(vnode, true); - return B_NO_ERROR; + return B_OK; } @@ -1868,6 +1941,29 @@ common_sync(int fd, bool kernel) } +static int +common_symlink(char *path, const char *toPath, bool kernel) +{ + // path validity checks have to be in the calling function! + char name[B_FILE_NAME_LENGTH]; + struct vnode *vnode; + int status; + + status = path_to_dir_vnode(path, &vnode, name, kernel); + if (status < B_OK) + return status; + + if (FS_CALL(vnode,fs_symlink) != NULL) + status = FS_CALL(vnode,fs_symlink)(vnode->mount->cookie, vnode->private_node, name, toPath); + else + status = EROFS; + + put_vnode(vnode); + + return status; +} + + static int common_unlink(char *path, bool kernel) { @@ -1928,19 +2024,19 @@ err: static int -common_write_stat(char *path, struct stat *stat, int stat_mask, bool kernel) +common_write_stat(char *path, const struct stat *stat, int statMask, bool kernel) { struct vnode *vnode; int status; - FUNCTION(("vfs_write_stat: path '%s', stat 0x%p, stat_mask %d, kernel %d\n", path, stat, stat_mask, kernel)); + FUNCTION(("vfs_write_stat: path '%s', stat 0x%p, stat_mask %d, kernel %d\n", path, stat, statMask, kernel)); status = path_to_vnode(path, &vnode, kernel); if (status < 0) return status; if (FS_CALL(vnode,fs_write_stat)) - status = FS_CALL(vnode,fs_write_stat)(vnode->mount->cookie, vnode->private_node, stat, stat_mask); + status = FS_CALL(vnode,fs_write_stat)(vnode->mount->cookie, vnode->private_node, stat, statMask); else status = EROFS; @@ -2070,29 +2166,27 @@ err: static int fs_unmount(char *path, bool kernel) { - struct vnode *v; struct fs_mount *mount; + struct vnode *vnode; int err; FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel)); - err = path_to_vnode(path, &v, kernel); - if (err < 0) { - err = ERR_VFS_PATH_NOT_FOUND; - goto err; - } + err = path_to_vnode(path, &vnode, kernel); + if (err < 0) + return ERR_VFS_PATH_NOT_FOUND; mutex_lock(&vfs_mount_op_mutex); - mount = find_mount(v->fs_id); + mount = find_mount(vnode->fs_id); if (!mount) - panic("vfs_unmount: fsid_to_mount failed on root vnode @%p of mount\n", v); + panic("vfs_unmount: fsid_to_mount failed on root vnode @%p of mount\n", vnode); - if (mount->root_vnode != v) { + if (mount->root_vnode != vnode) { // not mountpoint - dec_vnode_ref_count(v, false); + dec_vnode_ref_count(vnode, false); err = ERR_VFS_NOT_MOUNTPOINT; - goto err1; + goto err; } /* grab the vnode master mutex to keep someone from creating a vnode @@ -2102,24 +2196,24 @@ fs_unmount(char *path, bool kernel) /* simulate the root vnode having it's refcount decremented */ mount->root_vnode->ref_count -= 2; - /* cycle through the list of vnodes associated with this mount and - make sure all of them are not busy or have refs on them */ - err = 0; - for (v = mount->vnodes_head; v; v = v->mount_next) { - if (v->busy || v->ref_count != 0) { + // cycle through the list of vnodes associated with this mount and + // make sure all of them are not busy or have refs on them + for (vnode = mount->vnodes_head; vnode != NULL; vnode = vnode->mount_next) { + if (vnode->busy || vnode->ref_count != 0) { mount->root_vnode->ref_count += 2; mutex_unlock(&vfs_vnode_mutex); dec_vnode_ref_count(mount->root_vnode, false); + err = EBUSY; - goto err1; + goto err; } } /* we can safely continue, mark all of the vnodes busy and this mount structure in unmounting state */ - for (v = mount->vnodes_head; v; v = v->mount_next) - if (v != mount->root_vnode) - v->busy = true; + for (vnode = mount->vnodes_head; vnode; vnode = vnode->mount_next) + if (vnode != mount->root_vnode) + vnode->busy = true; mount->unmounting = true; mutex_unlock(&vfs_vnode_mutex); @@ -2148,9 +2242,8 @@ fs_unmount(char *path, bool kernel) return 0; -err1: - mutex_unlock(&vfs_mount_op_mutex); err: + mutex_unlock(&vfs_mount_op_mutex); return err; } @@ -2210,7 +2303,7 @@ error: static int -fs_write_info(dev_t device, struct fs_info *info, int mask) +fs_write_info(dev_t device, const struct fs_info *info, int mask) { struct fs_mount *mount; int status; @@ -2264,7 +2357,7 @@ set_cwd(char *path, bool kernel) struct stat stat; int rc; - FUNCTION(("vfs_set_cwd: path=\'%s\'\n", path)); + FUNCTION(("vfs_set_cwd: path = \'%s\'\n", path)); // Get vnode for passed path, and bail if it failed rc = path_to_vnode(path, &vnode, kernel); @@ -2309,7 +2402,7 @@ err: int sys_mount(const char *path, const char *device, const char *fs_name, void *args) { - char buf[SYS_MAX_PATH_LEN+1]; + char buf[SYS_MAX_PATH_LEN + 1]; strncpy(buf, path, SYS_MAX_PATH_LEN); buf[SYS_MAX_PATH_LEN] = 0; @@ -2321,7 +2414,7 @@ sys_mount(const char *path, const char *device, const char *fs_name, void *args) int sys_unmount(const char *path) { - char buf[SYS_MAX_PATH_LEN+1]; + char buf[SYS_MAX_PATH_LEN + 1]; strncpy(buf, path, SYS_MAX_PATH_LEN); buf[SYS_MAX_PATH_LEN] = 0; @@ -2415,7 +2508,7 @@ sys_create_entry_ref(dev_t device, ino_t inode, const char *name, int omode, int int sys_create(const char *path, int omode, int perms) { - char buffer[SYS_MAX_PATH_LEN+1]; + char buffer[SYS_MAX_PATH_LEN + 1]; strncpy(buffer, path, SYS_MAX_PATH_LEN); buffer[SYS_MAX_PATH_LEN] = '\0'; @@ -2439,7 +2532,7 @@ sys_create_dir_entry_ref(dev_t device, ino_t inode, const char *name, int perms) int sys_create_dir(const char *path, int perms) { - char buffer[SYS_MAX_PATH_LEN+1]; + char buffer[SYS_MAX_PATH_LEN + 1]; strncpy(buffer, path, SYS_MAX_PATH_LEN); buffer[SYS_MAX_PATH_LEN] = 0; @@ -2448,10 +2541,31 @@ sys_create_dir(const char *path, int perms) } +int +sys_symlink(const char *userPath, const char *userToPath) +{ + char path[SYS_MAX_PATH_LEN + 1]; + char toPath[SYS_MAX_PATH_LEN + 1]; + int status; + + strncpy(path, userPath, SYS_MAX_PATH_LEN); + path[SYS_MAX_PATH_LEN] = '\0'; + + strncpy(toPath, userToPath, SYS_MAX_PATH_LEN); + toPath[SYS_MAX_PATH_LEN] = '\0'; + + status = check_path(toPath); + if (status < B_OK) + return status; + + return common_symlink(path, toPath, true); +} + + int sys_unlink(const char *path) { - char buf[SYS_MAX_PATH_LEN+1]; + char buf[SYS_MAX_PATH_LEN + 1]; strncpy(buf, path, SYS_MAX_PATH_LEN); buf[SYS_MAX_PATH_LEN] = 0; @@ -2463,8 +2577,8 @@ sys_unlink(const char *path) int sys_rename(const char *oldpath, const char *newpath) { - char buf1[SYS_MAX_PATH_LEN+1]; - char buf2[SYS_MAX_PATH_LEN+1]; + char buf1[SYS_MAX_PATH_LEN + 1]; + char buf2[SYS_MAX_PATH_LEN + 1]; strncpy(buf1, oldpath, SYS_MAX_PATH_LEN); buf1[SYS_MAX_PATH_LEN] = 0; @@ -2554,20 +2668,16 @@ sys_setcwd(const char *_path) int user_mount(const char *upath, const char *udevice, const char *ufs_name, void *args) { - char path[SYS_MAX_PATH_LEN+1]; - char fs_name[SYS_MAX_OS_NAME_LEN+1]; - char device[SYS_MAX_PATH_LEN+1]; + char path[SYS_MAX_PATH_LEN + 1]; + char fs_name[SYS_MAX_OS_NAME_LEN + 1]; + char device[SYS_MAX_PATH_LEN + 1]; int rc; - if ((addr)upath >= KERNEL_BASE && (addr)upath <= KERNEL_TOP) + if (!CHECK_USER_ADDRESS(upath) + || !CHECK_USER_ADDRESS(ufs_name) + || !CHECK_USER_ADDRESS(udevice)) return B_BAD_ADDRESS; - if ((addr)ufs_name >= KERNEL_BASE && (addr)ufs_name <= KERNEL_TOP) - return ERR_VM_BAD_USER_MEMORY; - - if (udevice != NULL && (addr)udevice >= KERNEL_BASE && (addr)udevice <= KERNEL_TOP) - return ERR_VM_BAD_USER_MEMORY; - rc = user_strncpy(path, upath, SYS_MAX_PATH_LEN); if (rc < 0) return rc; @@ -2593,7 +2703,7 @@ user_mount(const char *upath, const char *udevice, const char *ufs_name, void *a int user_unmount(const char *upath) { - char path[SYS_MAX_PATH_LEN+1]; + char path[SYS_MAX_PATH_LEN + 1]; int rc; rc = user_strncpy(path, upath, SYS_MAX_PATH_LEN); @@ -2633,16 +2743,16 @@ user_open_entry_ref(dev_t device, ino_t inode, const char *uname, int omode) int user_open(const char *upath, int omode) { - char path[SYS_MAX_PATH_LEN]; + char path[SYS_MAX_PATH_LEN + 1]; int rc; if (!CHECK_USER_ADDRESS(upath)) return ERR_VM_BAD_USER_MEMORY; - rc = user_strncpy(path, upath, sizeof(path) - 1); + rc = user_strncpy(path, upath, sizeof(path)); if (rc < 0) return rc; - path[sizeof(path) - 1] = 0; + path[sizeof(path)] = 0; return file_open(path, omode, false); } @@ -2676,16 +2786,16 @@ user_open_dir_entry_ref(dev_t device, ino_t inode, const char *uname) int user_open_dir(const char *upath) { - char path[SYS_MAX_PATH_LEN]; + char path[SYS_MAX_PATH_LEN + 1]; int status; if (!CHECK_USER_ADDRESS(upath)) return ERR_VM_BAD_USER_MEMORY; - status = user_strncpy(path, upath, sizeof(path) - 1); + status = user_strncpy(path, upath, sizeof(path)); if (status < 0) return status; - path[sizeof(path) - 1] = 0; + path[sizeof(path)] = 0; return dir_open(path, false); } @@ -2719,16 +2829,16 @@ user_create_entry_ref(dev_t device, ino_t inode, const char *uname, int omode, i int user_create(const char *upath, int omode, int perms) { - char path[SYS_MAX_PATH_LEN]; - int rc; + char path[SYS_MAX_PATH_LEN + 1]; + int status; if ((addr)upath >= KERNEL_BASE && (addr)upath <= KERNEL_TOP) return ERR_VM_BAD_USER_MEMORY; - rc = user_strncpy(path, upath, SYS_MAX_PATH_LEN - 1); - if (rc < 0) - return rc; - path[SYS_MAX_PATH_LEN - 1] = '\0'; + status = user_strncpy(path, upath, SYS_MAX_PATH_LEN); + if (status < 0) + return status; + path[SYS_MAX_PATH_LEN] = '\0'; return file_create(path, omode, perms, false); } @@ -2755,25 +2865,54 @@ user_create_dir_entry_ref(dev_t device, ino_t inode, const char *uname, int perm int user_create_dir(const char *upath, int perms) { - char path[SYS_MAX_PATH_LEN]; - int rc; + char path[SYS_MAX_PATH_LEN + 1]; + int status; if (!CHECK_USER_ADDRESS(upath)) return ERR_VM_BAD_USER_MEMORY; - rc = user_strncpy(path, upath, SYS_MAX_PATH_LEN - 1); - if (rc < 0) - return rc; - path[SYS_MAX_PATH_LEN - 1] = '\0'; + status = user_strncpy(path, upath, SYS_MAX_PATH_LEN); + if (status < 0) + return status; + path[SYS_MAX_PATH_LEN] = '\0'; return dir_create(path, perms, false); } +int +user_symlink(const char *userPath, const char *userToPath) +{ + char path[SYS_MAX_PATH_LEN + 1]; + char toPath[SYS_MAX_PATH_LEN + 1]; + int status; + + if (!CHECK_USER_ADDRESS(userPath) + || !CHECK_USER_ADDRESS(userToPath)) + return B_BAD_ADDRESS; + + status = user_strncpy(path, userPath, SYS_MAX_PATH_LEN); + if (status < 0) + return status; + path[SYS_MAX_PATH_LEN] = '\0'; + + status = user_strncpy(toPath, userToPath, SYS_MAX_PATH_LEN); + if (status < 0) + return status; + toPath[SYS_MAX_PATH_LEN] = '\0'; + + status = check_path(toPath); + if (status < B_OK) + return status; + + return common_symlink(path, toPath, false); +} + + int user_unlink(const char *upath) { - char path[SYS_MAX_PATH_LEN+1]; + char path[SYS_MAX_PATH_LEN + 1]; int rc; if ((addr)upath >= KERNEL_BASE && (addr)upath <= KERNEL_TOP) @@ -2791,8 +2930,8 @@ user_unlink(const char *upath) int user_rename(const char *uoldpath, const char *unewpath) { - char oldpath[SYS_MAX_PATH_LEN+1]; - char newpath[SYS_MAX_PATH_LEN+1]; + char oldpath[SYS_MAX_PATH_LEN + 1]; + char newpath[SYS_MAX_PATH_LEN + 1]; int rc; if ((addr)uoldpath >= KERNEL_BASE && (addr)uoldpath <= KERNEL_TOP)