Add rename(2) and renameat(2).

This commit is contained in:
Jonas 'Sortie' Termansen 2012-12-20 16:19:07 +01:00
parent fd6098a3a2
commit 8e0aefda20
16 changed files with 256 additions and 4 deletions

View File

@ -200,6 +200,8 @@ readdirents.o \
read.o \
removeat.o \
remove.o \
renameat.o \
rename.o \
rmdir.o \
sbrk.o \
scanf.o \

View File

@ -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" */

View File

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

31
libc/rename.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
rename.cpp
Moves a directory entry within the same file system.
*******************************************************************************/
#include <fcntl.h>
#include <stdio.h>
extern "C" int rename(const char* oldname, const char* newname)
{
return renameat(AT_FDCWD, oldname, AT_FDCWD, newname);
}

36
libc/renameat.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
renameat.cpp
Moves a directory entry within the same file system.
*******************************************************************************/
#include <sys/syscall.h>
#include <fcntl.h>
#include <stdio.h>
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);
}

View File

@ -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<Descriptor> from,
const char* oldpath, const char* newpath)
{
char* olddir_elem;
char* newdir_elem;
Ref<Descriptor> olddir = OpenDirContainingPath(ctx, from, oldpath,
&olddir_elem);
if ( !olddir ) return -1;
Ref<Descriptor> newdir = OpenDirContainingPath(ctx, Ref<Descriptor>(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);

View File

@ -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<Inode> 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<Inode> the_inode = from_dir->children[from_index].inode;
size_t to_index = this->FindChild(newname);
if ( to_index != SIZE_MAX )
{
Ref<Inode> 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<Inode>(this));
return 0;
}
} // namespace KRAMFS
} // namespace Sortix

View File

@ -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<Inode> from, const char* oldname,
const char* newname);
private:
size_t FindChild(const char* filename);

View File

@ -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<Inode> 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<Inode> 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.
//

View File

@ -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<Descriptor> from, const char* oldpath,
const char* newpath);
private:
Ref<Descriptor> open_elem(ioctx_t* ctx, const char* filename, int flags,

View File

@ -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<Inode> 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<Inode> from, const char* oldname,
const char* newname);
};

View File

@ -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<Vnode> from, const char* oldname,
const char* newname);
public /*TODO: private*/:
Ref<Inode> inode;

View File

@ -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

View File

@ -281,4 +281,12 @@ int AbstractInode::poll(ioctx_t* /*ctx*/, PollNode* /*node*/)
return errno = ENOTSUP, -1;
}
int AbstractInode::rename_here(ioctx_t* /*ctx*/, Ref<Inode> /*from*/,
const char* /*oldname*/, const char* /*newname*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
} // namespace Sortix

View File

@ -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<Descriptor> olddir(PrepareLookup(&oldrelpath, olddirfd));
if ( !olddir )
return -1;
const char* newrelpath = newpath;
Ref<Descriptor> 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);

View File

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