L4Re/departure

Annotated libfsserver/lib/files/ext2_file_operations.cc

239:184132df62ec
2022-02-09 Paul Boddie Introduced file permissions testing when accessing files.
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@156 47
bool Ext2FileOperations::is_directory(ext2_ino_t ino_file)
paul@156 48
{
paul@156 49
    std::lock_guard<std::mutex> guard(_lock);
paul@156 50
paul@156 51
    return _image_isdir(_fs, ino_file);
paul@156 52
}
paul@156 53
paul@156 54
bool Ext2FileOperations::is_file(ext2_ino_t ino_file)
paul@156 55
{
paul@156 56
    std::lock_guard<std::mutex> guard(_lock);
paul@156 57
paul@156 58
    return _image_isfile(_fs, ino_file);
paul@156 59
}
paul@156 60
paul@239 61
paul@239 62
paul@228 63
/* Perform closing operations on a file. */
paul@228 64
paul@228 65
void Ext2FileOperations::close_file(ext2_file_t file)
paul@228 66
{
paul@228 67
    std::lock_guard<std::mutex> guard(_lock);
paul@228 68
paul@228 69
    ext2fs_file_flush(file);
paul@228 70
    ext2fs_file_close(file);
paul@228 71
}
paul@228 72
paul@148 73
/* Create a file in the directory indicated by the given inode number with the
paul@148 74
   given filename. The file is created with the given user permissions. */
paul@148 75
paul@148 76
long Ext2FileOperations::create_file(ext2_ino_t ino_parent, const char *filename,
paul@148 77
                                     user_t user, ext2_ino_t *ino)
paul@148 78
{
paul@148 79
    if (!path_is_leafname(filename))
paul@148 80
        return -L4_EINVAL;
paul@148 81
paul@149 82
    std::lock_guard<std::mutex> guard(_lock);
paul@149 83
paul@148 84
    struct ext2_inode inode_parent;
paul@148 85
    errcode_t retval = ext2fs_read_inode(_fs, ino_parent, &inode_parent);
paul@148 86
paul@148 87
    if (retval)
paul@148 88
        return -L4_EIO;
paul@148 89
paul@148 90
    if (!access_can_write(user, &inode_parent))
paul@148 91
        return -L4_EPERM;
paul@148 92
paul@148 93
    if (image_create_file(_fs, ino_parent, filename, 0666 & ~user.umask,
paul@148 94
                          user.uid, user.gid, ino))
paul@148 95
        return -L4_EIO;
paul@148 96
paul@148 97
    return L4_EOK;
paul@148 98
}
paul@148 99
paul@148 100
/* For the given 'path', return an inode number or indicate the 'remaining'
paul@148 101
   part of the path that cannot be resolved. */
paul@148 102
paul@148 103
long Ext2FileOperations::find_file(const char *path, ext2_ino_t *ino,
paul@148 104
                                   const char **remaining)
paul@148 105
{
paul@149 106
    std::lock_guard<std::mutex> guard(_lock);
paul@149 107
paul@148 108
    *remaining = path;
paul@148 109
    errcode_t retval = image_find_path(_fs, remaining, ino);
paul@148 110
paul@148 111
    // NOTE: Map error conditions.
paul@148 112
paul@148 113
    if (retval)
paul@148 114
        return -L4_EIO;
paul@148 115
paul@148 116
    return L4_EOK;
paul@148 117
}
paul@148 118
paul@148 119
/* Open the file associated with the indicated inode. */
paul@148 120
paul@148 121
long Ext2FileOperations::open_file(ext2_ino_t ino, ext2_file_t *file)
paul@148 122
{
paul@149 123
    std::lock_guard<std::mutex> guard(_lock);
paul@149 124
paul@148 125
    errcode_t retval = ext2fs_file_open(_fs, ino, EXT2_FILE_WRITE, file);
paul@148 126
paul@148 127
    // NOTE: Map error conditions.
paul@148 128
paul@148 129
    if (retval)
paul@148 130
        return -L4_EIO;
paul@148 131
paul@148 132
    return L4_EOK;
paul@148 133
}
paul@148 134
paul@232 135
/* Remove an object from a directory. */
paul@232 136
paul@232 137
long Ext2FileOperations::remove(ext2_ino_t ino)
paul@232 138
{
paul@232 139
    std::lock_guard<std::mutex> guard(_lock);
paul@232 140
paul@232 141
    errcode_t retval = image_remove_by_inode(_fs, ino);
paul@232 142
paul@232 143
    // NOTE: Map error conditions.
paul@232 144
paul@232 145
    if (retval)
paul@232 146
        return -L4_EIO;
paul@232 147
paul@232 148
    return L4_EOK;
paul@232 149
}
paul@232 150
paul@236 151
/* Rename an object. */
paul@236 152
paul@236 153
long Ext2FileOperations::rename(ext2_ino_t source,
paul@236 154
                                ext2_ino_t source_parent, const char *source_basename,
paul@236 155
                                ext2_ino_t target_parent, const char *target_basename)
