LCOV - code coverage report
Current view: top level - src/client - gkfs_functions.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 456 618 73.8 %
Date: 2024-04-23 00:09:24 Functions: 30 32 93.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain
       3             :   Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany
       4             : 
       5             :   This software was partially supported by the
       6             :   EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).
       7             : 
       8             :   This software was partially supported by the
       9             :   ADA-FS project under the SPPEXA project funded by the DFG.
      10             : 
      11             :   This file is part of GekkoFS' POSIX interface.
      12             : 
      13             :   GekkoFS' POSIX interface is free software: you can redistribute it and/or
      14             :   modify it under the terms of the GNU Lesser General Public License as
      15             :   published by the Free Software Foundation, either version 3 of the License,
      16             :   or (at your option) any later version.
      17             : 
      18             :   GekkoFS' POSIX interface is distributed in the hope that it will be useful,
      19             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      20             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      21             :   GNU Lesser General Public License for more details.
      22             : 
      23             :   You should have received a copy of the GNU Lesser General Public License
      24             :   along with GekkoFS' POSIX interface.  If not, see
      25             :   <https://www.gnu.org/licenses/>.
      26             : 
      27             :   SPDX-License-Identifier: LGPL-3.0-or-later
      28             : */
      29             : 
      30             : #include <config.hpp>
      31             : #include <client/preload.hpp>
      32             : #include <client/preload_util.hpp>
      33             : #include <client/logging.hpp>
      34             : #include <client/gkfs_functions.hpp>
      35             : #include <client/rpc/forward_metadata.hpp>
      36             : #include <client/rpc/forward_data.hpp>
      37             : #include <client/open_dir.hpp>
      38             : 
      39             : #include <common/path_util.hpp>
      40             : 
      41             : extern "C" {
      42             : #include <dirent.h> // used for file types in the getdents{,64}() functions
      43             : #include <linux/kernel.h> // used for definition of alignment macros
      44             : #include <sys/statfs.h>
      45             : #include <sys/statvfs.h>
      46             : }
      47             : 
      48             : using namespace std;
      49             : 
      50             : /*
      51             :  * Macro used within getdents{,64} functions.
      52             :  * __ALIGN_KERNEL defined in linux/kernel.h
      53             :  */
      54             : #define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
      55             : 
      56             : /*
      57             :  * linux_dirent is used in getdents() but is privately defined in the linux
      58             :  * kernel: fs/readdir.c.
      59             :  */
      60             : struct linux_dirent {
      61             :     unsigned long d_ino;
      62             :     unsigned long d_off;
      63             :     unsigned short d_reclen;
      64             :     char d_name[1];
      65             : };
      66             : /*
      67             :  * linux_dirent64 is used in getdents64() and defined in the linux kernel:
      68             :  * include/linux/dirent.h. However, it is not part of the kernel-headers and
      69             :  * cannot be imported.
      70             :  */
      71             : struct linux_dirent64 {
      72             :     uint64_t d_ino;
      73             :     int64_t d_off;
      74             :     unsigned short d_reclen;
      75             :     unsigned char d_type;
      76             :     char d_name[1]; // originally `char d_name[0]` in kernel, but ISO C++
      77             :                     // forbids zero-size array 'd_name'
      78             : };
      79             : 
      80             : struct dirent_extended {
      81             :     size_t size;
      82             :     time_t ctime;
      83             :     unsigned short d_reclen;
      84             :     unsigned char d_type;
      85             :     char d_name[1]; // originally `char d_name[0]` in kernel, but ISO C++
      86             :                     // forbids zero-size array 'd_name'
      87             : };
      88             : 
      89             : 
      90             : namespace {
      91             : 
      92             : /**
      93             :  * Checks if metadata for parent directory exists (can be disabled with
      94             :  * GKFS_CREATE_CHECK_PARENTS). errno may be set
      95             :  * @param path
      96             :  * @return 0 on success, -1 on failure
      97             :  */
      98             : int
      99        1064 : check_parent_dir(const std::string& path) {
     100             : #if GKFS_CREATE_CHECK_PARENTS
     101        2128 :     auto p_comp = gkfs::path::dirname(path);
     102        2128 :     auto md = gkfs::utils::get_metadata(p_comp);
     103        1064 :     if(!md) {
     104           1 :         if(errno == ENOENT) {
     105           1 :             LOG(DEBUG, "Parent component does not exist: '{}'", p_comp);
     106             :         } else {
     107           0 :             LOG(ERROR, "Failed to get metadata for parent component '{}': {}",
     108           1 :                 path, strerror(errno));
     109             :         }
     110           1 :         return -1;
     111             :     }
     112        1063 :     if(!S_ISDIR(md->mode())) {
     113           1 :         LOG(DEBUG, "Parent component is not a directory: '{}'", p_comp);
     114           1 :         errno = ENOTDIR;
     115           1 :         return -1;
     116             :     }
     117             : #endif // GKFS_CREATE_CHECK_PARENTS
     118             :     return 0;
     119             : }
     120             : } // namespace
     121             : 
     122             : namespace gkfs::syscall {
     123             : 
     124             : /**
     125             :  * gkfs wrapper for open() system calls
     126             :  * errno may be set
     127             :  * @param path
     128             :  * @param mode
     129             :  * @param flags
     130             :  * @return 0 on success, -1 on failure
     131             :  */
     132             : int
     133        1167 : gkfs_open(const std::string& path, mode_t mode, int flags) {
     134             : 
     135        1167 :     if(flags & O_PATH) {
     136           1 :         LOG(ERROR, "`O_PATH` flag is not supported");
     137           1 :         errno = ENOTSUP;
     138           1 :         return -1;
     139             :     }
     140             : 
     141             :     // metadata object filled during create or stat
     142        2333 :     gkfs::metadata::Metadata md{};
     143        1166 :     if(flags & O_CREAT) {
     144        1048 :         if(flags & O_DIRECTORY) {
     145           1 :             LOG(ERROR, "O_DIRECTORY use with O_CREAT. NOT SUPPORTED");
     146           1 :             errno = ENOTSUP;
     147           1 :             return -1;
     148             :         }
     149             :         // no access check required here. If one is using our FS they have the
     150             :         // permissions.
     151        1047 :         auto err = gkfs_create(path, mode | S_IFREG);
     152        1047 :         if(err) {
     153           5 :             if(errno == EEXIST) {
     154             :                 // file exists, O_CREAT was set
     155           3 :                 if(flags & O_EXCL) {
     156             :                     // File exists and O_EXCL & O_CREAT was set
     157           1 :                     return -1;
     158             :                 }
     159             :                 // file exists, O_CREAT was set O_EXCL wasnt, so function does
     160             :                 // not fail this case is actually undefined as per `man 2 open`
     161           4 :                 auto md_ = gkfs::utils::get_metadata(path);
     162           2 :                 if(!md_) {
     163           0 :                     LOG(ERROR,
     164             :                         "Could not get metadata after creating file '{}': '{}'",
     165           0 :                         path, strerror(errno));
     166           1 :                     return -1;
     167             :                 }
     168           2 :                 md = *md_;
     169             : #ifdef HAS_RENAME
     170             :                 // This is an old file that was renamed which we do not open
     171           2 :                 if(md.blocks() == -1) {
     172           0 :                     LOG(DEBUG,
     173             :                         "This file was renamed and we do not open. path '{}'",
     174           0 :                         path);
     175           0 :                     return -1;
     176             :                 }
     177             : #endif // HAS_RENAME
     178             :             } else {
     179           2 :                 LOG(ERROR, "Error creating file: '{}'", strerror(errno));
     180           2 :                 return -1;
     181             :             }
     182             :         } else {
     183             :             // file was successfully created. Add to filemap
     184        2084 :             return CTX->file_map()->add(
     185        2084 :                     std::make_shared<gkfs::filemap::OpenFile>(path, flags));
     186             :         }
     187             :     } else {
     188         235 :         auto md_ = gkfs::utils::get_metadata(path);
     189         118 :         if(!md_) {
     190           1 :             if(errno != ENOENT) {
     191           0 :                 LOG(ERROR, "Error stating existing file '{}'", path);
     192             :             }
     193             :             // file doesn't exist and O_CREAT was not set
     194           1 :             return -1;
     195             :         }
     196         117 :         md = *md_;
     197             :     }
     198             : 
     199             : 
     200             : #ifdef HAS_SYMLINKS
     201         119 :     if(md.is_link()) {
     202           0 :         if(flags & O_NOFOLLOW) {
     203           0 :             LOG(WARNING, "Symlink found and O_NOFOLLOW flag was specified");
     204           0 :             errno = ELOOP;
     205           0 :             return -1;
     206             :         }
     207           0 :         return gkfs_open(md.target_path(), mode, flags);
     208             :     }
     209             : #ifdef HAS_RENAME
     210        1285 :     auto new_path = path;
     211         119 :     if(md.blocks() == -1) {
     212             :         // This is an old file that was renamed and essentially no longer exists
     213           0 :         errno = ENOENT;
     214           0 :         return -1;
     215             :     } else {
     216         119 :         if(!md.target_path().empty()) {
     217             :             // get renamed path from target and retrieve metadata from it
     218          24 :             auto md_ = gkfs::utils::get_metadata(md.target_path());
     219          12 :             new_path = md.target_path();
     220          18 :             while(!md_.value().target_path().empty()) {
     221           6 :                 new_path = md_.value().target_path();
     222          12 :                 md_ = gkfs::utils::get_metadata(md_.value().target_path(),
     223          12 :                                                 false);
     224           6 :                 if(!md_) {
     225             :                     return -1;
     226             :                 }
     227             :             }
     228          12 :             md = *md_;
     229          12 :             if(S_ISDIR(md.mode())) {
     230           0 :                 return gkfs_opendir(new_path);
     231             :             }
     232             : 
     233             :             /*** Regular file exists ***/
     234          12 :             assert(S_ISREG(md.mode()));
     235             : 
     236          12 :             if((flags & O_TRUNC) && ((flags & O_RDWR) || (flags & O_WRONLY))) {
     237           0 :                 if(gkfs_truncate(new_path, md.size(), 0)) {
     238           0 :                     LOG(ERROR, "Error truncating file");
     239           0 :                     return -1;
     240             :                 }
     241             :             }
     242             : 
     243          24 :             return CTX->file_map()->add(
     244          24 :                     std::make_shared<gkfs::filemap::OpenFile>(new_path, flags));
     245             :         }
     246             :     }
     247             : #endif // HAS_RENAME
     248             : #endif // HAS_SYMLINKS
     249         107 :     if(S_ISDIR(md.mode())) {
     250          25 :         return gkfs_opendir(path);
     251             :     }
     252             : 
     253             :     /*** Regular file exists ***/
     254          82 :     assert(S_ISREG(md.mode()));
     255             : 
     256          82 :     if((flags & O_TRUNC) && ((flags & O_RDWR) || (flags & O_WRONLY))) {
     257           2 :         if(gkfs_truncate(path, md.size(), 0)) {
     258           0 :             LOG(ERROR, "Error truncating file");
     259           0 :             return -1;
     260             :         }
     261             :     }
     262             : 
     263         164 :     return CTX->file_map()->add(
     264         164 :             std::make_shared<gkfs::filemap::OpenFile>(path, flags));
     265             : }
     266             : 
     267             : /**
     268             :  * Wrapper function for file/directory creation
     269             :  * errno may be set
     270             :  * @param path
     271             :  * @param mode
     272             :  * @return 0 on success, -1 on failure
     273             :  */
     274             : int
     275        1064 : gkfs_create(const std::string& path, mode_t mode) {
     276             : 
     277             :     // file type must be set
     278        1064 :     switch(mode & S_IFMT) {
     279           0 :         case 0:
     280           0 :             mode |= S_IFREG;
     281           0 :             break;
     282             :         case S_IFREG: // intentionally fall-through
     283             :         case S_IFDIR:
     284             :             break;
     285           0 :         case S_IFCHR: // intentionally fall-through
     286           0 :         case S_IFBLK:
     287           0 :         case S_IFIFO:
     288           0 :         case S_IFSOCK:
     289           0 :             LOG(WARNING, "Unsupported node type");
     290           0 :             errno = ENOTSUP;
     291           0 :             return -1;
     292           0 :         default:
     293           0 :             LOG(WARNING, "Unrecognized node type");
     294           0 :             errno = EINVAL;
     295           0 :             return -1;
     296             :     }
     297             : 
     298        1064 :     if(check_parent_dir(path)) {
     299             :         return -1;
     300             :     }
     301             :     // Write to all replicas, at least one need to success
     302             :     bool success = false;
     303        2124 :     for(auto copy = 0; copy < CTX->get_replicas() + 1; copy++) {
     304        1062 :         auto err = gkfs::rpc::forward_create(path, mode, copy);
     305        1062 :         if(err) {
     306           3 :             errno = err;
     307             :         } else {
     308        1059 :             success = true;
     309        1059 :             errno = 0;
     310             :         }
     311             :     }
     312        1062 :     if(!success) {
     313           3 :         return -1;
     314             :     }
     315             : 
     316             :     return 0;
     317             : }
     318             : 
     319             : /**
     320             :  * gkfs wrapper for unlink() system calls
     321             :  * errno may be set
     322             :  * @param path
     323             :  * @return 0 on success, -1 on failure
     324             :  */
     325             : int
     326           7 : gkfs_remove(const std::string& path) {
     327          14 :     auto md = gkfs::utils::get_metadata(path);
     328           7 :     if(!md) {
     329             :         return -1;
     330             :     }
     331             : 
     332           6 :     if(S_ISDIR(md->mode())) {
     333           1 :         LOG(ERROR, "Cannot remove directory '{}'", path);
     334           1 :         errno = EISDIR;
     335           1 :         return -1;
     336             :     }
     337             : #ifdef HAS_SYMLINKS
     338             : #ifdef HAS_RENAME
     339           5 :     if(md.value().blocks() == -1) {
     340           1 :         errno = ENOENT;
     341           1 :         return -1;
     342             :     } else {
     343           4 :         if(!md.value().target_path().empty()) {
     344           2 :             auto md_ = gkfs::utils::get_metadata(md.value().target_path());
     345           2 :             std::string new_path = md.value().target_path();
     346           2 :             while(!md.value().target_path().empty()) {
     347           1 :                 new_path = md.value().target_path();
     348           3 :                 md = gkfs::utils::get_metadata(md.value().target_path(), false);
     349           1 :                 if(!md) {
     350           0 :                     return -1;
     351             :                 }
     352             :             }
     353           1 :             auto err = gkfs::rpc::forward_remove(new_path, CTX->get_replicas());
     354           1 :             if(err) {
     355           0 :                 errno = err;
     356           0 :                 return -1;
     357             :             }
     358             :         }
     359             :     }
     360             : #endif // HAS_RENAME
     361             : #endif // HAS_SYMLINKS
     362             : 
     363           4 :     auto err = gkfs::rpc::forward_remove(path, CTX->get_replicas());
     364           4 :     if(err) {
     365           0 :         errno = err;
     366           0 :         return -1;
     367             :     }
     368             :     return 0;
     369             : }
     370             : 
     371             : /**
     372             :  * gkfs wrapper for access() system calls
     373             :  * errno may be set
     374             :  * @param path
     375             :  * @param mask
     376             :  * @param follow_links
     377             :  * @return 0 on success, -1 on failure
     378             :  */
     379             : int
     380           7 : gkfs_access(const std::string& path, const int mask, bool follow_links) {
     381          14 :     auto md = gkfs::utils::get_metadata(path, follow_links);
     382           7 :     if(!md) {
     383           1 :         LOG(DEBUG, "File does not exist '{}'", path);
     384           1 :         return -1;
     385             :     }
     386             : #ifdef HAS_SYMLINKS
     387             : #ifdef HAS_RENAME
     388           6 :     LOG(DEBUG, "Checking for renamed file '{}'", path);
     389           6 :     if(md.value().blocks() == -1) {
     390           1 :         errno = ENOENT;
     391           1 :         LOG(DEBUG, "File exist but it is renamed '{}'", path);
     392           1 :         return -1;
     393             : 
     394             :     } else {
     395           6 :         while(!md.value().target_path().empty()) {
     396           2 :             LOG(DEBUG, "File exist but it is renamed '{} -> {}'", path,
     397           1 :                 md.value().target_path());
     398           3 :             md = gkfs::utils::get_metadata(md.value().target_path(), false);
     399           1 :             if(!md) {
     400           0 :                 LOG(DEBUG, "File does not exist but it is renamed '{} -> {}'",
     401           0 :                     path, md.value().target_path());
     402           0 :                 return -1;
     403             :             }
     404             :         }
     405             :     }
     406             : #endif // HAS_RENAME
     407             : #endif // HAS_SYMLINKS
     408             :     return 0;
     409             : }
     410             : 
     411             : #ifdef HAS_SYMLINKS
     412             : #ifdef HAS_RENAME
     413             : /**
     414             :  * gkfs wrapper for rename() system calls
     415             :  * errno may be set
     416             :  * We use blocks to determine if the file is a renamed file.
     417             :  * If the file is re-renamed (a->b->a) a recovers the block of b
     418             :  * and we delete b.
     419             :  * There is no support for replication in rename
     420             :  * @param old_path
     421             :  * @param new_path
     422             :  * @return 0 on success, -1 on failure
     423             :  */
     424             : int
     425          12 : gkfs_rename(const string& old_path, const string& new_path) {
     426          24 :     auto md_old = gkfs::utils::get_metadata(old_path, false);
     427             : 
     428             :     // if the file is not found, or it is a renamed one cancel.
     429          12 :     if(!md_old || md_old.value().blocks() == -1) {
     430           1 :         return -1;
     431             :     }
     432          23 :     auto md_new = gkfs::utils::get_metadata(new_path, false);
     433          11 :     if(md_new) {
     434             :         // the new file exists... check for circular...
     435           1 :         if(md_new.value().blocks() == -1 &&
     436           2 :            md_old.value().target_path() == new_path) {
     437             :             // the new file is a renamed file, so we need to get the metadata of
     438             :             // the original file.
     439           1 :             LOG(DEBUG, "Destroying Circular Rename '{}' --> '{}'", old_path,
     440           1 :                 new_path);
     441           1 :             gkfs::metadata::MetadentryUpdateFlags flags{};
     442           1 :             flags.atime = false;
     443           1 :             flags.mtime = false;
     444           1 :             flags.ctime = false;
     445           1 :             flags.blocks = true;
     446           1 :             flags.mode = false;
     447           1 :             flags.size = false;
     448           1 :             flags.uid = false;
     449           1 :             flags.gid = false;
     450           1 :             flags.link_count = false;
     451           1 :             md_old.value().blocks(0);
     452           2 :             md_old.value().target_path("");
     453             : 
     454           1 :             auto err = gkfs::rpc::forward_update_metadentry(
     455           1 :                     new_path, md_old.value(), flags, 0);
     456             : 
     457           1 :             if(err) {
     458           0 :                 errno = err;
     459           0 :                 return -1;
     460             :             }
     461             :             // Delete old file
     462           1 :             err = gkfs::rpc::forward_remove(old_path, CTX->get_replicas());
     463           1 :             if(err) {
     464           0 :                 errno = err;
     465           0 :                 return -1;
     466             :             }
     467             :             return 0;
     468             :         }
     469             :         return -1;
     470             :     }
     471             : 
     472          10 :     auto err = gkfs::rpc::forward_rename(old_path, new_path, md_old.value());
     473          10 :     if(err) {
     474           0 :         errno = err;
     475           0 :         return -1;
     476             :     }
     477             : 
     478             :     return 0;
     479             : }
     480             : #endif
     481             : #endif
     482             : 
     483             : /**
     484             :  * gkfs wrapper for stat() system calls
     485             :  * errno may be set
     486             :  * @param path
     487             :  * @param buf
     488             :  * @param follow_links
     489             :  * @return 0 on success, -1 on failure
     490             :  */
     491             : int
     492          68 : gkfs_stat(const string& path, struct stat* buf, bool follow_links) {
     493         136 :     auto md = gkfs::utils::get_metadata(path, follow_links);
     494          68 :     if(!md) {
     495             :         return -1;
     496             :     }
     497             : #ifdef HAS_SYMLINKS
     498             : #ifdef HAS_RENAME
     499          60 :     if(md.value().blocks() == -1) {
     500           7 :         errno = ENOENT;
     501           7 :         return -1;
     502             :     } else {
     503          65 :         while(!md.value().target_path().empty()) {
     504          36 :             md = gkfs::utils::get_metadata(md.value().target_path(), false);
     505          12 :             if(!md) {
     506             :                 return -1;
     507             :             }
     508             :         }
     509             :     }
     510             : #endif
     511             : #endif
     512          53 :     gkfs::utils::metadata_to_stat(path, *md, *buf);
     513             :     return 0;
     514             : }
     515             : 
     516             : #ifdef STATX_TYPE
     517             : 
     518             : /**
     519             :  * gkfs wrapper for statx() system calls
     520             :  * errno may be set
     521             :  * @param dirfs
     522             :  * @param path
     523             :  * @param flags
     524             :  * @param mask
     525             :  * @param buf
     526             :  * @param follow_links
     527             :  * @return 0 on success, -1 on failure
     528             :  */
     529             : int
     530           4 : gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask,
     531             :            struct statx* buf, bool follow_links) {
     532           8 :     auto md = gkfs::utils::get_metadata(path, follow_links);
     533             : 
     534           4 :     if(!md) {
     535             :         return -1;
     536             :     }
     537             : #ifdef HAS_SYMLINKS
     538             : #ifdef HAS_RENAME
     539           3 :     if(md.value().blocks() == -1) {
     540           0 :         errno = ENOENT;
     541           0 :         return -1;
     542             :     } else {
     543           3 :         while(!md.value().target_path().empty()) {
     544           0 :             md = gkfs::utils::get_metadata(md.value().target_path(), false);
     545           0 :             if(!md) {
     546             :                 return -1;
     547             :             }
     548             :         }
     549             :     }
     550             : #endif
     551             : #endif
     552           3 :     struct stat tmp {};
     553             : 
     554           3 :     gkfs::utils::metadata_to_stat(path, *md, tmp);
     555             : 
     556           3 :     buf->stx_mask = 0;
     557           3 :     buf->stx_blksize = tmp.st_blksize;
     558           3 :     buf->stx_attributes = 0;
     559           3 :     buf->stx_nlink = tmp.st_nlink;
     560           3 :     buf->stx_uid = tmp.st_uid;
     561           3 :     buf->stx_gid = tmp.st_gid;
     562           3 :     buf->stx_mode = tmp.st_mode;
     563           3 :     buf->stx_ino = tmp.st_ino;
     564           3 :     buf->stx_size = tmp.st_size;
     565           3 :     buf->stx_blocks = tmp.st_blocks;
     566           3 :     buf->stx_attributes_mask = 0;
     567             : 
     568           3 :     buf->stx_atime.tv_sec = tmp.st_atim.tv_sec;
     569           3 :     buf->stx_atime.tv_nsec = tmp.st_atim.tv_nsec;
     570             : 
     571           3 :     buf->stx_mtime.tv_sec = tmp.st_mtim.tv_sec;
     572           3 :     buf->stx_mtime.tv_nsec = tmp.st_mtim.tv_nsec;
     573             : 
     574           3 :     buf->stx_ctime.tv_sec = tmp.st_ctim.tv_sec;
     575           3 :     buf->stx_ctime.tv_nsec = tmp.st_ctim.tv_nsec;
     576             : 
     577           3 :     buf->stx_btime = buf->stx_atime;
     578             : 
     579           3 :     return 0;
     580             : }
     581             : 
     582             : #endif
     583             : 
     584             : /**
     585             :  * gkfs wrapper for statfs() system calls
     586             :  * errno may be set
     587             :  * @param buf
     588             :  * @return 0 on success, -1 on failure
     589             :  */
     590             : int
     591           2 : gkfs_statfs(struct statfs* buf) {
     592             : 
     593           2 :     auto ret = gkfs::rpc::forward_get_chunk_stat();
     594           2 :     auto err = ret.first;
     595           2 :     if(err) {
     596           0 :         LOG(ERROR, "{}() Failure with error: '{}'", err);
     597           0 :         errno = err;
     598           0 :         return -1;
     599             :     }
     600           2 :     auto blk_stat = ret.second;
     601           2 :     buf->f_type = 0;
     602           2 :     buf->f_bsize = blk_stat.chunk_size;
     603           2 :     buf->f_blocks = blk_stat.chunk_total;
     604           2 :     buf->f_bfree = blk_stat.chunk_free;
     605           2 :     buf->f_bavail = blk_stat.chunk_free;
     606           2 :     buf->f_files = 0;
     607           2 :     buf->f_ffree = 0;
     608           2 :     buf->f_fsid = {0, 0};
     609           2 :     buf->f_namelen = path::max_length;
     610           2 :     buf->f_frsize = 0;
     611           2 :     buf->f_flags =
     612             :             ST_NOATIME | ST_NODIRATIME | ST_NOSUID | ST_NODEV | ST_SYNCHRONOUS;
     613           2 :     return 0;
     614             : }
     615             : 
     616             : #ifdef GKFS_ENABLE_UNUSED_FUNCTIONS
     617             : /**
     618             :  * gkfs wrapper for statvfs() system calls
     619             :  * errno may be set
     620             :  *
     621             :  * NOTE: Currently unused.
     622             :  *
     623             :  * @param buf
     624             :  * @return 0 on success, -1 on failure
     625             :  */
     626             : int
     627             : gkfs_statvfs(struct statvfs* buf) {
     628             :     auto ret = gkfs::rpc::forward_get_chunk_stat();
     629             :     auto err = ret.first;
     630             :     if(err) {
     631             :         LOG(ERROR, "{}() Failure with error: '{}'", err);
     632             :         errno = err;
     633             :         return -1;
     634             :     }
     635             :     auto blk_stat = ret.second;
     636             :     buf->f_bsize = blk_stat.chunk_size;
     637             :     buf->f_blocks = blk_stat.chunk_total;
     638             :     buf->f_bfree = blk_stat.chunk_free;
     639             :     buf->f_bavail = blk_stat.chunk_free;
     640             :     buf->f_files = 0;
     641             :     buf->f_ffree = 0;
     642             :     buf->f_favail = 0;
     643             :     buf->f_fsid = 0;
     644             :     buf->f_namemax = path::max_length;
     645             :     buf->f_frsize = 0;
     646             :     buf->f_flag =
     647             :             ST_NOATIME | ST_NODIRATIME | ST_NOSUID | ST_NODEV | ST_SYNCHRONOUS;
     648             :     return 0;
     649             : }
     650             : #endif
     651             : 
     652             : /**
     653             :  * gkfs wrapper for lseek() system calls with available file descriptor
     654             :  * errno may be set
     655             :  * @param fd
     656             :  * @param offset
     657             :  * @param whence
     658             :  * @return 0 on success, -1 on failure
     659             :  */
     660             : off_t
     661          12 : gkfs_lseek(unsigned int fd, off_t offset, unsigned int whence) {
     662          24 :     return gkfs_lseek(CTX->file_map()->get(fd), offset, whence);
     663             : }
     664             : 
     665             : /**
     666             :  * gkfs wrapper for lseek() system calls with available shared ptr to gkfs
     667             :  * FileMap errno may be set
     668             :  * @param gkfs_fd
     669             :  * @param offset
     670             :  * @param whence
     671             :  * @return 0 on success, -1 on failure
     672             :  */
     673             : off_t
     674          12 : gkfs_lseek(shared_ptr<gkfs::filemap::OpenFile> gkfs_fd, off_t offset,
     675             :            unsigned int whence) {
     676          12 :     switch(whence) {
     677           3 :         case SEEK_SET:
     678           3 :             if(offset < 0) {
     679           1 :                 errno = EINVAL;
     680           1 :                 return -1;
     681             :             }
     682           2 :             gkfs_fd->pos(offset);
     683           2 :             break;
     684           1 :         case SEEK_CUR:
     685           1 :             gkfs_fd->pos(gkfs_fd->pos() + offset);
     686           1 :             break;
     687           5 :         case SEEK_END: {
     688             :             // TODO: handle replicas
     689           5 :             auto ret =
     690           5 :                     gkfs::rpc::forward_get_metadentry_size(gkfs_fd->path(), 0);
     691           5 :             auto err = ret.first;
     692           5 :             if(err) {
     693           0 :                 errno = err;
     694           1 :                 return -1;
     695             :             }
     696             : 
     697           5 :             auto file_size = ret.second;
     698           5 :             if(offset < 0 && file_size < -offset) {
     699           1 :                 errno = EINVAL;
     700           1 :                 return -1;
     701             :             }
     702           4 :             gkfs_fd->pos(file_size + offset);
     703           4 :             break;
     704             :         }
     705           1 :         case SEEK_DATA:
     706           1 :             LOG(WARNING, "SEEK_DATA whence is not supported");
     707             :             // We do not support this whence yet
     708           1 :             errno = EINVAL;
     709           1 :             return -1;
     710           1 :         case SEEK_HOLE:
     711           1 :             LOG(WARNING, "SEEK_HOLE whence is not supported");
     712             :             // We do not support this whence yet
     713           1 :             errno = EINVAL;
     714           1 :             return -1;
     715           1 :         default:
     716           1 :             LOG(WARNING, "Unknown whence value {:#x}", whence);
     717           1 :             errno = EINVAL;
     718           1 :             return -1;
     719             :     }
     720           7 :     return gkfs_fd->pos();
     721             : }
     722             : 
     723             : /**
     724             :  * wrapper function for gkfs_truncate
     725             :  * errno may be set
     726             :  * @param path
     727             :  * @param old_size
     728             :  * @param new_size
     729             :  * @return 0 on success, -1 on failure
     730             :  */
     731             : int
     732           7 : gkfs_truncate(const std::string& path, off_t old_size, off_t new_size) {
     733           7 :     assert(new_size >= 0);
     734           7 :     assert(new_size <= old_size);
     735             : 
     736           7 :     if(new_size == old_size) {
     737             :         return 0;
     738             :     }
     739           6 :     for(auto copy = 0; copy < (CTX->get_replicas() + 1); copy++) {
     740           3 :         auto err = gkfs::rpc::forward_decr_size(path, new_size, copy);
     741           3 :         if(err) {
     742           0 :             LOG(DEBUG, "Failed to decrease size");
     743           0 :             errno = err;
     744           0 :             return -1;
     745             :         }
     746             :     }
     747             : 
     748           3 :     auto err = gkfs::rpc::forward_truncate(path, old_size, new_size,
     749           3 :                                            CTX->get_replicas());
     750           3 :     if(err) {
     751           0 :         LOG(DEBUG, "Failed to truncate data");
     752           0 :         errno = err;
     753           0 :         return -1;
     754             :     }
     755             :     return 0;
     756             : }
     757             : 
     758             : /**
     759             :  * gkfs wrapper for truncate() system calls
     760             :  * errno may be set
     761             :  * @param path
     762             :  * @param length
     763             :  * @return 0 on success, -1 on failure
     764             :  */
     765             : int
     766           7 : gkfs_truncate(const std::string& path, off_t length) {
     767             :     /* TODO CONCURRENCY:
     768             :      * At the moment we first ask the length to the metadata-server in order to
     769             :      * know which data-server have data to be deleted.
     770             :      *
     771             :      * From the moment we issue the gkfs_stat and the moment we issue the
     772             :      * gkfs_trunc_data, some more data could have been added to the file and the
     773             :      * length increased.
     774             :      */
     775           7 :     if(length < 0) {
     776           1 :         LOG(DEBUG, "Length is negative: {}", length);
     777           1 :         errno = EINVAL;
     778           1 :         return -1;
     779             :     }
     780             : 
     781          13 :     auto md = gkfs::utils::get_metadata(path, true);
     782           6 :     if(!md) {
     783             :         return -1;
     784             :     }
     785             : 
     786             :     // If rename is enabled we need to check if the file is renamed
     787             : #ifdef HAS_SYMLINKS
     788             : #ifdef HAS_RENAME
     789           6 :     if(md.value().blocks() == -1) {
     790           0 :         errno = ENOENT;
     791           0 :         return -1;
     792           6 :     } else if(!md.value().target_path().empty()) {
     793           2 :         std::string new_path;
     794           2 :         while(!md.value().target_path().empty()) {
     795           1 :             new_path = md.value().target_path();
     796           3 :             md = gkfs::utils::get_metadata(md.value().target_path());
     797             :         }
     798             :         // This could be optimized
     799           1 :         auto size = md->size();
     800           1 :         if(static_cast<unsigned long>(length) > size) {
     801           0 :             LOG(DEBUG, "Length is greater then file size: {} > {}", length,
     802           0 :                 size);
     803           0 :             errno = EINVAL;
     804           0 :             return -1;
     805             :         }
     806           1 :         return gkfs_truncate(new_path, size, length);
     807             :     }
     808             : #endif
     809             : #endif
     810             : 
     811           5 :     auto size = md->size();
     812           5 :     if(static_cast<unsigned long>(length) > size) {
     813           1 :         LOG(DEBUG, "Length is greater then file size: '{}' > '{}'", length,
     814           1 :             size);
     815           1 :         auto output_fd = gkfs_open(path, md->mode(), O_WRONLY);
     816           1 :         if(output_fd == -1) {
     817           0 :             errno = EINVAL;
     818           0 :             return -1;
     819             :         }
     820           1 :         gkfs_lseek(output_fd, 0, SEEK_END);
     821           1 :         ssize_t n = static_cast<unsigned long>(length) - size;
     822             :         // Zeroes the buffer. All make_* are value initialized
     823           2 :         auto buf = std::make_unique<char[]>(n);
     824           1 :         if(!buf) {
     825           0 :             errno = ENOMEM;
     826           0 :             return -1;
     827             :         }
     828           1 :         if(gkfs_write(output_fd, buf.get(), (size_t) n) != n) {
     829           0 :             errno = EINVAL;
     830           0 :             return -1;
     831             :         }
     832           1 :         CTX->file_map()->remove(output_fd);
     833             :         return 0;
     834             :     }
     835           4 :     return gkfs_truncate(path, size, length);
     836             : }
     837             : 
     838             : /**
     839             :  * gkfs wrapper for dup() system calls
     840             :  * errno may be set
     841             :  * @param oldfd
     842             :  * @return file descriptor int or -1 on error
     843             :  */
     844             : int
     845           3 : gkfs_dup(const int oldfd) {
     846           3 :     return CTX->file_map()->dup(oldfd);
     847             : }
     848             : 
     849             : /**
     850             :  * gkfs wrapper for dup2() system calls
     851             :  * errno may be set
     852             :  * @param oldfd
     853             :  * @param newfd
     854             :  * @return file descriptor int or -1 on error
     855             :  */
     856             : int
     857           1 : gkfs_dup2(const int oldfd, const int newfd) {
     858           1 :     return CTX->file_map()->dup2(oldfd, newfd);
     859             : }
     860             : 
     861             : /**
     862             :  * Wrapper function for all gkfs write operations
     863             :  * errno may be set
     864             :  * @param file
     865             :  * @param buf
     866             :  * @param count
     867             :  * @param offset
     868             :  * @param update_pos pos should only be updated for some write operations (see
     869             :  * man 2 pwrite)
     870             :  * @return written size or -1 on error
     871             :  */
     872             : ssize_t
     873          47 : gkfs_pwrite(std::shared_ptr<gkfs::filemap::OpenFile> file, const char* buf,
     874             :             size_t count, off64_t offset, bool update_pos) {
     875          47 :     if(file->type() != gkfs::filemap::FileType::regular) {
     876           1 :         assert(file->type() == gkfs::filemap::FileType::directory);
     877           1 :         LOG(WARNING, "Cannot write to directory");
     878           1 :         errno = EISDIR;
     879           1 :         return -1;
     880             :     }
     881          92 :     auto path = make_unique<string>(file->path());
     882          46 :     auto is_append = file->get_flag(gkfs::filemap::OpenFile_flags::append);
     883          46 :     auto write_size = 0;
     884          46 :     auto num_replicas = CTX->get_replicas();
     885             : 
     886          46 :     auto ret_offset = gkfs::rpc::forward_update_metadentry_size(
     887          46 :             *path, count, offset, is_append, num_replicas);
     888          46 :     auto err = ret_offset.first;
     889          46 :     if(err) {
     890           0 :         LOG(ERROR, "update_metadentry_size() failed with err '{}'", err);
     891           0 :         errno = err;
     892           0 :         return -1;
     893             :     }
     894          46 :     if(is_append) {
     895             :         // When append is set the EOF is set to the offset
     896             :         // forward_update_metadentry_size returns. This is because it is an
     897             :         // atomic operation on the server and reserves the space for this append
     898           3 :         if(ret_offset.second == -1) {
     899           0 :             LOG(ERROR,
     900             :                 "update_metadentry_size() received -1 as starting offset. "
     901             :                 "This occurs when the staring offset could not be extracted "
     902           0 :                 "from RocksDB's merge operations. Inform GekkoFS devs.");
     903           0 :             errno = EIO;
     904           0 :             return -1;
     905             :         }
     906             :         offset = ret_offset.second;
     907             :     }
     908             : 
     909          46 :     auto ret_write = gkfs::rpc::forward_write(*path, buf, offset, count, 0);
     910          46 :     err = ret_write.first;
     911          46 :     write_size = ret_write.second;
     912             : 
     913          46 :     if(num_replicas > 0) {
     914           0 :         auto ret_write_repl = gkfs::rpc::forward_write(*path, buf, offset,
     915           0 :                                                        count, num_replicas);
     916             : 
     917           0 :         if(err and ret_write_repl.first == 0) {
     918             :             // We succesfully write the data to some replica
     919           0 :             err = ret_write_repl.first;
     920             :             // Write size will be wrong
     921           0 :             write_size = ret_write_repl.second;
     922             :         }
     923             :     }
     924             : 
     925          46 :     if(err) {
     926           0 :         LOG(WARNING, "gkfs::rpc::forward_write() failed with err '{}'", err);
     927           0 :         errno = err;
     928           0 :         return -1;
     929             :     }
     930          46 :     if(update_pos) {
     931             :         // Update offset in file descriptor in the file map
     932          36 :         file->pos(offset + write_size);
     933             :     }
     934          46 :     if(static_cast<size_t>(write_size) != count) {
     935           0 :         LOG(WARNING,
     936             :             "gkfs::rpc::forward_write() wrote '{}' bytes instead of '{}'",
     937             :             write_size, count);
     938             :     }
     939          46 :     return write_size; // return written size
     940             : }
     941             : 
     942             : /**
     943             :  * gkfs wrapper for pwrite() system calls
     944             :  * errno may be set
     945             :  * @param fd
     946             :  * @param buf
     947             :  * @param count
     948             :  * @param offset
     949             :  * @return written size or -1 on error
     950             :  */
     951             : ssize_t
     952           2 : gkfs_pwrite_ws(int fd, const void* buf, size_t count, off64_t offset) {
     953           2 :     auto file = CTX->file_map()->get(fd);
     954           8 :     return gkfs_pwrite(file, reinterpret_cast<const char*>(buf), count, offset);
     955             : }
     956             : 
     957             : /**
     958             :  * gkfs wrapper for write() system calls
     959             :  * errno may be set
     960             :  * @param fd
     961             :  * @param buf
     962             :  * @param count
     963             :  * @return written size or -1 on error
     964             :  */
     965             : ssize_t
     966          37 : gkfs_write(int fd, const void* buf, size_t count) {
     967          37 :     auto gkfs_fd = CTX->file_map()->get(fd);
     968             :     // call pwrite and update pos
     969          74 :     auto ret = gkfs_pwrite(gkfs_fd, reinterpret_cast<const char*>(buf), count,
     970          74 :                            gkfs_fd->pos(), true);
     971          74 :     return ret;
     972             : }
     973             : 
     974             : /**
     975             :  * gkfs wrapper for pwritev() system calls
     976             :  * errno may be set
     977             :  * @param fd
     978             :  * @param iov
     979             :  * @param iovcnt
     980             :  * @param offset
     981             :  * @return written size or -1 on error
     982             :  */
     983             : ssize_t
     984           4 : gkfs_pwritev(int fd, const struct iovec* iov, int iovcnt, off_t offset) {
     985             : 
     986           8 :     auto file = CTX->file_map()->get(fd);
     987             :     auto pos = offset; // keep track of current position
     988             :     ssize_t written = 0;
     989             :     ssize_t ret;
     990          12 :     for(int i = 0; i < iovcnt; ++i) {
     991           8 :         auto count = (iov + i)->iov_len;
     992           8 :         if(count == 0) {
     993           0 :             continue;
     994             :         }
     995           8 :         auto buf = (iov + i)->iov_base;
     996          16 :         ret = gkfs_pwrite(file, reinterpret_cast<char*>(buf), count, pos);
     997           8 :         if(ret == -1) {
     998             :             break;
     999             :         }
    1000           8 :         written += ret;
    1001           8 :         pos += ret;
    1002             : 
    1003           8 :         if(static_cast<size_t>(ret) < count) {
    1004             :             break;
    1005             :         }
    1006             :     }
    1007             : 
    1008           4 :     if(written == 0) {
    1009           0 :         return -1;
    1010             :     }
    1011             :     return written;
    1012             : }
    1013             : 
    1014             : /**
    1015             :  * gkfs wrapper for writev() system calls
    1016             :  * errno may be set
    1017             :  * @param fd
    1018             :  * @param iov
    1019             :  * @param iovcnt
    1020             :  * @return written size or -1 on error
    1021             :  */
    1022             : ssize_t
    1023           2 : gkfs_writev(int fd, const struct iovec* iov, int iovcnt) {
    1024             : 
    1025           4 :     auto gkfs_fd = CTX->file_map()->get(fd);
    1026           2 :     auto pos = gkfs_fd->pos(); // retrieve the current offset
    1027           2 :     auto ret = gkfs_pwritev(fd, iov, iovcnt, pos);
    1028           2 :     assert(ret != 0);
    1029           2 :     if(ret < 0) {
    1030             :         return -1;
    1031             :     }
    1032           2 :     gkfs_fd->pos(pos + ret);
    1033             :     return ret;
    1034             : }
    1035             : 
    1036             : /**
    1037             :  * Wrapper function for all gkfs read operations
    1038             :  * @param file
    1039             :  * @param buf
    1040             :  * @param count
    1041             :  * @param offset
    1042             :  * @return read size or -1 on error
    1043             :  */
    1044             : ssize_t
    1045          33 : gkfs_pread(std::shared_ptr<gkfs::filemap::OpenFile> file, char* buf,
    1046             :            size_t count, off64_t offset) {
    1047          33 :     if(file->type() != gkfs::filemap::FileType::regular) {
    1048           1 :         assert(file->type() == gkfs::filemap::FileType::directory);
    1049           1 :         LOG(WARNING, "Cannot read from directory");
    1050           1 :         errno = EISDIR;
    1051           1 :         return -1;
    1052             :     }
    1053             : 
    1054             :     // Zeroing buffer before read is only relevant for sparse files. Otherwise
    1055             :     // sparse regions contain invalid data.
    1056          32 :     if constexpr(gkfs::config::io::zero_buffer_before_read) {
    1057             :         memset(buf, 0, sizeof(char) * count);
    1058             :     }
    1059          32 :     std::pair<int, off_t> ret;
    1060          65 :     std::set<int8_t> failed; // set with failed targets.
    1061          32 :     if(CTX->get_replicas() != 0) {
    1062             : 
    1063           0 :         ret = gkfs::rpc::forward_read(file->path(), buf, offset, count,
    1064           0 :                                       CTX->get_replicas(), failed);
    1065           0 :         while(ret.first == EIO) {
    1066           0 :             ret = gkfs::rpc::forward_read(file->path(), buf, offset, count,
    1067           0 :                                           CTX->get_replicas(), failed);
    1068           0 :             LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'",
    1069             :                 ret.first);
    1070             :         }
    1071             : 
    1072             :     } else {
    1073          64 :         ret = gkfs::rpc::forward_read(file->path(), buf, offset, count, 0,
    1074          32 :                                       failed);
    1075             :     }
    1076             : 
    1077          32 :     auto err = ret.first;
    1078          32 :     if(err) {
    1079           0 :         LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'", err);
    1080           0 :         errno = err;
    1081           0 :         return -1;
    1082             :     }
    1083             :     // XXX check that we don't try to read past end of the file
    1084          32 :     return ret.second; // return read size
    1085             : }
    1086             : 
    1087             : /**
    1088             :  * gkfs wrapper for read() system calls
    1089             :  * errno may be set
    1090             :  * @param fd
    1091             :  * @param buf
    1092             :  * @param count
    1093             :  * @return read size or -1 on error
    1094             :  */
    1095             : ssize_t
    1096          28 : gkfs_read(int fd, void* buf, size_t count) {
    1097          28 :     auto gkfs_fd = CTX->file_map()->get(fd);
    1098          28 :     auto pos = gkfs_fd->pos(); // retrieve the current offset
    1099          56 :     auto ret = gkfs_pread(gkfs_fd, reinterpret_cast<char*>(buf), count, pos);
    1100             :     // Update offset in file descriptor in the file map
    1101          28 :     if(ret > 0) {
    1102          17 :         gkfs_fd->pos(pos + ret);
    1103             :     }
    1104          56 :     return ret;
    1105             : }
    1106             : 
    1107             : /**
    1108             :  * gkfs wrapper for preadv() system calls
    1109             :  * errno may be set
    1110             :  * @param fd
    1111             :  * @param iov
    1112             :  * @param iovcnt
    1113             :  * @param offset
    1114             :  * @return read size or -1 on error
    1115             :  */
    1116             : ssize_t
    1117           2 : gkfs_preadv(int fd, const struct iovec* iov, int iovcnt, off_t offset) {
    1118             : 
    1119           4 :     auto file = CTX->file_map()->get(fd);
    1120             :     auto pos = offset; // keep track of current position
    1121             :     ssize_t read = 0;
    1122             :     ssize_t ret;
    1123           6 :     for(int i = 0; i < iovcnt; ++i) {
    1124           4 :         auto count = (iov + i)->iov_len;
    1125           4 :         if(count == 0) {
    1126           0 :             continue;
    1127             :         }
    1128           4 :         auto buf = (iov + i)->iov_base;
    1129           8 :         ret = gkfs_pread(file, reinterpret_cast<char*>(buf), count, pos);
    1130           4 :         if(ret == -1) {
    1131             :             break;
    1132             :         }
    1133           4 :         read += ret;
    1134           4 :         pos += ret;
    1135             : 
    1136           4 :         if(static_cast<size_t>(ret) < count) {
    1137             :             break;
    1138             :         }
    1139             :     }
    1140             : 
    1141           2 :     if(read == 0) {
    1142           0 :         return -1;
    1143             :     }
    1144             :     return read;
    1145             : }
    1146             : 
    1147             : /**
    1148             :  * gkfs wrapper for readv() system calls
    1149             :  * errno may be set
    1150             :  * @param fd
    1151             :  * @param iov
    1152             :  * @param iovcnt
    1153             :  * @return read size or -1 on error
    1154             :  */
    1155             : ssize_t
    1156           1 : gkfs_readv(int fd, const struct iovec* iov, int iovcnt) {
    1157             : 
    1158           2 :     auto gkfs_fd = CTX->file_map()->get(fd);
    1159           1 :     auto pos = gkfs_fd->pos(); // retrieve the current offset
    1160           1 :     auto ret = gkfs_preadv(fd, iov, iovcnt, pos);
    1161           1 :     assert(ret != 0);
    1162           1 :     if(ret < 0) {
    1163             :         return -1;
    1164             :     }
    1165           1 :     gkfs_fd->pos(pos + ret);
    1166             :     return ret;
    1167             : }
    1168             : 
    1169             : /**
    1170             :  * gkfs wrapper for pread() system calls
    1171             :  * errno may be set
    1172             :  * @param fd
    1173             :  * @param buf
    1174             :  * @param count
    1175             :  * @param offset
    1176             :  * @return read size or -1 on error
    1177             :  */
    1178             : ssize_t
    1179           1 : gkfs_pread_ws(int fd, void* buf, size_t count, off64_t offset) {
    1180           1 :     auto gkfs_fd = CTX->file_map()->get(fd);
    1181           4 :     return gkfs_pread(gkfs_fd, reinterpret_cast<char*>(buf), count, offset);
    1182             : }
    1183             : 
    1184             : /**
    1185             :  * wrapper function for opening directories
    1186             :  * errno may be set
    1187             :  * @param path
    1188             :  * @return 0 on success or -1 on error
    1189             :  */
    1190             : int
    1191          25 : gkfs_opendir(const std::string& path) {
    1192          50 :     auto md = gkfs::utils::get_metadata(path);
    1193          25 :     if(!md) {
    1194             :         return -1;
    1195             :     }
    1196             : 
    1197          25 :     if(!S_ISDIR(md->mode())) {
    1198           0 :         LOG(DEBUG, "Path is not a directory");
    1199           0 :         errno = ENOTDIR;
    1200           0 :         return -1;
    1201             :     }
    1202             : 
    1203          50 :     auto ret = gkfs::rpc::forward_get_dirents(path);
    1204          25 :     auto err = ret.first;
    1205          25 :     if(err) {
    1206           0 :         errno = err;
    1207           0 :         return -1;
    1208             :     }
    1209          25 :     assert(ret.second);
    1210          75 :     return CTX->file_map()->add(ret.second);
    1211             : }
    1212             : 
    1213             : /**
    1214             :  * gkfs wrapper for rmdir() system calls
    1215             :  * errno may be set
    1216             :  * @param path
    1217             :  * @return 0 on success or -1 on error
    1218             :  */
    1219             : int
    1220           4 : gkfs_rmdir(const std::string& path) {
    1221           8 :     auto md = gkfs::utils::get_metadata(path);
    1222           4 :     if(!md) {
    1223           0 :         LOG(DEBUG, "Error: Path '{}' err code '{}' ", path, strerror(errno));
    1224           0 :         return -1;
    1225             :     }
    1226           4 :     if(!S_ISDIR(md->mode())) {
    1227           1 :         LOG(DEBUG, "Path '{}' is not a directory", path);
    1228           1 :         errno = ENOTDIR;
    1229           1 :         return -1;
    1230             :     }
    1231             : 
    1232           7 :     auto ret = gkfs::rpc::forward_get_dirents(path);
    1233           3 :     auto err = ret.first;
    1234           3 :     if(err) {
    1235           0 :         errno = err;
    1236           0 :         return -1;
    1237             :     }
    1238           3 :     assert(ret.second);
    1239           6 :     auto open_dir = ret.second;
    1240           3 :     if(open_dir->size() != 0) {
    1241           1 :         errno = ENOTEMPTY;
    1242           1 :         return -1;
    1243             :     }
    1244           2 :     err = gkfs::rpc::forward_remove(path, CTX->get_replicas());
    1245           2 :     if(err) {
    1246           0 :         errno = err;
    1247           0 :         return -1;
    1248             :     }
    1249             :     return 0;
    1250             : }
    1251             : 
    1252             : /**
    1253             :  * gkfs wrapper for getdents() system calls
    1254             :  * errno may be set
    1255             :  * @param fd
    1256             :  * @param dirp
    1257             :  * @param count
    1258             :  * @return 0 on success or -1 on error
    1259             :  */
    1260             : int
    1261           0 : gkfs_getdents(unsigned int fd, struct linux_dirent* dirp, unsigned int count) {
    1262             : 
    1263             :     // Get opendir object (content was downloaded with opendir() call)
    1264           0 :     auto open_dir = CTX->file_map()->get_dir(fd);
    1265           0 :     if(open_dir == nullptr) {
    1266             :         // Cast did not succeeded: open_file is a regular file
    1267           0 :         errno = EBADF;
    1268           0 :         return -1;
    1269             :     }
    1270             : 
    1271             :     // get directory position of which entries to return
    1272           0 :     auto pos = open_dir->pos();
    1273           0 :     if(pos >= open_dir->size()) {
    1274             :         return 0;
    1275             :     }
    1276             : 
    1277             :     unsigned int written = 0;
    1278           0 :     struct linux_dirent* current_dirp = nullptr;
    1279           0 :     while(pos < open_dir->size()) {
    1280             :         // get dentry fir current position
    1281           0 :         auto de = open_dir->getdent(pos);
    1282             :         /*
    1283             :          * Calculate the total dentry size within the kernel struct
    1284             :          * `linux_dirent` depending on the file name size. The size is then
    1285             :          * aligned to the size of `long` boundary. This line was originally
    1286             :          * defined in the linux kernel: fs/readdir.c in function filldir(): int
    1287             :          * reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
    1288             :          * sizeof(long)); However, since d_name is null-terminated and
    1289             :          * de.name().size() does not include space for the null-terminator, we
    1290             :          * add 1. Thus, + 3 in total.
    1291             :          */
    1292           0 :         auto total_size = ALIGN(offsetof(struct linux_dirent, d_name) +
    1293             :                                         de.name().size() + 3,
    1294             :                                 sizeof(long));
    1295           0 :         if(total_size > (count - written)) {
    1296             :             // no enough space left on user buffer to insert next dirent
    1297             :             break;
    1298             :         }
    1299           0 :         current_dirp = reinterpret_cast<struct linux_dirent*>(
    1300           0 :                 reinterpret_cast<char*>(dirp) + written);
    1301           0 :         current_dirp->d_ino =
    1302           0 :                 std::hash<std::string>()(open_dir->path() + "/" + de.name());
    1303             : 
    1304           0 :         current_dirp->d_reclen = total_size;
    1305             : 
    1306           0 :         *(reinterpret_cast<char*>(current_dirp) + total_size - 1) =
    1307           0 :                 ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG
    1308             :                                                                  : DT_DIR);
    1309             : 
    1310           0 :         LOG(DEBUG, "name {}: {}", pos, de.name());
    1311           0 :         std::strcpy(&(current_dirp->d_name[0]), de.name().c_str());
    1312           0 :         ++pos;
    1313           0 :         current_dirp->d_off = pos;
    1314           0 :         written += total_size;
    1315             :     }
    1316             : 
    1317           0 :     if(written == 0) {
    1318           0 :         errno = EINVAL;
    1319           0 :         return -1;
    1320             :     }
    1321             :     // set directory position for next getdents() call
    1322           0 :     open_dir->pos(pos);
    1323           0 :     return written;
    1324             : }
    1325             : 
    1326             : /**
    1327             :  * gkfs wrapper for getdents64() system calls
    1328             :  * errno may be set
    1329             :  * @param fd
    1330             :  * @param dirp
    1331             :  * @param count
    1332             :  * @return 0 on success or -1 on error
    1333             :  */
    1334             : int
    1335          36 : gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp,
    1336             :                 unsigned int count) {
    1337             : 
    1338          72 :     auto open_dir = CTX->file_map()->get_dir(fd);
    1339          36 :     if(open_dir == nullptr) {
    1340             :         // Cast did not succeeded: open_file is a regular file
    1341           0 :         errno = EBADF;
    1342           0 :         return -1;
    1343             :     }
    1344          36 :     auto pos = open_dir->pos();
    1345          36 :     if(pos >= open_dir->size()) {
    1346             :         return 0;
    1347             :     }
    1348             :     unsigned int written = 0;
    1349        1045 :     struct linux_dirent64* current_dirp = nullptr;
    1350        1045 :     while(pos < open_dir->size()) {
    1351        2058 :         auto de = open_dir->getdent(pos);
    1352             :         /*
    1353             :          * Calculate the total dentry size within the kernel struct
    1354             :          * `linux_dirent` depending on the file name size. The size is then
    1355             :          * aligned to the size of `long` boundary.
    1356             :          *
    1357             :          * This line was originally defined in the linux kernel: fs/readdir.c in
    1358             :          * function filldir64(): int reclen = ALIGN(offsetof(struct
    1359             :          * linux_dirent64, d_name) + namlen + 1, sizeof(u64)); We keep + 1
    1360             :          * because: Since d_name is null-terminated and de.name().size() does
    1361             :          * not include space for the null-terminator, we add 1. Since d_name in
    1362             :          * our `struct linux_dirent64` definition is not a zero-size array (as
    1363             :          * opposed to the kernel version), we subtract 1. Thus, it stays + 1.
    1364             :          */
    1365        1029 :         auto total_size = ALIGN(offsetof(struct linux_dirent64, d_name) +
    1366             :                                         de.name().size() + 1,
    1367             :                                 sizeof(uint64_t));
    1368        1029 :         if(total_size > (count - written)) {
    1369             :             // no enough space left on user buffer to insert next dirent
    1370             :             break;
    1371             :         }
    1372        1029 :         current_dirp = reinterpret_cast<struct linux_dirent64*>(
    1373        1029 :                 reinterpret_cast<char*>(dirp) + written);
    1374        2058 :         current_dirp->d_ino =
    1375        2058 :                 std::hash<std::string>()(open_dir->path() + "/" + de.name());
    1376             : 
    1377        1029 :         current_dirp->d_reclen = total_size;
    1378        1029 :         current_dirp->d_type =
    1379        1029 :                 ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG
    1380             :                                                                  : DT_DIR);
    1381             : 
    1382        1029 :         LOG(DEBUG, "name {}: {}", pos, de.name());
    1383        1029 :         std::strcpy(&(current_dirp->d_name[0]), de.name().c_str());
    1384        1029 :         ++pos;
    1385        1029 :         current_dirp->d_off = pos;
    1386        1029 :         written += total_size;
    1387             :     }
    1388             : 
    1389          16 :     if(written == 0) {
    1390           0 :         errno = EINVAL;
    1391           0 :         return -1;
    1392             :     }
    1393          16 :     open_dir->pos(pos);
    1394          16 :     return written;
    1395             : }
    1396             : 
    1397             : /**
    1398             :  * @brief Closes an fd. To be used externally
    1399             :  *
    1400             :  * @param fd
    1401             :  * @return int
    1402             :  */
    1403             : int
    1404       12724 : gkfs_close(unsigned int fd) {
    1405       12724 :     if(CTX->file_map()->exist(fd)) {
    1406             :         // No call to the daemon is required
    1407        1004 :         CTX->file_map()->remove(fd);
    1408        1004 :         return 0;
    1409             :     }
    1410             : 
    1411       11720 :     if(CTX->is_internal_fd(fd)) {
    1412             :         // the client application (for some reason) is trying to close an
    1413             :         // internal fd: ignore it
    1414        3120 :         return 0;
    1415             :     }
    1416             : 
    1417             :     return -1;
    1418             : }
    1419             : 
    1420             : #ifdef HAS_SYMLINKS
    1421             : #ifdef GKFS_ENABLE_UNUSED_FUNCTIONS
    1422             : /**
    1423             :  * gkfs wrapper for make symlink() system calls
    1424             :  * errno may be set
    1425             :  *
    1426             :  * * NOTE: Currently unused
    1427             :  *
    1428             :  * @param path
    1429             :  * @param target_path
    1430             :  * @return 0 on success or -1 on error
    1431             :  */
    1432             : int
    1433             : gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
    1434             :     /* The following check is not POSIX compliant.
    1435             :      * In POSIX the target is not checked at all.
    1436             :      *  Here if the target is a directory we raise a NOTSUP error.
    1437             :      *  So that application know we don't support link to directory.
    1438             :      */
    1439             :     auto target_md = gkfs::utils::get_metadata(target_path, false);
    1440             :     if(target_md) {
    1441             :         auto trg_mode = target_md->mode();
    1442             :         if(!(S_ISREG(trg_mode) || S_ISLNK(trg_mode))) {
    1443             :             assert(S_ISDIR(trg_mode));
    1444             :             LOG(DEBUG, "Target path is a directory. Not supported");
    1445             :             errno = ENOTSUP;
    1446             :             return -1;
    1447             :         }
    1448             :     }
    1449             : 
    1450             :     if(check_parent_dir(path)) {
    1451             :         return -1;
    1452             :     }
    1453             : 
    1454             :     auto link_md = gkfs::utils::get_metadata(path, false);
    1455             :     if(link_md) {
    1456             :         LOG(DEBUG, "Link exists: '{}'", path);
    1457             :         errno = EEXIST;
    1458             :         return -1;
    1459             :     }
    1460             : 
    1461             :     auto err = gkfs::rpc::forward_mk_symlink(path, target_path);
    1462             :     if(err) {
    1463             :         errno = err;
    1464             :         return -1;
    1465             :     }
    1466             :     return 0;
    1467             : }
    1468             : 
    1469             : /**
    1470             :  * gkfs wrapper for reading symlinks
    1471             :  * errno may be set
    1472             :  *
    1473             :  * NOTE: Currently unused
    1474             :  *
    1475             :  * @param path
    1476             :  * @param buf
    1477             :  * @param bufsize
    1478             :  * @return 0 on success or -1 on error
    1479             :  */
    1480             : int
    1481             : gkfs_readlink(const std::string& path, char* buf, int bufsize) {
    1482             :     auto md = gkfs::utils::get_metadata(path, false);
    1483             :     if(!md) {
    1484             :         LOG(DEBUG, "Named link doesn't exist");
    1485             :         return -1;
    1486             :     }
    1487             :     if(!(md->is_link())) {
    1488             :         LOG(DEBUG, "The named file is not a symbolic link");
    1489             :         errno = EINVAL;
    1490             :         return -1;
    1491             :     }
    1492             :     int path_size = md->target_path().size() + CTX->mountdir().size();
    1493             :     if(path_size >= bufsize) {
    1494             :         LOG(WARNING, "Destination buffer size is too short: {} < {}, {} ",
    1495             :             bufsize, path_size, md->target_path());
    1496             :         errno = ENAMETOOLONG;
    1497             :         return -1;
    1498             :     }
    1499             : 
    1500             :     CTX->mountdir().copy(buf, CTX->mountdir().size());
    1501             :     std::strcpy(buf + CTX->mountdir().size(), md->target_path().c_str());
    1502             :     return path_size;
    1503             : }
    1504             : #endif
    1505             : #endif
    1506             : 
    1507             : 
    1508             : std::vector<std::string>
    1509           0 : gkfs_get_file_list(const std::string& path) {
    1510           0 :     auto ret = gkfs::rpc::forward_get_dirents(path);
    1511           0 :     auto err = ret.first;
    1512           0 :     if(err) {
    1513           0 :         errno = err;
    1514           0 :         return {};
    1515             :     }
    1516             : 
    1517           0 :     auto open_dir = ret.second;
    1518             : 
    1519           0 :     std::vector<std::string> file_list;
    1520           0 :     unsigned int pos = 0;
    1521             : 
    1522           0 :     while(pos < open_dir->size()) {
    1523           0 :         auto de = open_dir->getdent(pos++);
    1524           0 :         file_list.push_back(de.name());
    1525             :     }
    1526           0 :     return file_list;
    1527             : }
    1528             : 
    1529             : } // namespace gkfs::syscall
    1530             : 
    1531             : 
    1532             : /* This function defines an extension of the dirents prepared to do a find-like
    1533             :  * function The function only sends the request to the specified server
    1534             :  */
    1535             : extern "C" int
    1536           4 : gkfs_getsingleserverdir(const char* path, struct dirent_extended* dirp,
    1537             :                         unsigned int count, int server) {
    1538             : 
    1539          12 :     auto ret = gkfs::rpc::forward_get_dirents_single(path, server);
    1540           4 :     auto err = ret.first;
    1541           4 :     if(err) {
    1542           0 :         errno = err;
    1543           0 :         return -1;
    1544             :     }
    1545             : 
    1546           8 :     auto open_dir = ret.second;
    1547           4 :     unsigned int pos = 0;
    1548           4 :     unsigned int written = 0;
    1549           4 :     struct dirent_extended* current_dirp = nullptr;
    1550           8 :     while(pos < open_dir.size()) {
    1551           8 :         auto de = open_dir[pos];
    1552             :         /*
    1553             :          * Calculate the total dentry size within the 'dirent_extended`
    1554             :          * depending on the file name size. The size is then aligned to the size
    1555             :          * of `long` boundary.
    1556             :          */
    1557           4 :         auto total_size = ALIGN(offsetof(struct dirent_extended, d_name) +
    1558             :                                         (get<0>(de)).size() + 1,
    1559             :                                 sizeof(uint64_t));
    1560           4 :         if(total_size > (count - written)) {
    1561             :             // no enough space left on user buffer to insert next dirent
    1562             :             break;
    1563             :         }
    1564           4 :         current_dirp = reinterpret_cast<struct dirent_extended*>(
    1565           4 :                 reinterpret_cast<char*>(dirp) + written);
    1566             : 
    1567           4 :         current_dirp->d_reclen = total_size;
    1568           4 :         current_dirp->d_type = get<1>(de);
    1569           4 :         current_dirp->size = get<2>(de);
    1570           4 :         current_dirp->ctime = get<3>(de);
    1571             : 
    1572           4 :         LOG(DEBUG, "name {}: {} {} {} {} / size {}", pos, get<0>(de),
    1573           4 :             get<1>(de), get<2>(de), get<3>(de), total_size);
    1574           4 :         std::strcpy(&(current_dirp->d_name[0]), (get<0>(de)).c_str());
    1575           4 :         ++pos;
    1576           4 :         written += total_size;
    1577             :     }
    1578             : 
    1579           4 :     if(written == 0) {
    1580           2 :         errno = EINVAL;
    1581           2 :         return -1;
    1582             :     }
    1583           2 :     return written;
    1584             : }

Generated by: LCOV version 1.16