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);