# HG changeset patch # User Paul Boddie # Date 1643323517 -3600 # Node ID b769dcb3f4b339953ad34ca48c42e8e22d4f0e1e # Parent 472c1e2b030276d675ed88cea7877b87b1d3e704 Added initial support for renaming filesystem objects. diff -r 472c1e2b0302 -r b769dcb3f4b3 conf/dstest_file_rename.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file_rename.cfg Thu Jan 27 23:45:17 2022 +0100 @@ -0,0 +1,51 @@ +-- vim:set ft=lua: + +local L4 = require("L4"); + +local l = L4.default_loader; + +local pipe_server = l:new_channel(); + +l:startv({ + caps = { + server = pipe_server:svr(), + }, + log = { "pipes", "r" }, + }, + "rom/dstest_pipe_server", "10"); + +local block_server = l:new_channel(); + +l:startv({ + caps = { + server = block_server:svr(), + }, + log = { "blocksvr", "r" }, + }, + "rom/dstest_block_server", "10"); + +local ext2svr = l:new_channel(); + +l:startv({ + caps = { + blocksvr = block_server, + pipes = pipe_server, + ext2svr = ext2svr:svr(), + }, + log = { "ext2svr", "y" }, + }, + "rom/dstest_ext2_server", "blocksvr", "rom/e2test.fs", "10", "ext2svr"); + +-- Obtain user filesystems with umask 0022 (18). + +local open_for_user = 6; +local ext2svr_paulb = L4.cast(L4.Proto.Factory, ext2svr):create(open_for_user, 1000, 1000, 18); + +l:startv({ + caps = { + server = ext2svr_paulb, + }, + log = { "client", "g" }, + }, + -- program, directory to read + "rom/dstest_file_rename", "home/paulb/many"); diff -r 472c1e2b0302 -r b769dcb3f4b3 conf/dstest_file_rename.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file_rename.list Thu Jan 27 23:45:17 2022 +0100 @@ -0,0 +1,28 @@ +entry dstest_file_rename +roottask moe rom/dstest_file_rename.cfg +module dstest_file_rename.cfg +module e2test.fs +module l4re +module ned +module dstest_file_rename +module dstest_ext2_server +module dstest_block_server +module dstest_pipe_server +module lib4re-c.so +module lib4re-c-util.so +module lib4re.so +module lib4re-util.so +module libc_be_l4refile.so +module libc_be_l4re.so +module libc_be_socket_noop.so +module libc_support_misc.so +module libdl.so +module libipc.so +module libl4sys-direct.so +module libl4sys.so +module libl4util.so +module libld-l4.so +module libpthread.so +module libstdc++.so +module libsupc++.so +module libuc_c.so diff -r 472c1e2b0302 -r b769dcb3f4b3 libe2access/include/e2access/image.h --- a/libe2access/include/e2access/image.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libe2access/include/e2access/image.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * Filesystem image access functions. * - * Copyright (C) 2019, 2021 Paul Boddie + * Copyright (C) 2019, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -33,6 +33,8 @@ const char *basename, __u16 mode, __u16 uid, __u16 gid, ext2_ino_t *ino_file); +int image_file_type(int mode); + errcode_t image_find_next(ext2_filsys fs, ext2_ino_t ino_dir, const char **basename, char *buf, ext2_ino_t *ino); @@ -64,6 +66,10 @@ errcode_t image_remove_by_inode(ext2_filsys fs, ext2_ino_t ino); +errcode_t image_rename(ext2_filsys fs, ext2_ino_t source, + ext2_ino_t source_parent, const char *source_basename, + ext2_ino_t target_parent, const char *target_basename); + void image_set_metadata(struct ext2_inode *inode, int clean, __u16 mode, __u16 uid, __u16 gid); diff -r 472c1e2b0302 -r b769dcb3f4b3 libe2access/lib/src/image.c --- a/libe2access/lib/src/image.c Fri Jan 14 01:10:35 2022 +0100 +++ b/libe2access/lib/src/image.c Thu Jan 27 23:45:17 2022 +0100 @@ -28,28 +28,20 @@ -/* Create an inode for a file. */ +/* Link an inode within the given target directory having the given basename. */ -errcode_t image_create_file(ext2_filsys fs, ext2_ino_t ino_target, - const char *basename, __u16 mode, - __u16 uid, __u16 gid, ext2_ino_t *ino_file) +static errcode_t _image_link(ext2_filsys fs, ext2_ino_t ino_target, + const char *basename, ext2_ino_t ino_file, + int flags) { - struct ext2_inode inode_file; errcode_t retval; int retry; - /* Without an inode, create a new one. */ - - retval = ext2fs_new_inode(fs, ino_target, LINUX_S_IFREG | mode, 0, ino_file); - if (retval) - return retval; - /* Connect the inode to its parent. */ for (retry = 0; retry <= 1; retry++) { - retval = ext2fs_link(fs, ino_target, basename, *ino_file, - EXT2_FT_REG_FILE); + retval = ext2fs_link(fs, ino_target, basename, ino_file, flags); if (!retval) break; @@ -65,6 +57,50 @@ return retval; } + return 0; +} + +/* Update the parent entry of a renamed directory. */ + +struct _image_parent_entry_state +{ + ext2_ino_t ino; +}; + +static int _image_update_parent_entry(struct ext2_dir_entry *dir_entry, + int offset, int blocksize, char *buf, + void *priv_data) +{ + struct _image_parent_entry_state *state = (struct _image_parent_entry_state *) priv_data; + + (void) offset; (void) blocksize; (void) buf; + + if (strcmp(dir_entry->name, "..") == 0) + { + dir_entry->inode = state->ino; + return DIRENT_CHANGED | DIRENT_ABORT; + } + else + return 0; +} + +/* Create an inode for a file. */ + +errcode_t image_create_file(ext2_filsys fs, ext2_ino_t ino_target, + const char *basename, __u16 mode, + __u16 uid, __u16 gid, ext2_ino_t *ino_file) +{ + struct ext2_inode inode_file; + errcode_t retval; + + /* Without an inode, create a new one. */ + + retval = ext2fs_new_inode(fs, ino_target, LINUX_S_IFREG | mode, 0, ino_file); + if (retval) + return retval; + + _image_link(fs, ino_target, basename, *ino_file, EXT2_FT_REG_FILE); + /* Make sure that subsequent files employ different inodes. */ ext2fs_inode_alloc_stats2(fs, *ino_file, 1, 0); @@ -77,6 +113,26 @@ return ext2fs_write_new_inode(fs, *ino_file, &inode_file); } +/* Return the appropriate ext2 file type value for the given mode value. */ + +int image_file_type(int mode) +{ + switch (mode & LINUX_S_IFMT) + { + case LINUX_S_IFSOCK: return EXT2_FT_SOCK; + case LINUX_S_IFLNK: return EXT2_FT_SYMLINK; + case LINUX_S_IFREG: return EXT2_FT_REG_FILE; + case LINUX_S_IFBLK: return EXT2_FT_BLKDEV; + case LINUX_S_IFDIR: return EXT2_FT_DIR; + case LINUX_S_IFCHR: return EXT2_FT_CHRDEV; + case LINUX_S_IFIFO: return EXT2_FT_FIFO; + + /* NOTE: Perhaps signal an error. */ + + default: return EXT2_FT_REG_FILE; + } +} + /* Find an object in the given directory with the given name in the filesystem image, updating the name reference to refer to the next component. */ @@ -348,6 +404,83 @@ sizeof(inode)); } +/* Rename a file. */ + +errcode_t image_rename(ext2_filsys fs, ext2_ino_t source, + ext2_ino_t source_parent, const char *source_basename, + ext2_ino_t target_parent, const char *target_basename) +{ + errcode_t retval; + struct ext2_inode source_inode, source_parent_inode, target_parent_inode; + struct _image_parent_entry_state state = {target_parent}; + + /* NOTE: Should check for space. */ + + /* Obtain the source object. */ + + retval = ext2fs_read_inode(fs, source, &source_inode); + + if (retval) + return retval; + + /* Link from the target parent. */ + + retval = _image_link(fs, target_parent, target_basename, source, + image_file_type(source_inode.i_mode)); + + if (retval) + return retval; + + if (_image_isdir(fs, source)) + { + /* Update the link count for the target. */ + + retval = ext2fs_read_inode(fs, target_parent, &target_parent_inode); + + if (retval) + return retval; + + target_parent_inode.i_links_count++; + + retval = ext2fs_write_inode(fs, target_parent, &target_parent_inode); + + if (retval) + return retval; + + /* A directory needs its .. entry updating to refer to its new + parent. */ + + retval = ext2fs_dir_iterate(fs, source, 0, NULL, + _image_update_parent_entry, &state); + + /* Update the link count for the source. */ + + retval = ext2fs_read_inode(fs, source_parent, &source_parent_inode); + + if (retval) + return retval; + + source_parent_inode.i_links_count--; + + retval = ext2fs_write_inode(fs, source_parent, &source_parent_inode); + + if (retval) + return retval; + } + + /* Unlink from the source parent, doing so by name because the file is now + already linked from the target parent, and when the parents are the same, + unlinking by inode could just cause the file to disappear from the + catalogue. */ + + retval = image_unlink_by_name(fs, source_parent, source_basename); + + if (retval) + return retval; + + return ext2fs_flush2(fs, 0); +} + /* Set the mode, user and group metadata for a file. */ void image_set_metadata(struct ext2_inode *inode, int clean, __u16 mode, @@ -396,6 +529,8 @@ errcode_t image_unlink_by_name(ext2_filsys fs, ext2_ino_t ino_parent, const char *basename) { + /* NOTE: This might do more work for a directory. */ + return ext2fs_unlink(fs, ino_parent, basename, 0, 0); } @@ -404,6 +539,8 @@ errcode_t image_unlink_by_inode(ext2_filsys fs, ext2_ino_t ino_parent, ext2_ino_t ino) { + /* NOTE: This might do more work for a directory. */ + return ext2fs_unlink(fs, ino_parent, 0, ino, 0); } @@ -448,3 +585,6 @@ return _image_isfile(fs, ino); } + +/* vim: tabstop=4 expandtab shiftwidth=4 +*/ diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsclient/include/fsclient/client.h --- a/libfsclient/include/fsclient/client.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsclient/include/fsclient/client.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * Filesystem client functions. * - * Copyright (C) 2018, 2019, 2020, 2021 Paul Boddie + * Copyright (C) 2018, 2019, 2020, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -52,6 +52,9 @@ long client_remove(const char *name); long client_remove_using(const char *name, l4_cap_idx_t server); +long client_rename(const char *source, const char *target); +long client_rename_using(const char *source, const char *target, l4_cap_idx_t server); + /* File and region operations. */ long client_flush(file_t *file); diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsclient/include/fsclient/file.h --- a/libfsclient/include/fsclient/file.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsclient/include/fsclient/file.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * File access convenience functions and types. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -85,12 +85,14 @@ void file_close(file_t *file); long file_open(file_t *file, const char *filename, flags_t flags, l4_cap_idx_t server); long file_remove(const char *filename, l4_cap_idx_t server); +long file_rename(const char *source, const char *target, l4_cap_idx_t server); /* File lifecycle operations. */ long file_context(file_t *file, l4_cap_idx_t server); long file_context_open(file_t *file, flags_t flags, file_t *context); long file_context_remove(file_t *context); +long file_context_rename(file_t *context); void file_init(file_t *file); /* File and region operations. */ diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsclient/lib/src/client.cc --- a/libfsclient/lib/src/client.cc Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsclient/lib/src/client.cc Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * Filesystem client functions. * - * Copyright (C) 2018, 2019, 2020, 2021 Paul Boddie + * Copyright (C) 2018, 2019, 2020, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -377,6 +377,24 @@ +/* Rename a file in the filesystem. */ + +long client_rename(const char *source, const char *target) +{ + l4_cap_idx_t server = l4re_env_get_cap("server"); + + return client_rename_using(source, target, server); +} + +/* Rename a file in the filesystem via a named capability. */ + +long client_rename_using(const char *source, const char *target, l4_cap_idx_t server) +{ + return file_rename(source, target, server); +} + + + /* Obtain the current region of a pipe. */ long client_current_region(file_t *file) diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsclient/lib/src/file.cc --- a/libfsclient/lib/src/file.cc Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsclient/lib/src/file.cc Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * File access convenience functions. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -155,7 +155,35 @@ err = file_context_remove(&context); /* Close the context, although a separate mechanism could permit contexts to - open several files. */ + remove several files. */ + + file_close(&context); + return err; +} + +/* Rename an object in the filesystem. This is a convenience function invoking + file_context and file_context_rename. */ + +long file_rename(const char *source, const char *target, l4_cap_idx_t server) +{ + file_t context; + offset_t written; + long err; + + err = file_context(&context, server); + if (err) + return err; + + if (!file_string_set(&context, source, 0, &written)) + return -L4_ENOMEM; + + if (!file_string_set(&context, target, written + 1, NULL)) + return -L4_ENOMEM; + + err = file_context_rename(&context); + + /* Close the context, although a separate mechanism could permit contexts to + rename several files. */ file_close(&context); return err; @@ -211,6 +239,14 @@ return openercontext.remove(); } +/* Rename a file using the given context. */ + +long file_context_rename(file_t *context) +{ + client_OpenerContext openercontext(context->ref); + return openercontext.rename(); +} + /* Flush populated data and obtain an updated file size and populated data diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/include/fsserver/ext2_file_opener.h --- a/libfsserver/include/fsserver/ext2_file_opener.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/include/fsserver/ext2_file_opener.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * An opener for a file provided by an Ext2-compatible filesystem. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -65,6 +65,9 @@ virtual long remove_object(const char *path, fileid_t fileid); + virtual long rename_object(const char *source, fileid_t source_fileid, + const char *target); + virtual long unlink_object(const char *path, fileid_t fileid); }; diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/include/fsserver/ext2_file_operations.h --- a/libfsserver/include/fsserver/ext2_file_operations.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/include/fsserver/ext2_file_operations.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * File operations supporting an Ext2-compatible filesystem. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -72,6 +72,10 @@ long remove(ext2_ino_t ino); + long rename(ext2_ino_t source, + ext2_ino_t source_parent, const char *source_basename, + ext2_ino_t target_parent, const char *target_basename); + long unlink(ext2_ino_t ino_parent, ext2_ino_t ino); /* File property methods. */ diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/include/fsserver/file_opening.h --- a/libfsserver/include/fsserver/file_opening.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/include/fsserver/file_opening.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * Generic support for opening files. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -37,6 +37,8 @@ virtual long get_fileid(const char *path, flags_t flags, fileid_t *fileid) = 0; + virtual long get_parent(const char *path, fileid_t *fileid); + virtual long make_accessor(const char *path, flags_t flags, fileid_t fileid, Accessor **accessor) = 0; @@ -46,6 +48,9 @@ virtual long remove_object(const char *path, fileid_t fileid) = 0; + virtual long rename_object(const char *source, fileid_t source_fileid, + const char *target) = 0; + virtual long unlink_object(const char *path, fileid_t fileid) = 0; }; diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/include/fsserver/host_file_opener.h --- a/libfsserver/include/fsserver/host_file_opener.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/include/fsserver/host_file_opener.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * An opener for a "host" file provided via the C library. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -75,6 +75,9 @@ virtual long remove_object(const char *path, fileid_t fileid); + virtual long rename_object(const char *source, fileid_t source_fileid, + const char *target); + virtual long unlink_object(const char *path, fileid_t fileid); }; diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/include/fsserver/opener_context_resource.h --- a/libfsserver/include/fsserver/opener_context_resource.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/include/fsserver/opener_context_resource.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * A context resource offering support for opening files. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -53,7 +53,7 @@ /* Data access methods. */ - char *get_path(); + char *get_path(offset_t offset=0); /* Opener context interface methods. */ @@ -62,6 +62,8 @@ long remove(); + long rename(); + /* Pager/dataspace methods. */ long map(unsigned long offset, address_t hot_spot, map_flags_t flags, diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/include/fsserver/opener_resource.h --- a/libfsserver/include/fsserver/opener_resource.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/include/fsserver/opener_resource.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * A resource offering support for creating contexts and opening files. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -36,10 +36,6 @@ protected: ResourceRegistry *_registry; - /* Retrieval methods. */ - - virtual long get_parent(const char *path, fileid_t *fileid); - /* Notification methods. */ virtual long notify_parent(const char *path); @@ -65,6 +61,8 @@ long remove(const char *path); + long rename(const char *source, const char *target); + /* Opener interface methods. */ long context(l4_cap_idx_t *context); diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/include/fsserver/resource_registry.h --- a/libfsserver/include/fsserver/resource_registry.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/include/fsserver/resource_registry.h Thu Jan 27 23:45:17 2022 +0100 @@ -53,6 +53,11 @@ flags_t flags, fileid_t fileid, Provider **provider); + /* Provider manipulation methods. */ + + long _remove_provider(FileOpening *opening, const char *path, + fileid_t fileid); + public: explicit ResourceRegistry(Pages *pages); @@ -70,6 +75,8 @@ /* Provider manipulation methods. */ long remove_provider(FileOpening *opening, const char *path); + + long rename_provider(FileOpening *opening, const char *source, const char *target); }; // vim: tabstop=4 expandtab shiftwidth=4 diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/include/fsserver/test_file_opener.h --- a/libfsserver/include/fsserver/test_file_opener.h Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/include/fsserver/test_file_opener.h Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * An opener for a test file containing generated content. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -56,6 +56,9 @@ virtual long remove_object(const char *path, fileid_t fileid); + virtual long rename_object(const char *source, fileid_t source_fileid, + const char *target); + virtual long unlink_object(const char *path, fileid_t fileid); }; diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/lib/Makefile --- a/libfsserver/lib/Makefile Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/lib/Makefile Thu Jan 27 23:45:17 2022 +0100 @@ -56,6 +56,7 @@ files/ext2_file_opener.cc \ files/ext2_file_operations.cc \ files/ext2_filesystem.cc \ + files/file_opening.cc \ files/file_pager.cc \ files/file_provider.cc \ files/filesystem_resource.cc \ diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/lib/files/ext2_file_opener.cc --- a/libfsserver/lib/files/ext2_file_opener.cc Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/lib/files/ext2_file_opener.cc Thu Jan 27 23:45:17 2022 +0100 @@ -129,6 +129,31 @@ return _ops->remove((ext2_ino_t) fileid); } +/* Rename a filesystem object, placing source inside the parent of target. */ + +long Ext2FileOpener::rename_object(const char *source, fileid_t source_fileid, + const char *target) +{ + (void) source; + + fileid_t source_parent, target_parent; + long err; + + err = get_parent(source, &source_parent); + + if (err) + return err; + + err = get_parent(target, &target_parent); + + if (err) + return err; + + return _ops->rename((ext2_ino_t) source_fileid, + (ext2_ino_t) source_parent, path_basename(source), + (ext2_ino_t) target_parent, path_basename(target)); +} + /* Unlink a filesystem object. */ long Ext2FileOpener::unlink_object(const char *path, fileid_t fileid) diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/lib/files/ext2_file_operations.cc --- a/libfsserver/lib/files/ext2_file_operations.cc Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/lib/files/ext2_file_operations.cc Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * File operations performed by an Ext2-compatible filesystem. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -129,6 +129,25 @@ return L4_EOK; } +/* Rename an object. */ + +long Ext2FileOperations::rename(ext2_ino_t source, + ext2_ino_t source_parent, const char *source_basename, + ext2_ino_t target_parent, const char *target_basename) +{ + std::lock_guard guard(_lock); + + errcode_t retval = image_rename(_fs, source, source_parent, source_basename, + target_parent, target_basename); + + // NOTE: Map error conditions. + + if (retval) + return -L4_EIO; + + return L4_EOK; +} + /* Unlink an object from a directory. */ long Ext2FileOperations::unlink(ext2_ino_t ino_parent, ext2_ino_t ino) @@ -145,6 +164,8 @@ return L4_EOK; } + + /* Obtain the size of a file. */ offset_t Ext2FileOperations::get_size(ext2_file_t file) diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/lib/files/file_opening.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfsserver/lib/files/file_opening.cc Thu Jan 27 23:45:17 2022 +0100 @@ -0,0 +1,54 @@ +/* + * File opening support. + * + * Copyright (C) 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "file_opening.h" + + + +/* Obtain the identifier for any active parent directory for a path. */ + +long FileOpening::get_parent(const char *path, fileid_t *fileid) +{ + char *sep = strrchr(path, (int) '/'); + long err; + + /* For top-level paths, use the empty string to get the root directory. + Otherwise, obtain the parent directory path to obtain the file + identifier. */ + + if (sep == NULL) + err = get_fileid("", 0, fileid); + else + { + char *parent = strndup(path, sep - path + 1); + + parent[sep - path] = '\0'; + err = get_fileid(parent, 0, fileid); + free(parent); + } + + return err; +} + +// vim: tabstop=4 expandtab shiftwidth=4 diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/lib/files/host_file_opener.cc --- a/libfsserver/lib/files/host_file_opener.cc Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/lib/files/host_file_opener.cc Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * An opener for a "host" file provided via the C library. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -19,6 +19,7 @@ * Boston, MA 02110-1301, USA */ +#include #include #include @@ -140,6 +141,21 @@ return L4_EOK; } +/* Rename a filesystem object, placing source inside the parent of target. */ + +long HostFileOpener::rename_object(const char *source, fileid_t source_fileid, + const char *target) +{ + (void) source_fileid; + + /* NOTE: Should propagate a more meaningful error. */ + + if (rename(source, target)) + return -L4_EIO; + + return L4_EOK; +} + /* Unlink a filesystem object. */ long HostFileOpener::unlink_object(const char *path, fileid_t fileid) diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/lib/files/opener_context_resource.cc --- a/libfsserver/lib/files/opener_context_resource.cc Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/lib/files/opener_context_resource.cc Thu Jan 27 23:45:17 2022 +0100 @@ -50,9 +50,9 @@ /* Data access methods. */ -char *OpenerContextResource::get_path() +char *OpenerContextResource::get_path(offset_t offset) { - char *buffer = _region->read(); + char *buffer = _region->read(offset); offset_t size = _region->size(); /* Confine the path to the limit of the buffer. */ @@ -88,4 +88,21 @@ return _opener->remove(path); } +long OpenerContextResource::rename() +{ + char *source, *target; + + source = get_path(); + + if (source == NULL) + return -L4_EINVAL; + + target = get_path(strlen(source) + 1); + + if (target == NULL) + return -L4_EINVAL; + + return _opener->rename(source, target); +} + // vim: tabstop=4 expandtab shiftwidth=4 diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/lib/files/opener_resource.cc --- a/libfsserver/lib/files/opener_resource.cc Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/lib/files/opener_resource.cc Thu Jan 27 23:45:17 2022 +0100 @@ -1,7 +1,7 @@ /* * A resource offering support for creating contexts and opening files. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -19,8 +19,6 @@ * Boston, MA 02110-1301, USA */ -#include - #include "opener_server.h" #include "opener_resource.h" #include "resource_server.h" @@ -48,31 +46,6 @@ -/* Obtain the identifier for any active parent directory for a path. */ - -long OpenerResource::get_parent(const char *path, fileid_t *fileid) -{ - char *sep = strrchr(path, (int) '/'); - long err; - - /* For top-level paths, use the empty string to get the root directory. - Otherwise, obtain the parent directory path to obtain the file - identifier. */ - - if (sep == NULL) - err = get_fileid("", 0, fileid); - else - { - char *parent = strndup(path, sep - path + 1); - - parent[sep - path] = '\0'; - err = get_fileid(parent, 0, fileid); - free(parent); - } - - return err; -} - /* Obtain any active parent directory for a path, notifying its subscribers of the file opening event. NOTE: This might need to happen in the registry due to concurrency issues. */ @@ -132,6 +105,13 @@ return _registry->remove_provider(this, path); } +/* Request the renaming of the named filesystem object. */ + +long OpenerResource::rename(const char *source, const char *target) +{ + return _registry->rename_provider(this, source, target); +} + /* Opener interface methods. */ diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/lib/files/test_file_opener.cc --- a/libfsserver/lib/files/test_file_opener.cc Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/lib/files/test_file_opener.cc Thu Jan 27 23:45:17 2022 +0100 @@ -87,6 +87,15 @@ return L4_EOK; } +/* Rename a filesystem object, placing source inside the parent of target. */ + +long TestFileOpener::rename_object(const char *source, fileid_t source_fileid, + const char *target) +{ + (void) source; (void) source_fileid; (void) target; + return -L4_EIO; +} + /* Unlink a filesystem object. */ long TestFileOpener::unlink_object(const char *path, fileid_t fileid) diff -r 472c1e2b0302 -r b769dcb3f4b3 libfsserver/lib/generic/resource_registry.cc --- a/libfsserver/lib/generic/resource_registry.cc Fri Jan 14 01:10:35 2022 +0100 +++ b/libfsserver/lib/generic/resource_registry.cc Thu Jan 27 23:45:17 2022 +0100 @@ -184,10 +184,16 @@ if (err) return err; + return _remove_provider(opening, path, fileid); +} + +long ResourceRegistry::_remove_provider(FileOpening *opening, const char *path, + fileid_t fileid) +{ /* Obtain a provider for the object. */ Provider *provider; - err = find_provider(fileid, &provider); + long err = find_provider(fileid, &provider); if (err && (err != -L4_ENOENT)) return err; @@ -210,4 +216,94 @@ return L4_EOK; } +/* Request the renaming of a filesystem object. */ + +long ResourceRegistry::rename_provider(FileOpening *opening, const char *source, + const char *target) +{ + std::lock_guard guard(_lock); + + long err; + fileid_t source_fileid, target_fileid; + + /* Obtain an identifier for any recognised object. */ + + err = opening->get_fileid(source, 0, &source_fileid); + + if (err) + return err; + + /* If source is a directory, the target must be a new directory or an empty + directory. An empty directory will be replaced by the source + directory. */ + + if (opening->accessing_directory(source, 0, source_fileid)) + { + err = opening->get_fileid(target, 0, &target_fileid); + + if (err && (err != -L4_ENOENT)) + return err; + + /* Any existing directory must be empty. */ + + if (!err) + { + /* NOTE: Should really use the equivalent of ENOTDIR. */ + + if (!opening->accessing_directory(target, 0, target_fileid)) + return -L4_EIO; + + /* NOTE: Test for empty directory. */ + + /* The existing directory will be removed. */ + + err = _remove_provider(opening, target, target_fileid); + + if (err) + return err; + } + + /* The source directory is moved within the filesystem to the parent of + the indicated target. */ + + return opening->rename_object(source, source_fileid, target); + } + + /* If source is a file, the target must be a new or existing file, not a + directory. An existing file will be replaced. */ + + else if (opening->accessing_file(source, 0, source_fileid)) + { + err = opening->get_fileid(target, 0, &target_fileid); + + if (err && (err != -L4_ENOENT)) + return err; + + if (!err) + { + /* NOTE: Should really use the equivalent of EISDIR. */ + + if (!opening->accessing_file(target, 0, target_fileid)) + return -L4_EIO; + + /* The existing file will be removed. */ + + err = _remove_provider(opening, target, target_fileid); + + if (err) + return err; + } + + /* The source file is moved within the filesystem to the parent of the + indicated target. */ + + return opening->rename_object(source, source_fileid, target); + } + + /* NOTE: Other object types are to be supported. */ + + else + return -L4_EIO; +} + // vim: tabstop=4 expandtab shiftwidth=4 diff -r 472c1e2b0302 -r b769dcb3f4b3 libsystypes/idl/opener_context.idl --- a/libsystypes/idl/opener_context.idl Fri Jan 14 01:10:35 2022 +0100 +++ b/libsystypes/idl/opener_context.idl Thu Jan 27 23:45:17 2022 +0100 @@ -23,6 +23,11 @@ /* Remove a file using a path written to the context's dataspace. */ [opcode(14)] void remove(); + + /* Rename a file within a filesystem using paths written to the context's + dataspace. */ + + [opcode(15)] void rename(); }; /* vim: tabstop=2 expandtab shiftwidth=2 diff -r 472c1e2b0302 -r b769dcb3f4b3 tests/Makefile --- a/tests/Makefile Fri Jan 14 01:10:35 2022 +0100 +++ b/tests/Makefile Thu Jan 27 23:45:17 2022 +0100 @@ -8,6 +8,7 @@ dstest_file_monitor \ dstest_file_readdir \ dstest_file_readdir_concurrent \ + dstest_file_rename \ dstest_host_client \ dstest_pipe_client \ dstest_test_client @@ -28,6 +29,8 @@ SRC_CC_dstest_file_readdir_concurrent = dstest_file_readdir_concurrent.cc +SRC_CC_dstest_file_rename = dstest_file_rename.cc + SRC_CC_dstest_host_client = dstest_host_client.cc SRC_CC_dstest_pipe_client = dstest_pipe_client.cc diff -r 472c1e2b0302 -r b769dcb3f4b3 tests/dstest_file_rename.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dstest_file_rename.cc Thu Jan 27 23:45:17 2022 +0100 @@ -0,0 +1,161 @@ +/* + * Test renaming operations. + * + * Copyright (C) 2020, 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#include +#include +#include + +#include +#include + + + +static file_t *open_directory(char *filename, bool have_uid, sys_uid_t uid) +{ + /* With a user, open a user-specific file opener. */ + + if (have_uid) + { + l4_cap_idx_t opener = client_open_for_user((user_t) {uid, uid, 0022}); + + if (l4_is_invalid_cap(opener)) + { + printf("Could not obtain opener for file.\n"); + return NULL; + } + + /* Invoke the open method to receive the file reference. */ + + return client_opendir_using(filename, opener); + } + else + { + return client_opendir(filename); + } +} + +static long rename_file(char *source, char *target, bool have_uid, sys_uid_t uid) +{ + /* With a user, open a user-specific file opener. */ + + if (have_uid) + { + l4_cap_idx_t opener = client_open_for_user((user_t) {uid, uid, 0022}); + + if (l4_is_invalid_cap(opener)) + { + printf("Could not obtain opener for file.\n"); + return -L4_EIO; + } + + /* Invoke the rename method to rename the file. */ + + return client_rename_using(source, target, opener); + } + else + { + return client_rename(source, target); + } +} + + + +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + printf("Need a directory name and an optional user identifier (if used with a filesystem).\n"); + return 1; + } + + char *filename = argv[1]; + bool have_uid = (argc > 2) && strlen(argv[2]); + sys_uid_t uid = have_uid ? atoi(argv[2]) : 0; + + printf("Opening %s...\n", filename); + + file_t *reader = open_directory(filename, have_uid, uid); + + if (reader == NULL) + { + printf("Could not read from directory.\n"); + return 1; + } + + printf("Reading...\n"); + + struct dirent *dirent; + + while ((dirent = client_readdir(reader)) != NULL) + { + printf("> %s\n", dirent->d_name); + free(dirent); + } + + printf("Directory shown.\n"); + + /* Rename some files. */ + + char source[strlen(filename) + strlen("/file-XXXX.txt") + 10]; + char target[strlen(filename) + strlen("/file-XXXX.txt") + 10]; + int filenum; + + for (filenum = 1; filenum < 100; filenum++) + { + sprintf(source, "%s/file-%d.txt", filename, filenum); + sprintf(target, "%s/renamed-%d.txt", filename, filenum); + + long err = rename_file(source, target, have_uid, uid); + + if (err) + { + printf("Could not rename file: %s\n", source); + return 1; + } + } + + /* Show the new listing. */ + + reader = open_directory(filename, have_uid, uid); + + if (reader == NULL) + { + printf("Could not read from directory.\n"); + return 1; + } + + printf("Reading...\n"); + + while ((dirent = client_readdir(reader)) != NULL) + { + printf("> %s\n", dirent->d_name); + free(dirent); + } + + printf("Directory shown.\n"); + + return 0; +} + +// vim: tabstop=2 expandtab shiftwidth=2