L4Re/departure

Annotated libe2access/lib/src/image.c

235:472c1e2b0302
2022-01-14 Paul Boddie Reordered functions and declarations.
paul@181 1
/*
paul@181 2
 * Filesystem access functions.
paul@181 3
 *
paul@235 4
 * Copyright (C) 2019, 2021, 2022 Paul Boddie <paul@boddie.org.uk>
paul@181 5
 *
paul@181 6
 * This program is free software; you can redistribute it and/or
paul@181 7
 * modify it under the terms of the GNU General Public License as
paul@181 8
 * published by the Free Software Foundation; either version 2 of
paul@181 9
 * the License, or (at your option) any later version.
paul@181 10
 *
paul@181 11
 * This program is distributed in the hope that it will be useful,
paul@181 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@181 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@181 14
 * GNU General Public License for more details.
paul@181 15
 *
paul@181 16
 * You should have received a copy of the GNU General Public License
paul@181 17
 * along with this program; if not, write to the Free Software
paul@181 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@181 19
 * Boston, MA  02110-1301, USA
paul@181 20
 */
paul@181 21
paul@181 22
#include <string.h>
paul@181 23
paul@181 24
#include <ext2fs/ext2fs.h>
paul@181 25
paul@181 26
#include "image.h"
paul@181 27
#include "path.h"
paul@181 28
paul@181 29
paul@181 30
paul@181 31
/* Create an inode for a file. */
paul@181 32
paul@181 33
errcode_t image_create_file(ext2_filsys fs, ext2_ino_t ino_target,
paul@181 34
                            const char *basename, __u16 mode,
paul@181 35
                            __u16 uid, __u16 gid, ext2_ino_t *ino_file)
paul@181 36
{
paul@181 37
    struct ext2_inode inode_file;
paul@181 38
    errcode_t retval;
paul@227 39
    int retry;
paul@181 40
paul@181 41
    /* Without an inode, create a new one. */
paul@181 42
paul@181 43
    retval = ext2fs_new_inode(fs, ino_target, LINUX_S_IFREG | mode, 0, ino_file);
paul@181 44
    if (retval)
paul@181 45
        return retval;
paul@181 46
paul@181 47
    /* Connect the inode to its parent. */
paul@181 48
paul@227 49
    for (retry = 0; retry <= 1; retry++)
paul@181 50
    {
paul@181 51
        retval = ext2fs_link(fs, ino_target, basename, *ino_file,
paul@181 52
                             EXT2_FT_REG_FILE);
paul@181 53
paul@181 54
        if (!retval)
paul@181 55
            break;
paul@227 56
        else if (retry)
paul@227 57
            return retval;
paul@181 58
paul@181 59
        /* Expand the directory if necessary. */
paul@181 60
paul@181 61
        if (retval == EXT2_ET_DIR_NO_SPACE)
paul@181 62
            retval = ext2fs_expand_dir(fs, ino_target);
paul@181 63
paul@181 64
        if (retval)
paul@181 65
            return retval;
paul@181 66
    }
paul@181 67
paul@181 68
    /* Make sure that subsequent files employ different inodes. */
paul@181 69
paul@181 70
    ext2fs_inode_alloc_stats2(fs, *ino_file, 1, 0);
paul@181 71
paul@181 72
    /* Populate the inode details. */
paul@181 73
paul@181 74
    image_set_metadata(&inode_file, 1, LINUX_S_IFREG | mode, uid, gid);
paul@225 75
    inode_file.i_links_count++;
paul@181 76
paul@181 77
    return ext2fs_write_new_inode(fs, *ino_file, &inode_file);
paul@181 78
}
paul@181 79
paul@181 80
/* Find an object in the given directory with the given name in the filesystem
paul@181 81
   image, updating the name reference to refer to the next component. */
paul@181 82
paul@181 83
errcode_t image_find_next(ext2_filsys fs, ext2_ino_t ino_dir,
paul@181 84
                          const char **basename, char *buf, ext2_ino_t *ino)
