L4Re/departure

Annotated libfsserver/lib/files/ext2_file_operations.cc

645:3047b11cc814
7 months ago Paul Boddie Fixed the file_data_available result to return zero if the populated span has somehow become less than the current position in the memory region.
paul@148 1
/*
paul@148 2
 * File operations performed by an Ext2-compatible filesystem.
paul@148 3
 *
paul@236 4
 * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk>
paul@148 5
 *
paul@148 6
 * This program is free software; you can redistribute it and/or
paul@148 7
 * modify it under the terms of the GNU General Public License as
paul@148 8
 * published by the Free Software Foundation; either version 2 of
paul@148 9
 * the License, or (at your option) any later version.
paul@148 10
 *
paul@148 11
 * This program is distributed in the hope that it will be useful,
paul@148 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@148 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@148 14
 * GNU General Public License for more details.
paul@148 15
 *
paul@148 16
 * You should have received a copy of the GNU General Public License
paul@148 17
 * along with this program; if not, write to the Free Software
paul@148 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@148 19
 * Boston, MA  02110-1301, USA
paul@148 20
 */
paul@148 21
paul@148 22
#include <e2access/access.h>
paul@148 23
#include <e2access/image.h>
paul@148 24
#include <e2access/path.h>
paul@239 25
#include <systypes/fcntl.h>
paul@148 26
paul@148 27
#include "ext2_file_operations.h"
paul@148 28
paul@239 29
paul@239 30
paul@239 31
/* Access and object type tests. */
paul@239 32
paul@239 33
bool Ext2FileOperations::can_access(user_t user, flags_t flags, ext2_ino_t ino)
paul@239 34
{
paul@239 35
    struct ext2_inode inode;
paul@239 36
    errcode_t retval = ext2fs_read_inode(_fs, ino, &inode);
paul@239 37
paul@239 38
    if (retval)
paul@239 39
        return false;
paul@239 40
paul@239 41
    if ((flags & O_RDWR) || (flags & O_WRONLY))
paul@239 42
        return access_can_write(user, &inode);
paul@239 43
    else
paul@239 44
        return access_can_read(user, &inode);
paul@239 45
}
paul@156 46
paul@256 47
/* Assert that a directory is empty. */
paul@256 48
paul@256 49
bool Ext2FileOperations::directory_is_empty(ext2_ino_t ino)
paul@256 50
{
paul@256 51
    std::lock_guard<std::mutex> guard(_lock);
paul@256 52
paul@256 53
    return !image_dir_empty_by_inode(_fs, ino);
paul@256 54
}
paul@256 55
paul@256 56
/* Test for a directory. */
paul@256 57
paul@156 58
bool Ext2FileOperations::is_directory(ext2_ino_t ino_file)
paul@156 59
{
paul@156 60
    std::lock_guard<std::mutex> guard(_lock);
paul@156 61
paul@258 62
    return image_isdir_by_inode(_fs, ino_file);
paul@156 63
}
paul@156 64
paul@256 65
/* Test for a file. */
paul@256 66
paul@156 67
bool Ext2FileOperations::is_file(ext2_ino_t ino_file)
paul@156 68
{
paul@156 69
    std::lock_guard<std::mutex> guard(_lock);
paul@156 70
paul@258 71
    return image_isfile_by_inode(_fs, ino_file);
paul@156 72
}
paul@156 73
paul@239 74
paul@239 75
paul@228 76
/* Perform closing operations on a file. */
paul@228 77
paul@228 78
void Ext2FileOperations::close_file(ext2_file_t file)
paul@228 79
{
paul@228 80
    std::lock_guard<std::mutex> guard(_lock);
paul@228 81
paul@439 82
    /* ext2fs_file_flush is called within the close function. */
paul@439 83
paul@228 84
    ext2fs_file_close(file);
paul@228 85
}
paul@228 86
paul@148 87
/* Create a file in the directory indicated by the given inode number with the
paul@148 88
   given filename. The file is created with the given user permissions. */
paul@148 89
paul@148 90
long Ext2FileOperations::create_file(ext2_ino_t ino_parent, const char *filename,
paul@148 91
                                     user_t user, ext2_ino_t *ino)
