Add symbolic links.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-09-22 17:35:54 +02:00
parent f26b2d6201
commit bb3f591057
11 changed files with 262 additions and 69 deletions

View File

@ -113,7 +113,7 @@ sysroot-source: sysroot-fsh
cp Makefile -t "$(SYSROOT)/src"
cp README -t "$(SYSROOT)/src"
cp -RT build-aux "$(SYSROOT)/src/build-aux"
(for D in $(MODULES); do (cp -LR $$D -t "$(SYSROOT)/src" && $(MAKE) -C "$(SYSROOT)/src/$$D" clean) || exit $$?; done)
(for D in $(MODULES); do (cp -R $$D -t "$(SYSROOT)/src" && $(MAKE) -C "$(SYSROOT)/src/$$D" clean) || exit $$?; done)
.PHONY: sysroot-ports
sysroot-ports: sysroot-fsh sysroot-base-headers sysroot-system sysroot-source

View File

@ -202,6 +202,14 @@ bool RespondRead(int svr, int chl, const uint8_t* buf, size_t count)
RespondData(svr, chl, buf, count);
}
bool RespondReadlink(int svr, int chl, const uint8_t* buf, size_t count)
{
struct fsm_resp_readlink body;
body.targetlen = count;
return RespondMessage(svr, chl, FSM_RESP_READLINK, &body, sizeof(body)) &&
RespondData(svr, chl, buf, count);
}
bool RespondWrite(int svr, int chl, size_t count)
{
struct fsm_resp_write body;
@ -577,6 +585,59 @@ void HandleLink(int svr, int chl, struct fsm_req_link* msg, Filesystem* fs)
inode->Unref();
}
void HandleSymlink(int svr, int chl, struct fsm_req_symlink* msg, Filesystem* fs)
{
if ( fs->num_inodes <= msg->dirino ) { RespondError(svr, chl, EBADF); return; }
Inode* inode = fs->GetInode((uint32_t) msg->dirino);
if ( !inode ) { RespondError(svr, chl, errno); return; }
char* dest_raw = (char*) &(msg[1]);
char* dest = (char*) malloc(msg->targetlen + 1);
if ( !dest )
{
RespondError(svr, chl, errno);
inode->Unref();
return;
}
memcpy(dest, dest_raw, msg->namelen);
dest[msg->targetlen] = '\0';
char* path_raw = (char*) dest_raw + msg->targetlen;
char* path = (char*) malloc(msg->namelen + 1);
if ( !path )
{
RespondError(svr, chl, errno);
inode->Unref();
return;
}
memcpy(path, path_raw, msg->namelen);
path[msg->namelen] = '\0';
if ( inode->Symlink(path, dest) )
RespondSuccess(svr, chl);
else
RespondError(svr, chl, errno);
free(path);
free(dest);
inode->Unref();
}
void HandleReadlink(int svr, int chl, struct fsm_req_readlink* msg, Filesystem* fs)
{
if ( fs->num_inodes <= msg->ino ) { RespondError(svr, chl, EBADF); return; }
Inode* inode = fs->GetInode((uint32_t) msg->ino);
if ( !inode ) { RespondError(svr, chl, errno); return; }
if ( !EXT2_S_ISLNK(inode->Mode()) ) { RespondError(svr, chl, EINVAL); return; }
size_t count = inode->Size();
uint8_t* buf = (uint8_t*) malloc(count);
if ( !buf ) { inode->Unref(); RespondError(svr, chl, errno); return; }
ssize_t amount = inode->ReadAt(buf, count, 0);
RespondReadlink(svr, chl, buf, amount);
inode->Unref();
free(buf);
}
void HandleRename(int svr, int chl, struct fsm_req_rename* msg, Filesystem* fs)
{
if ( fs->num_inodes <= msg->olddirino ) { RespondError(svr, chl, EBADF); return; }
@ -629,6 +690,8 @@ void HandleIncomingMessage(int svr, int chl, struct fsm_msg_header* hdr,
handlers[FSM_REQ_RMDIR] = (handler_t) HandleRemoveDir;
handlers[FSM_REQ_UNLINK] = (handler_t) HandleUnlink;
handlers[FSM_REQ_LINK] = (handler_t) HandleLink;
handlers[FSM_REQ_SYMLINK] = (handler_t) HandleSymlink;
handlers[FSM_REQ_READLINK] = (handler_t) HandleReadlink;
handlers[FSM_REQ_RENAME] = (handler_t) HandleRename;
// TODO: symlink
// TODO: readlink

View File

@ -31,6 +31,7 @@
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
@ -289,6 +290,12 @@ bool Inode::FreeIndirect(uint64_t from, uint64_t offset, uint32_t block_id,
void Inode::Truncate(uint64_t new_size)
{
if ( 0 < Size() && Size() <= 60 && !data->i_blocks )
{
fprintf(stderr, "extfs: Truncating optimized symlinks is not implemented!\n");
return;
}
// TODO: Enforce a filesize limit!
uint64_t old_size = Size();
SetSize(new_size);
@ -638,7 +645,7 @@ Inode* Inode::Unlink(const char* elem, bool directories, bool force)
ssize_t Inode::ReadAt(uint8_t* buf, size_t s_count, off_t o_offset)
{
if ( !EXT2_S_ISREG(Mode()) )
if ( !EXT2_S_ISREG(Mode()) && !EXT2_S_ISLNK(Mode()) )
return errno = EISDIR, -1;
if ( o_offset < 0 )
return errno = EINVAL, -1;
@ -652,6 +659,15 @@ ssize_t Inode::ReadAt(uint8_t* buf, size_t s_count, off_t o_offset)
return 0;
if ( file_size - offset < count )
count = file_size - offset;
// TODO: This case also needs to be handled in SetSize, Truncate, WriteAt,
// and so on.
if ( 0 < file_size && file_size <= 60 && !data->i_blocks )
{
assert(count <= 60);
unsigned char* block_data = (unsigned char*) &(data->i_block[0]);
memcpy(buf, block_data + offset, count);
return (ssize_t) count;
}
while ( sofar < count )
{
uint64_t block_id = offset / filesystem->block_size;
@ -671,7 +687,7 @@ ssize_t Inode::ReadAt(uint8_t* buf, size_t s_count, off_t o_offset)
ssize_t Inode::WriteAt(const uint8_t* buf, size_t s_count, off_t o_offset)
{
if ( !EXT2_S_ISREG(Mode()) )
if ( !EXT2_S_ISREG(Mode()) && !EXT2_S_ISLNK(Mode()) )
return errno = EISDIR, -1;
if ( o_offset < 0 )
return errno = EINVAL, -1;
@ -684,6 +700,11 @@ ssize_t Inode::WriteAt(const uint8_t* buf, size_t s_count, off_t o_offset)
uint64_t end_at = offset + count;
if ( offset < end_at )
/* TODO: Overflow! off_t overflow? */{};
if ( 0 < file_size && file_size <= 60 && !data->i_blocks )
{
fprintf(stderr, "extfs: Writing to optimized symlinks is not implemented!\n");
return errno = EROFS, -1;
}
if ( file_size < end_at )
Truncate(end_at);
while ( sofar < count )
@ -744,6 +765,44 @@ bool Inode::Rename(Inode* olddir, const char* oldname, const char* newname)
return true;
}
bool Inode::Symlink(const char* elem, const char* dest)
{
// TODO: Preferred block group!
uint32_t result_inode_id = filesystem->AllocateInode();
if ( !result_inode_id )
return NULL;
Inode* result = filesystem->GetInode(result_inode_id);
memset(result->data, 0, sizeof(*result->data));
result->SetMode((0777 & S_SETABLE) | EXT2_S_IFLNK);
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
result->data->i_atime = now.tv_sec;
result->data->i_ctime = now.tv_sec;
result->data->i_mtime = now.tv_sec;
// TODO: Set all the other inode properties!
if ( result->WriteAt((const uint8_t*) dest, strlen(dest), 0) < 0 )
{
error:
memset(result->data, 0, sizeof(*result->data));
// TODO: dtime
result->Unref();
filesystem->FreeInode(result_inode_id);
return false;
}
if ( !Link(elem, result, false) )
{
result->Truncate(0);
goto error;
}
result->Unref();
return true;
}
Inode* Inode::CreateDirectory(const char* path, mode_t mode)
{
// TODO: Preferred block group!
@ -753,7 +812,7 @@ Inode* Inode::CreateDirectory(const char* path, mode_t mode)
Inode* result = filesystem->GetInode(result_inode_id);
memset(result->data, 0, sizeof(*result->data));
result->SetMode((mode & S_SETABLE) | S_IFDIR);
result->SetMode((mode & S_SETABLE) | EXT2_S_IFDIR);
// Increase the directory count statistics.
uint32_t group_id = (result->inode_id - 1) / filesystem->sb->s_inodes_per_group;

View File

@ -59,6 +59,7 @@ public:
Block* GetBlockFromTable(Block* table, uint32_t index);
Inode* Open(const char* elem, int flags, mode_t mode);
bool Link(const char* elem, Inode* dest, bool directories);
bool Symlink(const char* elem, const char* dest);
Inode* Unlink(const char* elem, bool directories, bool force=false);
ssize_t ReadAt(uint8_t* buffer, size_t count, off_t offset);
ssize_t WriteAt(const uint8_t* buffer, size_t count, off_t offset);

View File

@ -385,6 +385,11 @@ Ref<Descriptor> Descriptor::open(ioctx_t* ctx, const char* filename, int flags,
if ( !IsSaneFlagModeCombination(flags, mode) )
return errno = EINVAL, Ref<Descriptor>();
char* filename_mine = NULL;
size_t symlink_iteration = 0;
const size_t MAX_SYMLINK_ITERATION = 20;
Ref<Descriptor> desc(this);
while ( filename[0] )
{
@ -393,7 +398,7 @@ Ref<Descriptor> Descriptor::open(ioctx_t* ctx, const char* filename, int flags,
if ( filename[0] == '/' )
{
if ( !S_ISDIR(desc->type) )
return errno = ENOTDIR, Ref<Descriptor>();
return delete[] filename_mine, errno = ENOTDIR, Ref<Descriptor>();
filename++;
continue;
}
@ -402,7 +407,7 @@ Ref<Descriptor> Descriptor::open(ioctx_t* ctx, const char* filename, int flags,
size_t slashpos = strcspn(filename, "/");
char* elem = String::Substring(filename, 0, slashpos);
if ( !elem )
return Ref<Descriptor>();
return delete[] filename_mine, Ref<Descriptor>();
// Decide how to open the next element in the path.
bool lastelem = IsLastPathElement(filename);
@ -414,12 +419,69 @@ Ref<Descriptor> Descriptor::open(ioctx_t* ctx, const char* filename, int flags,
delete[] elem;
if ( !next )
return Ref<Descriptor>();
return delete[] filename_mine, Ref<Descriptor>();
filename += slashpos;
bool want_the_symlink_itself = lastelem && (flags & O_SYMLINK_NOFOLLOW);
if ( S_ISLNK(next->type) && !want_the_symlink_itself )
{
if ( (flags & O_NOFOLLOW) && lastelem )
return delete[] filename_mine, errno = ELOOP, Ref<Descriptor>();
if ( symlink_iteration++ == MAX_SYMLINK_ITERATION )
return delete[] filename_mine, errno = ELOOP, Ref<Descriptor>();
ioctx_t kctx;
SetupKernelIOCtx(&kctx);
struct stat st;
if ( next->stat(&kctx, &st) < 0 )
return delete[] filename_mine, Ref<Descriptor>();
assert(0 <= st.st_size);
if ( (uintmax_t) SIZE_MAX <= (uintmax_t) st.st_size )
return delete[] filename_mine, Ref<Descriptor>();
size_t linkpath_length = (size_t) st.st_size;
char* linkpath = new char[linkpath_length + 1];
if ( !linkpath )
return delete[] filename_mine, Ref<Descriptor>();
ssize_t linkpath_ret = next->readlink(&kctx, linkpath, linkpath_length);
if ( linkpath_ret < 0 )
return delete[] linkpath, delete[] filename_mine, Ref<Descriptor>();
linkpath[linkpath_length] = '\0';
linkpath_length = strlen(linkpath);
if ( linkpath_length == 0 )
return delete[] linkpath, delete[] filename_mine,
errno = ENOENT, Ref<Descriptor>();
bool link_from_root = linkpath[0] == '/';
// Either filename is the empty string or starts with a slash.
size_t filename_length = strlen(filename);
// TODO: Avoid overflow here.
size_t new_filename_length = linkpath_length + filename_length;
char* new_filename = new char[new_filename_length + 1];
if ( !new_filename )
return delete[] linkpath, delete[] filename_mine,
errno = ENOENT, Ref<Descriptor>();
stpcpy(stpcpy(new_filename, linkpath), filename);
delete[] filename_mine;
filename = filename_mine = new_filename;
if ( link_from_root )
desc = CurrentProcess()->GetRoot();
continue;
}
desc = next;
filename += slashpos;
}
delete[] filename_mine;
// Abort the open if the user wanted a directory but this wasn't.
if ( flags & O_DIRECTORY && !S_ISDIR(desc->type) )
return errno = ENOTDIR, Ref<Descriptor>();

View File

@ -51,14 +51,15 @@
namespace Sortix {
namespace KRAMFS {
File::File(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode)
File::File(InodeType inode_type, mode_t type, dev_t dev, ino_t ino, uid_t owner,
gid_t group, mode_t mode)
{
inode_type = INODE_TYPE_FILE;
this->inode_type = inode_type;
if ( !dev )
dev = (dev_t) this;
if ( !ino )
ino = (ino_t) this;
this->type = S_IFREG;
this->type = type;
this->stat_uid = owner;
this->stat_gid = group;
this->stat_mode = (mode & S_SETABLE) | this->type;
@ -106,6 +107,15 @@ ssize_t File::pwrite(ioctx_t* ctx, const uint8_t* src, size_t count, off_t off)
return ret;
}
ssize_t File::readlink(ioctx_t* ctx, char* buf, size_t bufsize)
{
if ( !S_ISLNK(type) )
return errno = EINVAL, -1;
if ( (size_t) SSIZE_MAX < bufsize )
bufsize = SSIZE_MAX;
return fcache.pread(ctx, (uint8_t*) buf, bufsize, 0);
}
Dir::Dir(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode)
{
inode_type = INODE_TYPE_DIR;
@ -234,7 +244,8 @@ Ref<Inode> Dir::open(ioctx_t* ctx, const char* filename, int flags, mode_t mode)
}
if ( !(flags & O_CREATE) )
return errno = ENOENT, Ref<Inode>(NULL);
Ref<File> file(new File(dev, 0, ctx->uid, ctx->gid, mode));
Ref<File> file(new File(INODE_TYPE_FILE, S_IFREG, dev, 0, ctx->uid,
ctx->gid, mode));
if ( !file )
return Ref<Inode>(NULL);
if ( !AddChild(filename, file) )
@ -373,15 +384,36 @@ int Dir::unlink_raw(ioctx_t* /*ctx*/, const char* filename)
return 0;
}
int Dir::symlink(ioctx_t* /*ctx*/, const char* oldname, const char* filename)
int Dir::symlink(ioctx_t* ctx, const char* oldname, const char* filename)
{
ScopedLock lock(&dir_lock);
if ( shut_down )
return errno = ENOENT, -1;
(void) oldname;
(void) filename;
errno = ENOSYS;
return -1;
if ( FindChild(filename) != SIZE_MAX )
return errno = EEXIST, -1;
Ref<File> file(new File(INODE_TYPE_SYMLINK, S_IFLNK, dev, 0, ctx->uid,
ctx->gid, 0777));
if ( !file )
return Ref<Inode>(NULL);
ioctx_t kctx;
SetupKernelIOCtx(&kctx);
size_t oldname_length = strlen(oldname);
size_t so_far = 0;
while ( so_far < oldname_length )
{
#if OFF_MAX < SIZE_MAX
if ( (uintmax_t) OFF_MAX < (uintmax_t) so_far )
return Ref<Inode>(NULL);
#endif
ssize_t amount = file->pwrite(&kctx, (const uint8_t*) oldname + so_far,
oldname_length - so_far, (off_t) so_far);
if ( amount <= 0 )
return Ref<Inode>(NULL);
so_far += (size_t) amount;
}
if ( !AddChild(filename, file) )
return Ref<Inode>(NULL);
return 0;
}
int Dir::rename_here(ioctx_t* ctx, Ref<Inode> from, const char* oldname,

View File

@ -41,7 +41,8 @@ struct DirEntry
class File : public AbstractInode
{
public:
File(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode);
File(InodeType inode_type, mode_t type, dev_t dev, ino_t ino, uid_t owner,
gid_t group, mode_t mode);
virtual ~File();
virtual int truncate(ioctx_t* ctx, off_t length);
virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence);
@ -49,6 +50,7 @@ public:
off_t off);
virtual ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count,
off_t off);
virtual ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz);
private:
FileCache fcache;

View File

@ -122,6 +122,7 @@ enum InodeType
INODE_TYPE_STREAM,
INODE_TYPE_TTY,
INODE_TYPE_DIR,
INODE_TYPE_SYMLINK,
};
class AbstractInode : public Inode

View File

@ -260,38 +260,22 @@ static bool ExtractDir(struct initrd_context* ctx, initrd_inode_t* inode, Ref<De
}
}
}
}
for ( size_t i = 0; i < numfiles; i++ )
{
const char* name = initrd_directory_get_filename(ctx, inode, i);
if ( !name )
return false;
if ( IsDotOrDotDot(name) )
continue;
uint32_t childino = initrd_directory_open(ctx, inode, name);
if ( !childino )
return false;
initrd_inode_t* child = (initrd_inode_t*) initrd_get_inode(ctx, childino);
if ( INITRD_S_ISLNK(child->mode) )
{
size_t filesize;
uint8_t* data = initrd_inode_get_data(ctx, child, &filesize);
if ( !data )
return false;
char* dest_path = new char[filesize + 1];
if ( !dest_path )
char* oldname = new char[filesize + 1];
memcpy(oldname, data, filesize);
oldname[filesize] = '\0';
int ret = dir->symlink(&ctx->ioctx, oldname, name);
delete[] oldname;
if ( ret < 0 )
return false;
memcpy(dest_path, data, filesize);
dest_path[filesize] = '\0';
// TODO: Currently only symbolic links to files inside the same
// directory are supported when converted to hardlinks.
if ( !strchr(dest_path, '/') )
{
if ( Ref<Descriptor> dest = dir->open(&ctx->ioctx, dest_path, O_READ, 0) )
dir->link(&ctx->ioctx, name, dest);
}
delete[] dest_path;
Ref<Descriptor> desc = dir->open(&ctx->ioctx, name, O_READ | O_SYMLINK_NOFOLLOW, 0);
if ( desc )
ExtractNode(ctx, child, desc);
ctx->amount_extracted += child->size;
initrd_progress(ctx);
}

View File

@ -182,7 +182,7 @@ static int sys_openat(int dirfd, const char* path, int flags, mode_t mode)
// TODO: This is a hack! Stat the file in some manner and check permissions.
static int sys_faccessat(int dirfd, const char* path, int /*mode*/, int flags)
{
if ( flags & (AT_SYMLINK_NOFOLLOW) )
if ( flags & ~(AT_SYMLINK_NOFOLLOW) )
return errno = EINVAL, -1;
char* pathcopy = GetStringFromUser(path);
if ( !pathcopy )
@ -567,32 +567,26 @@ static int sys_symlinkat(const char* oldpath, int newdirfd, const char* newpath)
{
ioctx_t ctx; SetupUserIOCtx(&ctx);
char* newpathcopy = GetStringFromUser(newpath);
if ( !newpathcopy )
char* newpath_copy = GetStringFromUser(newpath);
if ( !newpath_copy )
return -1;
const char* newrelpath = newpathcopy;
Ref<Descriptor> newfrom(PrepareLookup(&newrelpath, newdirfd));
if ( !newfrom ) { delete[] newpathcopy; return -1; }
char* oldpath_copy = GetStringFromUser(oldpath);
if ( !oldpath_copy )
return delete[] newpath_copy, -1;
char* final_elem;
Ref<Descriptor> dir = OpenDirContainingPath(&ctx, newfrom, newpathcopy,
&final_elem);
delete[] newpathcopy;
if ( !dir )
return -1;
const char* newrel_path = newpath_copy;
Ref<Descriptor> newfrom(PrepareLookup(&newrel_path, newdirfd));
if ( !newfrom )
return delete[] newpath_copy, -1;
char* oldpathcopy = GetStringFromUser(oldpath);
if ( !oldpathcopy ) { delete[] final_elem; return -1; }
int ret = newfrom->symlink(&ctx, oldpath, newrel_path);
int ret = (errno = EPERM, -1);
delete[] oldpathcopy;
delete[] final_elem;
delete[] oldpath_copy;
delete[] newpath_copy;
return ret;
}
static int sys_settermmode(int fd, unsigned mode)
{
Ref<Descriptor> desc = CurrentProcess()->GetDescriptor(fd);
@ -698,8 +692,7 @@ static ssize_t sys_readlinkat(int dirfd, const char* path, char* buf, size_t siz
const char* relpath = pathcopy;
Ref<Descriptor> from = PrepareLookup(&relpath, dirfd);
if ( !from ) { delete[] pathcopy; return -1; }
// TODO: Open the symbolic link, instead of what it points to!
Ref<Descriptor> desc = from->open(&ctx, relpath, O_READ);
Ref<Descriptor> desc = from->open(&ctx, relpath, O_READ | O_SYMLINK_NOFOLLOW);
delete[] pathcopy;
if ( !desc )
return -1;

View File

@ -110,11 +110,7 @@ int main(int argc, char* argv[])
if ( force )
unlink(newname);
if ( symbolic )
fprintf(stderr, "%s: symbolic links are not supported, creating hard "
"link instead\n", argv0);
int ret = link(oldname, newname);
int ret = (symbolic ? symlink : link)(oldname, newname);
if ( ret == 0 )
{
if ( verbose )