paul@181 85
{
paul@181 86
    const char *end = path_component_end(*basename);
paul@181 87
    errcode_t retval;
paul@181 88
paul@181 89
    /* Find the basename in the directory. */
paul@181 90
paul@181 91
    retval = ext2fs_lookup(fs, ino_dir, *basename, end - *basename, buf, ino);
paul@181 92
paul@181 93
    /* Update the current component. */
paul@181 94
paul@181 95
    if (!retval)
paul@181 96
        *basename = path_component_next(end);
paul@181 97
paul@181 98
    return retval;
paul@181 99
}
paul@181 100
paul@181 101
/* Find an object with the given pathname in the filesystem image. */
paul@181 102
paul@181 103
errcode_t image_find_path(ext2_filsys fs, const char **pathname, ext2_ino_t *ino)
paul@181 104
{
paul@181 105
    char *buf;
paul@181 106
    ext2_ino_t ino_dir;
paul@181 107
    errcode_t retval;
paul@181 108
paul@181 109
    retval = ext2fs_get_mem(fs->blocksize, &buf);
paul@181 110
    if (retval)
paul@181 111
        return retval;
paul@181 112
paul@181 113
    /* Skip any leading root marker. */
paul@181 114
paul@181 115
    if (**pathname == '/')
paul@181 116
        (*pathname)++;
paul@181 117
paul@181 118
    if (!**pathname)
paul@181 119
        *ino = EXT2_ROOT_INO;
paul@181 120
paul@181 121
    /* Start at the root. */
paul@181 122
paul@181 123
    ino_dir = EXT2_ROOT_INO;
paul@181 124
paul@181 125
    /* With any remaining path, find the next component. */
paul@181 126
paul@181 127
    while (**pathname)
paul@181 128
    {
paul@181 129
        retval = image_find_next(fs, ino_dir, pathname, buf, ino);
paul@181 130
        if (retval)
paul@181 131
        {
paul@181 132
            *ino = ino_dir;
paul@181 133
            break;
paul@181 134
        }
paul@181 135
paul@181 136
        /* Move into the found object for searching the next component. */
paul@181 137
paul@181 138
        ino_dir = *ino;
paul@181 139
    }
paul@181 140
paul@181 141
    ext2fs_free_mem(&buf);
paul@181 142
paul@181 143
    return retval;
paul@181 144
}
paul@181 145
paul@181 146
/* Find an object in the given directory with the given name in the filesystem
paul@181 147
   image. */
paul@181 148
paul@181 149
errcode_t image_find_file(ext2_filsys fs, const char *dirname,
paul@181 150
                          const char *basename, ext2_ino_t *ino)
paul@181 151
{
paul@181 152
    char pathname[strlen(dirname) + strlen(basename) + 2];
paul@181 153
    const char *s = pathname;
paul@181 154
paul@181 155
    strcpy(pathname, dirname);
paul@181 156
    strcat(pathname, "/");
paul@181 157
    strcat(pathname, basename);
paul@181 158
paul@181 159
    return image_find_path(fs, &s, ino);
paul@181 160
}
paul@181 161
paul@181 162
/* Obtain the inode for the object with the given pathname in the filesystem
paul@181 163
   image. */
paul@181 164
paul@181 165
errcode_t image_inode(ext2_filsys fs, const char *pathname,
paul@181 166
                      struct ext2_inode *inode)
paul@181 167
{
paul@181 168
    ext2_ino_t ino;
paul@181 169
    errcode_t retval;
paul@181 170
paul@181 171
    retval = image_find_path(fs, &pathname, &ino);
paul@181 172
    if (retval)
paul@181 173
        return retval;
paul@181 174
paul@181 175
    return ext2fs_read_inode(fs, ino, inode);
paul@181 176
}
paul@181 177
paul@226 178
/* List a directory in the filesystem image. */
paul@226 179
paul@226 180
errcode_t image_list_dir(ext2_filsys fs, const char *path,
paul@226 181
                         int (*proc)(struct ext2_dir_entry *, int, int, char *,
paul@226 182
                                     void *),
paul@226 183
                         void *data)
