1 /* 2 * An opener for a "host" file provided via the C library. 3 * 4 * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <dirent.h> 23 #include <stdio.h> 24 #include <string.h> 25 #include <sys/stat.h> 26 #include <unistd.h> 27 28 #include <fsclient/client.h> 29 #include <resource/resource_server.h> 30 31 #include "host_directory_accessor.h" 32 #include "host_file_accessor.h" 33 #include "host_file_opener.h" 34 35 36 37 HostFileOpener::~HostFileOpener() 38 { 39 } 40 41 bool HostFileOpener::accessing_directory(flags_t flags, fileid_t fileid) 42 { 43 (void) flags; 44 45 const char *path = _get_path(fileid); 46 47 if (path == NULL) 48 return false; 49 50 struct stat st; 51 52 if (::stat(path, &st)) 53 return false; 54 55 return (st.st_mode & S_IFDIR) ? true : false; 56 } 57 58 bool HostFileOpener::accessing_file(flags_t flags, fileid_t fileid) 59 { 60 (void) flags; 61 struct stat st; 62 63 const char *path = _get_path(fileid); 64 65 if (path == NULL) 66 return false; 67 68 if (::stat(path, &st)) 69 return false; 70 71 return (st.st_mode & S_IFREG) ? true : false; 72 } 73 74 /* Test if a directory is empty. */ 75 76 bool HostFileOpener::directory_is_empty(fileid_t fileid) 77 { 78 const char *path = _get_path(fileid); 79 80 if (path == NULL) 81 return false; 82 83 DIR *dir = opendir(path); 84 struct dirent *entry; 85 86 if (dir == NULL) 87 return false; 88 89 while ((entry = readdir(dir)) != NULL) 90 { 91 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) 92 { 93 closedir(dir); 94 return false; 95 } 96 } 97 98 closedir(dir); 99 return true; 100 } 101 102 /* Return a file identifier for the given 'path'. */ 103 104 long HostFileOpener::get_fileid(const char *path, flags_t flags, fileid_t *fileid) 105 { 106 (void) flags; 107 108 /* Test for a valid file. */ 109 110 FILE *fp = fopen(path, "r"); 111 112 if (fp == NULL) 113 return -L4_ENOENT; 114 115 fclose(fp); 116 117 *fileid = _get_fileid(path, true); 118 return L4_EOK; 119 } 120 121 fileid_t HostFileOpener::_get_fileid(const char *path, bool create) 122 { 123 std::lock_guard<std::mutex> guard(_lock); 124 125 /* The inode number would be a good choice, but the L4Re read-only file 126 implementation just provides the value of a counter! 127 128 See: pkg/l4re-core/l4re_vfs/include/impl/ro_file_impl.h */ 129 130 std::string s(path); 131 132 HostFileIdentifiers::iterator it = _fileids.find(s); 133 134 if (it != _fileids.end()) 135 return it->second; 136 137 if (!create) 138 return FILEID_INVALID; 139 140 fileid_t fileid = _fileids.size(); 141 142 _fileids[s] = fileid; 143 _paths[fileid] = s; 144 145 return fileid; 146 } 147 148 const char *HostFileOpener::_get_path(fileid_t fileid) 149 { 150 std::lock_guard<std::mutex> guard(_lock); 151 152 HostFilePaths::iterator it = _paths.find(fileid); 153 154 if (it != _paths.end()) 155 return it->second.c_str(); 156 else 157 return NULL; 158 } 159 160 /* Return a new accessor for 'fileid'. */ 161 162 long HostFileOpener::make_accessor(flags_t flags, fileid_t fileid, 163 Accessor **accessor) 164 { 165 // NOTE: Not testing for create or write flags. 166 167 (void) flags; 168 169 const char *path = _get_path(fileid); 170 171 if (path == NULL) 172 return -L4_ENOENT; 173 174 FILE *fp = fopen(path, "r"); 175 176 if (fp == NULL) 177 return -L4_ENOENT; 178 179 *accessor = new HostFileAccessor(fp, fileid); 180 return L4_EOK; 181 } 182 183 /* Return a directory accessor for 'fileid'. */ 184 185 long HostFileOpener::make_directory_accessor(flags_t flags, fileid_t fileid, 186 DirectoryAccessor **accessor) 187 { 188 (void) flags; 189 190 const char *path = _get_path(fileid); 191 192 if (path == NULL) 193 return -L4_ENOENT; 194 195 *accessor = new HostDirectoryAccessor(path); 196 return L4_EOK; 197 } 198 199 200 201 /* Make a new directory. */ 202 203 long HostFileOpener::make_directory_object(const char *path, sys_mode_t mode) 204 { 205 int err = mkdir(path, systypes_from_sys_mode(mode)); 206 207 // NOTE: Other return codes may need converting. 208 209 switch (err) 210 { 211 case 0: break; 212 case EACCES: return -L4_EACCESS; 213 case EEXIST: return -L4_EEXIST; 214 case ENOENT: return -L4_ENOENT; 215 default: return -L4_EIO; 216 } 217 218 fileid_t fileid = _get_fileid(path, true); 219 220 _fileids[path] = fileid; 221 _paths[fileid] = path; 222 223 return L4_EOK; 224 } 225 226 /* Remove a filesystem object. */ 227 228 long HostFileOpener::remove_object(fileid_t fileid) 229 { 230 (void) fileid; 231 return L4_EOK; 232 } 233 234 /* Rename a filesystem object, placing source inside the parent of target. */ 235 236 long HostFileOpener::rename_object(const char *source, const char *target) 237 { 238 /* NOTE: Should propagate a more meaningful error. */ 239 240 if (rename(source, target)) 241 return -L4_EIO; 242 243 return L4_EOK; 244 } 245 246 /* Populate a memory region with statistics metadata for a filesystem object. */ 247 248 long HostFileOpener::stat_object(const char *path, void *base, offset_t size) 249 { 250 struct stat *st = (struct stat *) base; 251 252 if (sizeof(struct stat) > size) 253 return -L4_ENOMEM; 254 255 return ::stat(path, st); 256 } 257 258 /* Unlink a filesystem object. */ 259 260 long HostFileOpener::unlink_object(fileid_t parent_fileid, fileid_t fileid) 261 { 262 /* Ignore the parent and always use the path. */ 263 264 (void) parent_fileid; 265 266 const char *path = _get_path(fileid); 267 268 if (path == NULL) 269 return -L4_ENOENT; 270 271 // NOTE: Return code may need converting. 272 273 int err = unlink(path); 274 275 if (err) 276 return err; 277 278 /* Remove the file identifier mapping. */ 279 280 std::string s(path); 281 282 HostFileIdentifiers::iterator it = _fileids.find(s); 283 284 if (it != _fileids.end()) 285 _fileids.erase(it); 286 287 HostFilePaths::iterator pit = _paths.find(fileid); 288 289 if (pit != _paths.end()) 290 _paths.erase(pit); 291 292 return L4_EOK; 293 } 294 295 // vim: tabstop=4 expandtab shiftwidth=4