diff --git a/libc/Makefile b/libc/Makefile index 20cb8977..9e651267 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -200,6 +200,8 @@ readdirents.o \ read.o \ removeat.o \ remove.o \ +renameat.o \ +rename.o \ rmdir.o \ sbrk.o \ scanf.o \ diff --git a/libc/include/fsmarshall-msg.h b/libc/include/fsmarshall-msg.h index 711ca0d1..c46f9313 100644 --- a/libc/include/fsmarshall-msg.h +++ b/libc/include/fsmarshall-msg.h @@ -301,7 +301,18 @@ struct fsm_req_rmdir //char name[namelen]; }; -#define FSM_MSG_NUM 37 +#define FSM_REQ_RENAME 37 +struct fsm_req_rename +{ + ino_t olddirino; + ino_t newdirino; + size_t oldnamelen; + size_t newnamelen; + //char oldname[oldnamelen]; + //char newname[newnamelen]; +}; + +#define FSM_MSG_NUM 38 #if defined(__cplusplus) } /* extern "C" */ diff --git a/libc/include/stdio.h b/libc/include/stdio.h index ff8a09ab..9634c10d 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -104,6 +104,7 @@ extern int putchar(int c); extern int puts(const char* str); extern int removeat(int dirrfd, const char* path); extern int remove(const char* path); +extern int renameat(int oldfd, const char* oldname, int newfd, const char* newname); extern int rename(const char* oldname, const char* newname); extern void rewind(FILE* stream); extern int snprintf(char* restrict s, size_t n, const char* restrict format, ...); @@ -138,7 +139,6 @@ extern int getc_unlocked(FILE* stream); extern int pclose(FILE* steam); extern int putchar_unlocked(int c); extern int putc_unlocked(int c, FILE* steam); -extern int renameat(int oldfd, const char* oldname, int newfd, const char* newname); extern int setvbuf(FILE* restrict stream, char* restrict buf, int type, size_t size); extern int vdprintf(int fildes, const char* restrict format, __gnuc_va_list ap); extern void flockfile(FILE* file); diff --git a/libc/rename.cpp b/libc/rename.cpp new file mode 100644 index 00000000..1572923f --- /dev/null +++ b/libc/rename.cpp @@ -0,0 +1,31 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + rename.cpp + Moves a directory entry within the same file system. + +*******************************************************************************/ + +#include +#include + +extern "C" int rename(const char* oldname, const char* newname) +{ + return renameat(AT_FDCWD, oldname, AT_FDCWD, newname); +} diff --git a/libc/renameat.cpp b/libc/renameat.cpp new file mode 100644 index 00000000..fa7c1fee --- /dev/null +++ b/libc/renameat.cpp @@ -0,0 +1,36 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + renameat.cpp + Moves a directory entry within the same file system. + +*******************************************************************************/ + +#include + +#include +#include + +DEFN_SYSCALL4(int, sys_renameat, SYSCALL_RENAMEAT, int, const char*, int, const char*); + +extern "C" int renameat(int olddir, const char* oldname, int newdir, + const char* newname) +{ + return sys_renameat(olddir, oldname, newdir, newname); +} diff --git a/sortix/descriptor.cpp b/sortix/descriptor.cpp index cfe0fc10..f83b5b6a 100644 --- a/sortix/descriptor.cpp +++ b/sortix/descriptor.cpp @@ -396,6 +396,28 @@ int Descriptor::symlink(ioctx_t* ctx, const char* oldname, const char* filename) return ret; } +int Descriptor::rename_here(ioctx_t* ctx, Ref from, + const char* oldpath, const char* newpath) +{ + char* olddir_elem; + char* newdir_elem; + Ref olddir = OpenDirContainingPath(ctx, from, oldpath, + &olddir_elem); + if ( !olddir ) return -1; + + Ref newdir = OpenDirContainingPath(ctx, Ref(this), + newpath, &newdir_elem); + if ( !newdir ) { delete[] olddir_elem; return -1; } + + int ret = newdir->vnode->rename_here(ctx, olddir->vnode, olddir_elem, + newdir_elem); + + delete[] newdir_elem; + delete[] olddir_elem; + + return ret; +} + ssize_t Descriptor::readlink(ioctx_t* ctx, char* buf, size_t bufsize) { return vnode->readlink(ctx, buf, bufsize); diff --git a/sortix/fs/kram.cpp b/sortix/fs/kram.cpp index 5b3324c2..0b1780ce 100644 --- a/sortix/fs/kram.cpp +++ b/sortix/fs/kram.cpp @@ -393,5 +393,72 @@ int Dir::symlink(ioctx_t* /*ctx*/, const char* oldname, const char* filename) return -1; } +int Dir::rename_here(ioctx_t* ctx, Ref from, const char* oldname, + const char* newname) +{ + if ( IsDotOrDotDot(oldname) || IsDotOrDotDot(newname) ) + return errno = EINVAL, -1; + + // TODO: Check whether oldpath is an ancestor of newpath. + + // Avoid deadlocks by locking directories in the right order. + Dir* from_dir = (Dir*) from.Get(); + kthread_mutex_t* mutex_ptr1; + kthread_mutex_t* mutex_ptr2; + if ( from_dir->ino < this->ino ) + mutex_ptr1 = &from_dir->dirlock, + mutex_ptr2 = &this->dirlock; + else if ( from_dir->ino == this->ino ) + { + mutex_ptr1 = &this->dirlock, + mutex_ptr2 = NULL; + if ( !strcmp(oldname, newname) ) + return 0; + } + else + mutex_ptr1 = &this->dirlock, + mutex_ptr2 = &from_dir->dirlock; + ScopedLock lock1(mutex_ptr1); + ScopedLock lock2(mutex_ptr2); + + size_t from_index = from_dir->FindChild(oldname); + if ( from_index == SIZE_MAX ) + return errno = ENOENT, -1; + + Ref the_inode = from_dir->children[from_index].inode; + + size_t to_index = this->FindChild(newname); + if ( to_index != SIZE_MAX ) + { + Ref existing = this->children[to_index].inode; + + if ( existing->dev == the_inode->dev && + existing->ino == the_inode->ino ) + return 0; + + if ( S_ISDIR(existing->type) ) + { + Dir* existing_dir = (Dir*) existing.Get(); + if ( !S_ISDIR(the_inode->type) ) + return errno = EISDIR, -1; + assert(&existing_dir->dirlock != mutex_ptr1); + assert(&existing_dir->dirlock != mutex_ptr2); + if ( existing_dir->rmdir_me(ctx) != 0 ) + return -1; + } + this->children[to_index].inode = the_inode; + } + else + if ( !this->AddChild(newname, the_inode) ) + return -1; + + from_dir->RemoveChild(from_index); + + if ( S_ISDIR(the_inode->type) ) + the_inode->link_raw(ctx, "..", Ref(this)); + + return 0; +} + } // namespace KRAMFS } // namespace Sortix diff --git a/sortix/fs/kram.h b/sortix/fs/kram.h index dd740fca..3dc8360b 100644 --- a/sortix/fs/kram.h +++ b/sortix/fs/kram.h @@ -82,6 +82,8 @@ public: virtual int rmdir_me(ioctx_t* ctx); virtual int symlink(ioctx_t* ctx, const char* oldname, const char* filename); + virtual int rename_here(ioctx_t* ctx, Ref from, const char* oldname, + const char* newname); private: size_t FindChild(const char* filename); diff --git a/sortix/fs/user.cpp b/sortix/fs/user.cpp index 86f04759..aaa787a5 100644 --- a/sortix/fs/user.cpp +++ b/sortix/fs/user.cpp @@ -214,6 +214,8 @@ public: virtual int settermmode(ioctx_t* ctx, unsigned mode); virtual int gettermmode(ioctx_t* ctx, unsigned* mode); virtual int poll(ioctx_t* ctx, PollNode* node); + virtual int rename_here(ioctx_t* ctx, Ref from, const char* oldname, + const char* newname); private: bool SendMessage(Channel* channel, size_t type, void* ptr, size_t size, @@ -1121,6 +1123,28 @@ int Unode::poll(ioctx_t* /*ctx*/, PollNode* /*node*/) return errno = ENOTSUP, -1; } +int Unode::rename_here(ioctx_t* /*ctx*/, Ref from, const char* oldname, + const char* newname) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_rename msg; + msg.olddirino = this->ino; + msg.newdirino = from->ino; + msg.oldnamelen = strlen(oldname); + msg.newnamelen = strlen(newname); + size_t extra = msg.oldnamelen + msg.newnamelen; + if ( SendMessage(channel, FSM_REQ_RENAME, &msg, sizeof(msg), extra) && + channel->KernelSend(&kctx, oldname, msg.oldnamelen) && + channel->KernelSend(&kctx, newname, msg.newnamelen) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + // // Initialization. // diff --git a/sortix/include/sortix/kernel/descriptor.h b/sortix/include/sortix/kernel/descriptor.h index e66e1faf..9179c49d 100644 --- a/sortix/include/sortix/kernel/descriptor.h +++ b/sortix/include/sortix/kernel/descriptor.h @@ -79,6 +79,8 @@ public: int settermmode(ioctx_t* ctx, unsigned mode); int gettermmode(ioctx_t* ctx, unsigned* mode); int poll(ioctx_t* ctx, PollNode* node); + int rename_here(ioctx_t* ctx, Ref from, const char* oldpath, + const char* newpath); private: Ref open_elem(ioctx_t* ctx, const char* filename, int flags, diff --git a/sortix/include/sortix/kernel/inode.h b/sortix/include/sortix/kernel/inode.h index 2dacdb55..50d2ef80 100644 --- a/sortix/include/sortix/kernel/inode.h +++ b/sortix/include/sortix/kernel/inode.h @@ -89,6 +89,8 @@ public: virtual int settermmode(ioctx_t* ctx, unsigned mode) = 0; virtual int gettermmode(ioctx_t* ctx, unsigned* mode) = 0; virtual int poll(ioctx_t* ctx, PollNode* node) = 0; + virtual int rename_here(ioctx_t* ctx, Ref from, const char* oldname, + const char* newname) = 0; }; @@ -154,6 +156,8 @@ public: virtual int settermmode(ioctx_t* ctx, unsigned mode); virtual int gettermmode(ioctx_t* ctx, unsigned* mode); virtual int poll(ioctx_t* ctx, PollNode* node); + virtual int rename_here(ioctx_t* ctx, Ref from, const char* oldname, + const char* newname); }; diff --git a/sortix/include/sortix/kernel/vnode.h b/sortix/include/sortix/kernel/vnode.h index 42a09b2f..bf8ce780 100644 --- a/sortix/include/sortix/kernel/vnode.h +++ b/sortix/include/sortix/kernel/vnode.h @@ -76,6 +76,8 @@ public: int settermmode(ioctx_t* ctx, unsigned mode); int gettermmode(ioctx_t* ctx, unsigned* mode); int poll(ioctx_t* ctx, PollNode* node); + int rename_here(ioctx_t* ctx, Ref from, const char* oldname, + const char* newname); public /*TODO: private*/: Ref inode; diff --git a/sortix/include/sortix/syscallnum.h b/sortix/include/sortix/syscallnum.h index fab1d7c2..67ceee96 100644 --- a/sortix/include/sortix/syscallnum.h +++ b/sortix/include/sortix/syscallnum.h @@ -94,6 +94,7 @@ #define SYSCALL_LINKAT 70 #define SYSCALL_FSM_FSBIND 71 #define SYSCALL_PPOLL 72 -#define SYSCALL_MAX_NUM 73 /* index of highest constant + 1 */ +#define SYSCALL_RENAMEAT 73 +#define SYSCALL_MAX_NUM 74 /* index of highest constant + 1 */ #endif diff --git a/sortix/inode.cpp b/sortix/inode.cpp index 7afa5c43..e75ee311 100644 --- a/sortix/inode.cpp +++ b/sortix/inode.cpp @@ -281,4 +281,12 @@ int AbstractInode::poll(ioctx_t* /*ctx*/, PollNode* /*node*/) return errno = ENOTSUP, -1; } +int AbstractInode::rename_here(ioctx_t* /*ctx*/, Ref /*from*/, + const char* /*oldname*/, const char* /*newname*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + } // namespace Sortix diff --git a/sortix/io.cpp b/sortix/io.cpp index 6499994b..b4460700 100644 --- a/sortix/io.cpp +++ b/sortix/io.cpp @@ -510,6 +510,36 @@ static int sys_tcgetwinsize(int fd, struct winsize* ws) return desc->tcgetwinsize(&ctx, ws); } +static int sys_renameat_inner(int olddirfd, const char* oldpath, + int newdirfd, const char* newpath) +{ + const char* oldrelpath = oldpath; + Ref olddir(PrepareLookup(&oldrelpath, olddirfd)); + if ( !olddir ) + return -1; + + const char* newrelpath = newpath; + Ref newdir(PrepareLookup(&newrelpath, newdirfd)); + if ( !newdir ) + return -1; + + ioctx_t ctx; SetupUserIOCtx(&ctx); + return newdir->rename_here(&ctx, olddir, oldrelpath, newrelpath); +} + +static int sys_renameat(int olddirfd, const char* oldpath, + int newdirfd, const char* newpath) +{ + char* oldpathcopy = GetStringFromUser(oldpath); + if ( !oldpathcopy ) return -1; + char* newpathcopy = GetStringFromUser(newpath); + if ( !newpathcopy ) { delete[] oldpathcopy; return -1; } + int ret = sys_renameat_inner(olddirfd, oldpathcopy, newdirfd, newpathcopy); + delete[] newpathcopy; + delete[] oldpathcopy; + return ret; +} + void Init() { Syscall::Register(SYSCALL_ACCESS, (void*) sys_access); @@ -517,8 +547,8 @@ void Init() Syscall::Register(SYSCALL_CHMOD, (void*) sys_chmod); Syscall::Register(SYSCALL_CHOWN, (void*) sys_chown); Syscall::Register(SYSCALL_CLOSE, (void*) sys_close); - Syscall::Register(SYSCALL_DUP, (void*) sys_dup); Syscall::Register(SYSCALL_DUP2, (void*) sys_dup2); + Syscall::Register(SYSCALL_DUP, (void*) sys_dup); Syscall::Register(SYSCALL_FACCESSAT, (void*) sys_faccessat); Syscall::Register(SYSCALL_FCHDIR, (void*) sys_fchdir); Syscall::Register(SYSCALL_FCHMODAT, (void*) sys_fchmodat); @@ -541,6 +571,7 @@ void Init() Syscall::Register(SYSCALL_PWRITE, (void*) sys_pwrite); Syscall::Register(SYSCALL_READDIRENTS, (void*) sys_readdirents); Syscall::Register(SYSCALL_READ, (void*) sys_read); + Syscall::Register(SYSCALL_RENAMEAT, (void*) sys_renameat); Syscall::Register(SYSCALL_RMDIR, (void*) sys_rmdir); Syscall::Register(SYSCALL_SEEK, (void*) sys_seek); Syscall::Register(SYSCALL_SETTERMMODE, (void*) sys_settermmode); diff --git a/sortix/vnode.cpp b/sortix/vnode.cpp index b8dfa870..46c62be8 100644 --- a/sortix/vnode.cpp +++ b/sortix/vnode.cpp @@ -186,6 +186,15 @@ int Vnode::symlink(ioctx_t* ctx, const char* oldname, const char* filename) return inode->symlink(ctx, oldname, filename); } +int Vnode::rename_here(ioctx_t* ctx, Ref from, const char* oldname, + const char* newname) +{ + if ( from->dev != dev ) + return errno = EXDEV, -1; + // TODO: Force the same mount point here, like Linux does. + return inode->rename_here(ctx, from->inode, oldname, newname); +} + ssize_t Vnode::readlink(ioctx_t* ctx, char* buf, size_t bufsiz) { return inode->readlink(ctx, buf, bufsiz);