paul@148 92
{
paul@148 93
    if (!path_is_leafname(filename))
paul@148 94
        return -L4_EINVAL;
paul@148 95
paul@149 96
    std::lock_guard<std::mutex> guard(_lock);
paul@149 97
paul@148 98
    struct ext2_inode inode_parent;
paul@148 99
    errcode_t retval = ext2fs_read_inode(_fs, ino_parent, &inode_parent);
paul@148 100
paul@148 101
    if (retval)
paul@148 102
        return -L4_EIO;
paul@148 103
paul@148 104
    if (!access_can_write(user, &inode_parent))
paul@414 105
        return -L4_EACCESS;
paul@148 106
paul@148 107
    if (image_create_file(_fs, ino_parent, filename, 0666 & ~user.umask,
paul@148 108
                          user.uid, user.gid, ino))
paul@148 109
        return -L4_EIO;
paul@148 110
paul@148 111
    return L4_EOK;
paul@148 112
}
paul@148 113
paul@148 114
/* For the given 'path', return an inode number or indicate the 'remaining'
paul@148 115
   part of the path that cannot be resolved. */
paul@148 116
paul@148 117
long Ext2FileOperations::find_file(const char *path, ext2_ino_t *ino,
paul@148 118
                                   const char **remaining)
paul@148 119
{
paul@149 120
    std::lock_guard<std::mutex> guard(_lock);
paul@149 121
paul@148 122
    *remaining = path;
paul@258 123
    errcode_t retval = image_resolve_by_path(_fs, remaining, ino);
paul@148 124
paul@148 125
    // NOTE: Map error conditions.
paul@148 126
paul@148 127
    if (retval)
paul@148 128
        return -L4_EIO;
paul@148 129
paul@148 130
    return L4_EOK;
paul@148 131
}
paul@148 132
paul@271 133
/* Make a new directory. */
paul@271 134
paul@271 135
long Ext2FileOperations::mkdir(ext2_ino_t ino_parent, const char *basename,
paul@271 136
                               sys_mode_t mode, user_t user)
paul@271 137
{
paul@271 138
    std::lock_guard<std::mutex> guard(_lock);
paul@271 139
paul@271 140
    ext2_ino_t ino;
paul@271 141
    errcode_t retval;
paul@271 142
paul@271 143
    /* Test for permission to create the directory. */
paul@271 144
paul@271 145
    if (!can_access(user, O_WRONLY, ino_parent))
paul@414 146
        return -L4_EACCESS;
paul@271 147
paul@271 148
    /* Make the directory. */
paul@271 149
paul@271 150
    retval = image_make_dir(_fs, ino_parent, basename, mode, user.uid, user.gid,
paul@271 151
                            &ino);
paul@271 152
paul@271 153
    // NOTE: Map error conditions.
paul@271 154
paul@271 155
    if (retval)
paul@271 156
        return -L4_EIO;
paul@271 157
paul@271 158
    return L4_EOK;
paul@271 159
}
paul@271 160
paul@148 161
/* Open the file associated with the indicated inode. */
paul@148 162
paul@148 163
long Ext2FileOperations::open_file(ext2_ino_t ino, ext2_file_t *file)
paul@148 164
{
paul@149 165
    std::lock_guard<std::mutex> guard(_lock);
paul@149 166
paul@148 167
    errcode_t retval = ext2fs_file_open(_fs, ino, EXT2_FILE_WRITE, file);
paul@148 168
paul@148 169
    // NOTE: Map error conditions.
paul@148 170
paul@148 171
    if (retval)
paul@148 172
        return -L4_EIO;
paul@148 173
paul@148 174
    return L4_EOK;
paul@148 175
}
paul@148 176
paul@232 177
/* Remove an object from a directory. */
paul@232 178
paul@232 179
long Ext2FileOperations::remove(ext2_ino_t ino)
paul@232 180
{
paul@232 181
    std::lock_guard<std::mutex> guard(_lock);
paul@232 182
paul@232 183
    errcode_t retval = image_remove_by_inode(_fs, ino);
paul@232 184
paul@232 185
    // NOTE: Map error conditions.
paul@232 186
paul@232 187
    if (retval)
paul@232 188
        return -L4_EIO;
paul@232 189
paul@232 190
    return L4_EOK;
paul@232 191
}
paul@232 192
paul@236 193
/* Rename an object. */
paul@236 194
paul@236 195
long Ext2FileOperations::rename(ext2_ino_t source,
paul@236 196
                                ext2_ino_t source_parent, const char *source_basename,
paul@236 197
                                ext2_ino_t target_parent, const char *target_basename)