paul@226 184
{
paul@226 185
    char *buf;
paul@226 186
    ext2_ino_t ino;
paul@226 187
    errcode_t retval;
paul@226 188
paul@226 189
    retval = ext2fs_get_mem(fs->blocksize, &buf);
paul@226 190
    if (retval)
paul@226 191
        return retval;
paul@226 192
paul@226 193
    /* Locate the object and test whether it is a directory. */
paul@226 194
paul@226 195
    retval = image_find_path(fs, &path, &ino);
paul@226 196
    if (retval)
paul@226 197
    {
paul@226 198
        ext2fs_free_mem(&buf);
paul@226 199
        return retval;
paul@226 200
    }
paul@226 201
paul@226 202
    if (!_image_isdir(fs, ino))
paul@226 203
        return 1;
paul@226 204
paul@226 205
    /* List the directory contents. */
paul@226 206
paul@226 207
    retval = ext2fs_dir_iterate(fs, ino, 0, buf, proc, data);
paul@226 208
    if (retval)
paul@226 209
    {
paul@226 210
        ext2fs_free_mem(&buf);
paul@226 211
        return retval;
paul@226 212
    }
paul@226 213
paul@226 214
    ext2fs_free_mem(&buf);
paul@226 215
    return 0;
paul@226 216
}
paul@226 217
paul@181 218
/* Make a directory in the given directory in the filesystem image having the
paul@181 219
   given name and metadata. */
paul@181 220
paul@181 221
errcode_t image_make_dir(ext2_filsys fs, ext2_ino_t ino_dir,
paul@181 222
                         const char *basename, __u16 mode,
paul@181 223
                         __u16 uid, __u16 gid, ext2_ino_t *ino)
paul@181 224
{
paul@181 225
    struct ext2_inode inode_dir;
paul@181 226
    errcode_t retval = 0;
paul@181 227
paul@181 228
    /* Create an inode in the directory. */
paul@181 229
paul@181 230
    retval = ext2fs_new_inode(fs, ino_dir, LINUX_S_IFDIR | mode, 0, ino);
paul@181 231
    if (retval)
paul@181 232
        return retval;
paul@181 233
paul@181 234
    /* Make the directory and update the metadata (due to ext2fs_mkdir
paul@181 235
       limitation). */
paul@181 236
paul@181 237
    retval = ext2fs_mkdir(fs, ino_dir, *ino, basename);
paul@181 238
    if (retval)
paul@181 239
        return retval;
paul@181 240
paul@181 241
    retval = ext2fs_read_inode(fs, *ino, &inode_dir);
paul@181 242
    if (retval)
paul@181 243
        return retval;
paul@181 244
paul@181 245
    image_set_metadata(&inode_dir, 0, LINUX_S_IFDIR | mode, uid, gid);
paul@181 246
    return ext2fs_write_inode(fs, *ino, &inode_dir);
paul@181 247
}
paul@181 248
paul@181 249
/* Make a directory in the given directory in the filesystem image, updating
paul@181 250
   the name reference to refer to the next component. */
paul@181 251
paul@181 252
errcode_t image_make_next_dir(ext2_filsys fs, ext2_ino_t ino_dir,
paul@181 253
                              const char **basename, __u16 mode, __u16 uid,
paul@181 254
                              __u16 gid, ext2_ino_t *ino)
paul@181 255
{
paul@181 256
    char *end = (char *) path_component_end(*basename);
paul@181 257
    char endchar = *end;
paul@181 258
    errcode_t retval = 0;
paul@181 259
paul@181 260
    /* Delimit the basename and make a directory using the inode. */
paul@181 261
paul@181 262
    if (endchar)
paul@181 263
        *end = '\0';
paul@181 264
paul@181 265
    /* Do not create directories for empty components. */
paul@181 266
paul@181 267
    if (**basename)
paul@181 268
        retval = image_make_dir(fs, ino_dir, *basename, mode, uid, gid, ino);
paul@181 269
paul@181 270
    /* Restore the path separator and update the current component. */
paul@181 271
paul@181 272
    if (endchar)
paul@181 273
        *end = '/';
paul@181 274
paul@181 275
    if (!retval)
paul@181 276
        *basename = path_component_next(end);
paul@181 277
paul@181 278
    return retval;
paul@181 279
}
paul@181 280
paul@181 281
/* Make directories descending to the given path in the filesystem image. */
paul@181 282
paul@181 283
errcode_t image_make_dirs(ext2_filsys fs, const char **pathname,
paul@181 284
                          ext2_ino_t ino_dir, __u16 mode, __u16 uid, __u16 gid)