paul@236 156
{
paul@236 157
    std::lock_guard<std::mutex> guard(_lock);
paul@236 158
paul@236 159
    errcode_t retval = image_rename(_fs, source, source_parent, source_basename,
paul@236 160
                                    target_parent, target_basename);
paul@236 161
paul@236 162
    // NOTE: Map error conditions.
paul@236 163
paul@236 164
    if (retval)
paul@236 165
        return -L4_EIO;
paul@236 166
paul@236 167
    return L4_EOK;
paul@236 168
}
paul@236 169
paul@232 170
/* Unlink an object from a directory. */
paul@232 171
paul@232 172
long Ext2FileOperations::unlink(ext2_ino_t ino_parent, ext2_ino_t ino)
paul@232 173
{
paul@232 174
    std::lock_guard<std::mutex> guard(_lock);
paul@232 175
paul@232 176
    errcode_t retval = image_unlink_by_inode(_fs, ino_parent, ino);
paul@232 177
paul@232 178
    // NOTE: Map error conditions.
paul@232 179
paul@232 180
    if (retval)
paul@232 181
        return -L4_EIO;
paul@232 182
paul@232 183
    return L4_EOK;
paul@232 184
}
paul@232 185
paul@236 186
paul@236 187
paul@148 188
/* Obtain the size of a file. */
paul@148 189
paul@148 190
offset_t Ext2FileOperations::get_size(ext2_file_t file)
paul@148 191
{
paul@149 192
    std::lock_guard<std::mutex> guard(_lock);
paul@149 193
paul@148 194
    return ext2fs_file_get_size(file);
paul@148 195
}
paul@148 196
paul@148 197
/* Update the size of a file. */
paul@148 198
paul@148 199
void Ext2FileOperations::set_size(ext2_file_t file, offset_t size)
paul@148 200
{
paul@149 201
    std::lock_guard<std::mutex> guard(_lock);
paul@149 202
paul@148 203
    ext2fs_file_set_size(file, size);
paul@148 204
}
paul@148 205
paul@148 206
/* Populate the given memory region with file content. */
paul@148 207
paul@148 208
offset_t Ext2FileOperations::read_file(ext2_file_t file, offset_t filepos, void *addr, offset_t size)
paul@148 209
{
paul@149 210
    std::lock_guard<std::mutex> guard(_lock);
paul@149 211
paul@148 212
    unsigned int nread;
paul@148 213
paul@148 214
    ext2fs_file_llseek(file, filepos, SEEK_SET, NULL);
paul@148 215
    ext2fs_file_read(file, (void *) addr, size, &nread);
paul@148 216
paul@148 217
    return (offset_t) nread;
paul@148 218
}
paul@148 219
paul@148 220
/* Transfer content from the given memory region to a file. */
paul@148 221
paul@148 222
void Ext2FileOperations::write_file(ext2_file_t file, offset_t filepos, const void *addr, offset_t size)
paul@148 223
{
paul@149 224
    std::lock_guard<std::mutex> guard(_lock);
paul@149 225
paul@148 226
    ext2fs_file_llseek(file, filepos, SEEK_SET, NULL);
paul@148 227
    ext2fs_file_write(file, addr, size, NULL);
paul@148 228
}
paul@148 229
paul@191 230
/* Obtain information for the given inode. */
paul@191 231
paul@191 232
long Ext2FileOperations::read_inode(ext2_ino_t ino, struct ext2_inode *inode)
paul@191 233
{
paul@191 234
    std::lock_guard<std::mutex> guard(_lock);
paul@191 235
paul@191 236
    if (ext2fs_read_inode(_fs, ino, inode))
paul@191 237
        return -L4_EIO;
paul@191 238
paul@191 239
    return L4_EOK;
paul@191 240
}
paul@191 241
paul@191 242
paul@191 243
paul@191 244
/* Directory iteration support. */
paul@191 245
paul@191 246
/* Callback function invoking the callback method. */
paul@191 247
paul@191 248
static int _directory_iterate_fn(struct ext2_dir_entry *dir_entry,
paul@191 249
                                 int offset, int blocksize, char *buf,
paul@191 250
                                 void *priv_data)