paul@236 198
{
paul@236 199
    std::lock_guard<std::mutex> guard(_lock);
paul@236 200
paul@236 201
    errcode_t retval = image_rename(_fs, source, source_parent, source_basename,
paul@236 202
                                    target_parent, target_basename);
paul@236 203
paul@236 204
    // NOTE: Map error conditions.
paul@236 205
paul@236 206
    if (retval)
paul@236 207
        return -L4_EIO;
paul@236 208
paul@236 209
    return L4_EOK;
paul@236 210
}
paul@236 211
paul@232 212
/* Unlink an object from a directory. */
paul@232 213
paul@232 214
long Ext2FileOperations::unlink(ext2_ino_t ino_parent, ext2_ino_t ino)
paul@232 215
{
paul@232 216
    std::lock_guard<std::mutex> guard(_lock);
paul@232 217
paul@232 218
    errcode_t retval = image_unlink_by_inode(_fs, ino_parent, ino);
paul@232 219
paul@232 220
    // NOTE: Map error conditions.
paul@232 221
paul@232 222
    if (retval)
paul@232 223
        return -L4_EIO;
paul@232 224
paul@232 225
    return L4_EOK;
paul@232 226
}
paul@232 227
paul@236 228
paul@236 229
paul@148 230
/* Obtain the size of a file. */
paul@148 231
paul@148 232
offset_t Ext2FileOperations::get_size(ext2_file_t file)
paul@148 233
{
paul@149 234
    std::lock_guard<std::mutex> guard(_lock);
paul@149 235
paul@148 236
    return ext2fs_file_get_size(file);
paul@148 237
}
paul@148 238
paul@148 239
/* Update the size of a file. */
paul@148 240
paul@148 241
void Ext2FileOperations::set_size(ext2_file_t file, offset_t size)
paul@148 242
{
paul@149 243
    std::lock_guard<std::mutex> guard(_lock);
paul@149 244
paul@148 245
    ext2fs_file_set_size(file, size);
paul@148 246
}
paul@148 247
paul@148 248
/* Populate the given memory region with file content. */
paul@148 249
paul@148 250
offset_t Ext2FileOperations::read_file(ext2_file_t file, offset_t filepos, void *addr, offset_t size)
paul@148 251
{
paul@149 252
    std::lock_guard<std::mutex> guard(_lock);
paul@149 253
paul@148 254
    unsigned int nread;
paul@148 255
paul@148 256
    ext2fs_file_llseek(file, filepos, SEEK_SET, NULL);
paul@148 257
    ext2fs_file_read(file, (void *) addr, size, &nread);
paul@148 258
paul@148 259
    return (offset_t) nread;
paul@148 260
}
paul@148 261
paul@148 262
/* Transfer content from the given memory region to a file. */
paul@148 263
paul@148 264
void Ext2FileOperations::write_file(ext2_file_t file, offset_t filepos, const void *addr, offset_t size)
paul@148 265
{
paul@149 266
    std::lock_guard<std::mutex> guard(_lock);
paul@149 267
paul@148 268
    ext2fs_file_llseek(file, filepos, SEEK_SET, NULL);
paul@148 269
    ext2fs_file_write(file, addr, size, NULL);
paul@148 270
}
paul@148 271
paul@191 272
/* Obtain information for the given inode. */
paul@191 273
paul@191 274
long Ext2FileOperations::read_inode(ext2_ino_t ino, struct ext2_inode *inode)
paul@191 275
{
paul@191 276
    std::lock_guard<std::mutex> guard(_lock);
paul@191 277
paul@191 278
    if (ext2fs_read_inode(_fs, ino, inode))
paul@191 279
        return -L4_EIO;
paul@191 280
paul@191 281
    return L4_EOK;
paul@191 282
}
paul@191 283
paul@266 284
/* Obtain stat-based information for the given inode. */
paul@266 285
paul@266 286
long Ext2FileOperations::stat_inode(ext2_ino_t ino, struct stat *st)
paul@266 287
{
paul@266 288
    std::lock_guard<std::mutex> guard(_lock);
paul@266 289
paul@266 290
    if (image_stat_inode(_fs, ino, st))
paul@266 291
        return -L4_EIO;
paul@266 292
paul@266 293
    return L4_EOK;
paul@266 294
}
paul@266 295
paul@191 296
paul@191 297
paul@191 298
/* Directory iteration support. */
paul@191 299
paul@191 300
/* Callback function invoking the callback method. */
paul@191 301
paul@191 302
static int _directory_iterate_fn(struct ext2_dir_entry *dir_entry,
paul@191 303
                                 int offset, int blocksize, char *buf,
paul@191 304
                                 void *priv_data)