paul@181 285
{
paul@181 286
    ext2_ino_t ino;
paul@181 287
    errcode_t retval;
paul@181 288
paul@181 289
    while (**pathname)
paul@181 290
    {
paul@181 291
        retval = image_make_next_dir(fs, ino_dir, pathname, mode, uid, gid, &ino);
paul@181 292
        if (retval)
paul@181 293
            return retval;
paul@181 294
paul@181 295
        /* Move into the created object for handling the next component. */
paul@181 296
paul@181 297
        ino_dir = ino;
paul@181 298
    }
paul@181 299
paul@181 300
    return 0;
paul@181 301
}
paul@181 302
paul@231 303
/* Remove an inode. */
paul@231 304
paul@231 305
errcode_t image_remove_by_inode(ext2_filsys fs, ext2_ino_t ino)
paul@231 306
{
paul@231 307
    struct ext2_inode_large inode;
paul@231 308
    errcode_t err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *) &inode,
paul@231 309
                                           sizeof(inode));
paul@231 310
paul@231 311
    /* Handle invalid inodes, ignore unreferenced inodes. */
paul@231 312
paul@231 313
    if (err)
paul@231 314
        return err;
paul@231 315
paul@231 316
    if (!inode.i_links_count)
paul@231 317
        return 0;
paul@231 318
paul@231 319
    /* Decrement the reference count. With no more references to the inode,
paul@231 320
       remove its resources. */
paul@231 321
paul@231 322
    inode.i_links_count--;
paul@231 323
paul@231 324
    if (!inode.i_links_count)
paul@231 325
    {
paul@231 326
        err = ext2fs_free_ext_attr(fs, ino, &inode);
paul@231 327
paul@231 328
        if (!err)
paul@231 329
        {
paul@231 330
            /* Deallocate blocks, if appropriate. ~0ULL as the end represents
paul@231 331
               truncation. */
paul@231 332
paul@231 333
            if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *) &inode))
paul@231 334
            {
paul@231 335
                err = ext2fs_punch(fs, ino, (struct ext2_inode *) &inode, NULL,
paul@231 336
                                   0, ~0ULL);
paul@231 337
paul@231 338
                /* Update allocation statistics. */
paul@231 339
paul@231 340
                if (!err)
paul@231 341
                    ext2fs_inode_alloc_stats2(fs, ino, -1,
paul@231 342
                                  LINUX_S_ISDIR(inode.i_mode));
paul@231 343
            }
paul@231 344
        }
paul@231 345
    }
paul@231 346
paul@231 347
    return ext2fs_write_inode_full(fs, ino, (struct ext2_inode *) &inode,
paul@231 348
                                   sizeof(inode));
paul@231 349
}
paul@231 350
paul@235 351
/* Set the mode, user and group metadata for a file. */
paul@235 352
paul@235 353
void image_set_metadata(struct ext2_inode *inode, int clean, __u16 mode,
paul@235 354
                        __u16 uid, __u16 gid)