paul@191 251
{
paul@191 252
    struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data);
paul@191 253
paul@191 254
    return data->ops->directory_iterate_fn(dir_entry, offset, blocksize, buf, data);
paul@191 255
}
paul@191 256
paul@191 257
/* Callback method invoking the actual callback, with control over locking. */
paul@191 258
paul@191 259
int Ext2FileOperations::directory_iterate_fn(struct ext2_dir_entry *dir_entry,
paul@191 260
                                             int offset, int blocksize,
paul@191 261
                                             char *buf, struct Ext2DirectoryIteration *priv_data)
paul@191 262
{
paul@191 263
    /* Release the lock to allow other operations to proceed. */
paul@191 264
paul@191 265
    _lock.unlock();
paul@191 266
paul@191 267
    /* Invoke the actual callback function. */
paul@191 268
paul@191 269
    struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data);
paul@191 270
    int result = data->func(dir_entry, offset, blocksize, buf, data->priv_data);
paul@191 271
paul@191 272
    /* Acquire the lock and continue iteration. */
paul@191 273
paul@191 274
    _lock.lock();
paul@191 275
    return result;
paul@191 276
}
paul@191 277
paul@168 278
/* Initiate iteration over a directory, with the given 'func' being called
paul@168 279
   with directory entry details for each entry. */
paul@168 280
paul@168 281
long Ext2FileOperations::directory_iterate(ext2_ino_t dir,
paul@168 282
                                           int func(struct ext2_dir_entry *, int, int,
paul@168 283
                                                    char *, void *),
paul@168 284
                                           void *priv_data)
paul@168 285
{
paul@191 286
    /* NOTE: Iteration should probably be interrupted if it appears that the
paul@191 287
             directory has been modified and the structure of the data has
paul@191 288
             been changed. This condition might then cause iteration to begin
paul@191 289
             again, skipping ahead to the entry after the last visited entry. */
paul@168 290
paul@191 291
    /* Acquire the lock for iteration to proceed safely. */
paul@191 292
paul@191 293
    _lock.lock();
paul@168 294
paul@191 295
    /* Begin iteration using the given callback function to invoke a callback
paul@191 296
       method in this object that will wrap the actual callback with locking
paul@191 297
       operations. */
paul@191 298
paul@191 299
    struct Ext2DirectoryIteration data = {this, func, priv_data};
paul@191 300
    int result = ext2fs_dir_iterate(_fs, dir, 0, 0, _directory_iterate_fn, &data);
paul@168 301
paul@191 302
    /* Release the lock upon finishing iteration. */
paul@191 303
paul@191 304
    _lock.unlock();
paul@191 305
paul@191 306
    if (result)
paul@168 307
        return -L4_EIO;
paul@191 308
    else
paul@191 309
        return L4_EOK;
paul@168 310
}
paul@168 311
paul@148 312
// vim: tabstop=4 expandtab shiftwidth=4