paul@191 305
{
paul@191 306
    struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data);
paul@191 307
paul@191 308
    return data->ops->directory_iterate_fn(dir_entry, offset, blocksize, buf, data);
paul@191 309
}
paul@191 310
paul@191 311
/* Callback method invoking the actual callback, with control over locking. */
paul@191 312
paul@191 313
int Ext2FileOperations::directory_iterate_fn(struct ext2_dir_entry *dir_entry,
paul@191 314
                                             int offset, int blocksize,
paul@191 315
                                             char *buf, struct Ext2DirectoryIteration *priv_data)
paul@191 316
{
paul@191 317
    /* Release the lock to allow other operations to proceed. */
paul@191 318
paul@191 319
    _lock.unlock();
paul@191 320
paul@191 321
    /* Invoke the actual callback function. */
paul@191 322
paul@191 323
    struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data);
paul@191 324
    int result = data->func(dir_entry, offset, blocksize, buf, data->priv_data);
paul@191 325
paul@191 326
    /* Acquire the lock and continue iteration. */
paul@191 327
paul@191 328
    _lock.lock();
paul@191 329
    return result;
paul@191 330
}
paul@191 331
paul@168 332
/* Initiate iteration over a directory, with the given 'func' being called
paul@168 333
   with directory entry details for each entry. */
paul@168 334
paul@168 335
long Ext2FileOperations::directory_iterate(ext2_ino_t dir,
paul@168 336
                                           int func(struct ext2_dir_entry *, int, int,
paul@168 337
                                                    char *, void *),
paul@168 338
                                           void *priv_data)
paul@168 339
{
paul@191 340
    /* NOTE: Iteration should probably be interrupted if it appears that the
paul@191 341
             directory has been modified and the structure of the data has
paul@191 342
             been changed. This condition might then cause iteration to begin
paul@191 343
             again, skipping ahead to the entry after the last visited entry. */
paul@168 344
paul@191 345
    /* Acquire the lock for iteration to proceed safely. */
paul@191 346
paul@191 347
    _lock.lock();
paul@168 348
paul@191 349
    /* Begin iteration using the given callback function to invoke a callback
paul@191 350
       method in this object that will wrap the actual callback with locking
paul@191 351
       operations. */
paul@191 352
paul@191 353
    struct Ext2DirectoryIteration data = {this, func, priv_data};
paul@191 354
    int result = ext2fs_dir_iterate(_fs, dir, 0, 0, _directory_iterate_fn, &data);
paul@168 355
paul@191 356
    /* Release the lock upon finishing iteration. */
paul@191 357
paul@191 358
    _lock.unlock();
paul@191 359
paul@191 360
    if (result)
paul@168 361
        return -L4_EIO;
paul@191 362
    else
paul@191 363
        return L4_EOK;
paul@168 364
}
paul@168 365
paul@148 366
// vim: tabstop=4 expandtab shiftwidth=4