paul@235 355
{
paul@235 356
    if (clean)
paul@235 357
        memset(inode, 0, sizeof(*inode));
paul@235 358
paul@235 359
    inode->i_mode = mode;
paul@235 360
    inode->i_uid = uid;
paul@235 361
    inode->i_gid = gid;
paul@235 362
}
paul@235 363
paul@235 364
/* Copy file metadata into a stat structure. */
paul@235 365
paul@235 366
errcode_t image_stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *st)
paul@235 367
{
paul@235 368
    struct ext2_inode inode;
paul@235 369
    errcode_t retval = ext2fs_read_inode(fs, ino, &inode);
paul@235 370
paul@235 371
    if (retval)
paul@235 372
        return retval;
paul@235 373
paul@235 374
    st->st_dev = 0;                       /* device identifier */
paul@235 375
    st->st_ino = ino;
paul@235 376
    st->st_mode = inode.i_mode;
paul@235 377
    st->st_nlink = inode.i_links_count;
paul@235 378
    st->st_uid = inode_uid(inode);
paul@235 379
    st->st_gid = inode_gid(inode);
paul@235 380
    st->st_rdev = 0;                      /* special file device identifier */
paul@235 381
    st->st_size = EXT2_I_SIZE(&inode);
paul@235 382
    st->st_blksize = fs->blocksize;
paul@235 383
    st->st_blocks = 0;                    /* number of 512 byte blocks allocated */
paul@235 384
    st->st_atim.tv_sec = inode.i_atime;
paul@235 385
    st->st_atim.tv_nsec = 0;              /* nanosecond resolution */
paul@235 386
    st->st_mtim.tv_sec = inode.i_mtime;
paul@235 387
    st->st_mtim.tv_nsec = 0;
paul@235 388
    st->st_ctim.tv_sec = inode.i_ctime;
paul@235 389
    st->st_ctim.tv_nsec = 0;
paul@235 390
paul@235 391
    return 0;
paul@235 392
}
paul@235 393
paul@231 394
/* Unlink a directory entry by name. */
paul@231 395
paul@231 396
errcode_t image_unlink_by_name(ext2_filsys fs, ext2_ino_t ino_parent,
paul@231 397
                               const char *basename)
paul@231 398
{
paul@231 399
    return ext2fs_unlink(fs, ino_parent, basename, 0, 0);
paul@231 400
}
paul@231 401
paul@231 402
/* Unlink a directory entry by inode number. */
paul@231 403
paul@231 404
errcode_t image_unlink_by_inode(ext2_filsys fs, ext2_ino_t ino_parent,
paul@231 405
                                ext2_ino_t ino)
paul@231 406
{
paul@231 407
    return ext2fs_unlink(fs, ino_parent, 0, ino, 0);
paul@231 408
}
paul@231 409
paul@181 410
/* Test object types in the filesystem image. */
paul@181 411
paul@181 412
int _image_isdir(ext2_filsys fs, ext2_ino_t ino)
paul@181 413
{
paul@181 414
    struct ext2_inode inode;
paul@181 415
paul@181 416
    if (ext2fs_read_inode(fs, ino, &inode))
paul@181 417
        return 0;
paul@181 418
paul@181 419
    return LINUX_S_ISDIR(inode.i_mode);
paul@181 420
}
paul@181 421
paul@181 422
int image_isdir(ext2_filsys fs, const char *name)
paul@181 423
{
paul@181 424
    ext2_ino_t ino;
paul@181 425
paul@181 426
    if (image_find_path(fs, &name, &ino))
paul@181 427
        return 0;
paul@181 428
paul@181 429
    return _image_isdir(fs, ino);
paul@181 430
}
paul@181 431
paul@181 432
int _image_isfile(ext2_filsys fs, ext2_ino_t ino)
paul@181 433
{
paul@181 434
    struct ext2_inode inode;
paul@181 435
paul@181 436
    if (ext2fs_read_inode(fs, ino, &inode))
paul@181 437
        return 0;
paul@181 438
paul@181 439
    return LINUX_S_ISREG(inode.i_mode);
paul@181 440
}
paul@181 441
paul@181 442
int image_isfile(ext2_filsys fs, const char *name)
paul@181 443
{
paul@181 444
    ext2_ino_t ino;
paul@181 445
paul@181 446
    if (image_find_path(fs, &name, &ino))
paul@181 447
        return 0;
paul@181 448
paul@181 449
    return _image_isfile(fs, ino);
paul@181 450
}