L4Re/departure

Annotated libe2access/lib/src/image.c

249:af6019950ebe
2022-02-16 Paul Boddie Rearranged removal-related functions, exposing the semantics to e2access and to other users of the library. This reduces the activities performed by some functions, also permitting the reordering of removal operations. An adapter function has also been introduced to provide variants of operations accepting paths instead of inode numbers.
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@236 31
/* Link an inode within the given target directory having the given basename. */
paul@181 32
paul@236 33
static errcode_t _image_link(ext2_filsys fs, ext2_ino_t ino_target,
paul@236 34
                             const char *basename, ext2_ino_t ino_file,
paul@236 35
                             int flags)
paul@181 36
{
paul@181 37
    errcode_t retval;
paul@227 38
    int retry;
paul@181 39
paul@181 40
    /* Connect the inode to its parent. */
paul@181 41
paul@227 42
    for (retry = 0; retry <= 1; retry++)
paul@181 43
    {
paul@236 44
        retval = ext2fs_link(fs, ino_target, basename, ino_file, flags);
paul@181 45
paul@181 46
        if (!retval)
paul@181 47
            break;
paul@227 48
        else if (retry)
paul@227 49
            return retval;
paul@181 50
paul@181 51
        /* Expand the directory if necessary. */
paul@181 52
paul@181 53
        if (retval == EXT2_ET_DIR_NO_SPACE)
paul@181 54
            retval = ext2fs_expand_dir(fs, ino_target);
paul@181 55
paul@181 56
        if (retval)
paul@181 57
            return retval;
paul@181 58
    }
paul@181 59
paul@236 60
    return 0;
paul@236 61
}
paul@236 62
paul@248 63
/* Common parent entry state. */
paul@236 64
paul@236 65
struct _image_parent_entry_state
paul@236 66
{
paul@236 67
    ext2_ino_t ino;
paul@248 68
    int nonempty;
paul@236 69
};
paul@236 70
paul@248 71
/* Get entry details for a directory to be removed. */
paul@248 72
paul@248 73
static int _image_get_parent_entry(struct ext2_dir_entry *dir_entry,
paul@248 74
                                   int offset, int blocksize, char *buf,
paul@248 75
                                   void *priv_data)
paul@248 76
{
paul@248 77
    struct _image_parent_entry_state *state = (struct _image_parent_entry_state *) priv_data;
paul@248 78
paul@248 79
    (void) offset; (void) blocksize; (void) buf;
paul@248 80
paul@248 81
    if (!strcmp(dir_entry->name, ".."))
paul@248 82
        state->ino = dir_entry->inode;
paul@248 83
    else if (strcmp(dir_entry->name, "."))
paul@248 84
        state->nonempty = 1;
paul@248 85
paul@248 86
    return 0;
paul@248 87
}
paul@248 88
paul@248 89
/* Test for objects in a directory. */
paul@248 90
paul@248 91
static int _image_test_directory(struct ext2_dir_entry *dir_entry,
paul@248 92
                                 int offset, int blocksize, char *buf,
paul@248 93
                                 void *priv_data)
paul@248 94
{
paul@248 95
    struct _image_parent_entry_state *state = (struct _image_parent_entry_state *) priv_data;
paul@248 96
paul@248 97
    (void) offset; (void) blocksize; (void) buf;
paul@248 98
paul@248 99
    if (strcmp(dir_entry->name, ".") && strcmp(dir_entry->name, ".."))
paul@248 100
    {
paul@248 101
        state->nonempty = 1;
paul@248 102
        return DIRENT_ABORT;
paul@248 103
    }
paul@248 104
paul@248 105
    return 0;
paul@248 106
}
paul@248 107
paul@248 108
/* Update the parent entry of a renamed directory. */
paul@248 109
paul@236 110
static int _image_update_parent_entry(struct ext2_dir_entry *dir_entry,
paul@236 111
                                      int offset, int blocksize, char *buf,
paul@236 112
                                      void *priv_data)
paul@236 113
{
paul@236 114
    struct _image_parent_entry_state *state = (struct _image_parent_entry_state *) priv_data;
paul@236 115
paul@236 116
    (void) offset; (void) blocksize; (void) buf;
paul@236 117
paul@248 118
    if (!strcmp(dir_entry->name, ".."))
paul@236 119
    {
paul@236 120
        dir_entry->inode = state->ino;
paul@236 121
        return DIRENT_CHANGED | DIRENT_ABORT;
paul@236 122
    }
paul@236 123
    else
paul@236 124
        return 0;
paul@236 125
}
paul@236 126
paul@243 127
paul@243 128
paul@249 129
/* Adapter function. */
paul@249 130
paul@249 131
errcode_t image_access_by_path(ext2_filsys fs, const char *path,
paul@249 132
                               errcode_t (*op)(ext2_filsys, ext2_ino_t),
paul@249 133
                               ext2_ino_t *ino)
paul@249 134
{
paul@249 135
    const char *remaining = path;
paul@249 136
    errcode_t retval = image_find_path(fs, &remaining, ino);
paul@249 137
paul@249 138
    if (retval)
paul@249 139
        return retval;
paul@249 140
paul@249 141
    return op(fs, *ino);
paul@249 142
}
paul@249 143
paul@249 144
paul@249 145
paul@236 146
/* Create an inode for a file. */
paul@236 147
paul@236 148
errcode_t image_create_file(ext2_filsys fs, ext2_ino_t ino_target,
paul@236 149
                            const char *basename, __u16 mode,
paul@236 150
                            __u16 uid, __u16 gid, ext2_ino_t *ino_file)
paul@236 151
{
paul@236 152
    struct ext2_inode inode_file;
paul@236 153
    errcode_t retval;
paul@236 154
paul@236 155
    /* Without an inode, create a new one. */
paul@236 156
paul@236 157
    retval = ext2fs_new_inode(fs, ino_target, LINUX_S_IFREG | mode, 0, ino_file);
paul@236 158
    if (retval)
paul@236 159
        return retval;
paul@236 160
paul@236 161
    _image_link(fs, ino_target, basename, *ino_file, EXT2_FT_REG_FILE);
paul@236 162
paul@181 163
    /* Make sure that subsequent files employ different inodes. */
paul@181 164
paul@181 165
    ext2fs_inode_alloc_stats2(fs, *ino_file, 1, 0);
paul@181 166
paul@181 167
    /* Populate the inode details. */
paul@181 168
paul@181 169
    image_set_metadata(&inode_file, 1, LINUX_S_IFREG | mode, uid, gid);
paul@225 170
    inode_file.i_links_count++;
paul@181 171
paul@181 172
    return ext2fs_write_new_inode(fs, *ino_file, &inode_file);
paul@181 173
}
paul@181 174
paul@249 175
/* Test for an empty directory. */
paul@249 176
paul@249 177
errcode_t image_dir_empty_by_inode(ext2_filsys fs, ext2_ino_t ino)
paul@249 178
{
paul@249 179
    /* Initialise the directory listing processing state. */
paul@249 180
paul@249 181
    struct _image_parent_entry_state state = {.nonempty = 0};
paul@249 182
    errcode_t retval = ext2fs_dir_iterate(fs, ino, 0, NULL,
paul@249 183
                                          _image_test_directory, &state);
paul@249 184
paul@249 185
    if (retval)
paul@249 186
        return retval;
paul@249 187
paul@249 188
    /* NOTE: Need a proper error here. */
paul@249 189
paul@249 190
    return state.nonempty;
paul@249 191
}
paul@249 192
paul@249 193
/* Test for an empty directory using its path. */
paul@249 194
paul@249 195
errcode_t image_dir_empty_by_path(ext2_filsys fs, const char *path,
paul@249 196
                                  ext2_ino_t *ino)
paul@249 197
{
paul@249 198
    return image_access_by_path(fs, path, image_dir_empty_by_inode, ino);
paul@249 199
}
paul@249 200
paul@249 201
/* Find any parent reference. */
paul@249 202
paul@249 203
errcode_t image_dir_get_parent(ext2_filsys fs, ext2_ino_t ino, ext2_ino_t *ino_parent)
paul@249 204
{
paul@249 205
    /* Initialise the directory listing processing state. */
paul@249 206
paul@249 207
    struct _image_parent_entry_state state = {.ino = 0};
paul@249 208
paul@249 209
    /* A directory needs to be inspected for files and for the .. entry
paul@249 210
       to be queried to obtain the parent directory. */
paul@249 211
paul@249 212
    errcode_t retval = ext2fs_dir_iterate(fs, ino, 0, NULL,
paul@249 213
                                          _image_get_parent_entry, &state);
paul@249 214
paul@249 215
    if (!retval)
paul@249 216
        *ino_parent = state.ino;
paul@249 217
paul@249 218
    return retval;
paul@249 219
}
paul@249 220
paul@236 221
/* Return the appropriate ext2 file type value for the given mode value. */
paul@236 222
paul@236 223
int image_file_type(int mode)
paul@236 224
{
paul@236 225
    switch (mode & LINUX_S_IFMT)
paul@236 226
    {
paul@236 227
        case LINUX_S_IFSOCK: return EXT2_FT_SOCK;
paul@236 228
        case LINUX_S_IFLNK: return EXT2_FT_SYMLINK;
paul@236 229
        case LINUX_S_IFREG: return EXT2_FT_REG_FILE;
paul@236 230
        case LINUX_S_IFBLK: return EXT2_FT_BLKDEV;
paul@236 231
        case LINUX_S_IFDIR: return EXT2_FT_DIR;
paul@236 232
        case LINUX_S_IFCHR: return EXT2_FT_CHRDEV;
paul@236 233
        case LINUX_S_IFIFO: return EXT2_FT_FIFO;
paul@236 234
paul@236 235
        /* NOTE: Perhaps signal an error. */
paul@236 236
paul@236 237
        default: return EXT2_FT_REG_FILE;
paul@236 238
    }
paul@236 239
}
paul@236 240
paul@181 241
/* Find an object in the given directory with the given name in the filesystem
paul@181 242
   image, updating the name reference to refer to the next component. */
paul@181 243
paul@181 244
errcode_t image_find_next(ext2_filsys fs, ext2_ino_t ino_dir,
paul@181 245
                          const char **basename, char *buf, ext2_ino_t *ino)
paul@181 246
{
paul@181 247
    const char *end = path_component_end(*basename);
paul@181 248
    errcode_t retval;
paul@181 249
paul@181 250
    /* Find the basename in the directory. */
paul@181 251
paul@181 252
    retval = ext2fs_lookup(fs, ino_dir, *basename, end - *basename, buf, ino);
paul@181 253
paul@181 254
    /* Update the current component. */
paul@181 255
paul@181 256
    if (!retval)
paul@181 257
        *basename = path_component_next(end);
paul@181 258
paul@181 259
    return retval;
paul@181 260
}
paul@181 261
paul@181 262
/* Find an object with the given pathname in the filesystem image. */
paul@181 263
paul@181 264
errcode_t image_find_path(ext2_filsys fs, const char **pathname, ext2_ino_t *ino)
paul@181 265
{
paul@181 266
    char *buf;
paul@181 267
    ext2_ino_t ino_dir;
paul@181 268
    errcode_t retval;
paul@181 269
paul@181 270
    retval = ext2fs_get_mem(fs->blocksize, &buf);
paul@181 271
    if (retval)
paul@181 272
        return retval;
paul@181 273
paul@181 274
    /* Skip any leading root marker. */
paul@181 275
paul@181 276
    if (**pathname == '/')
paul@181 277
        (*pathname)++;
paul@181 278
paul@181 279
    if (!**pathname)
paul@181 280
        *ino = EXT2_ROOT_INO;
paul@181 281
paul@181 282
    /* Start at the root. */
paul@181 283
paul@181 284
    ino_dir = EXT2_ROOT_INO;
paul@181 285
paul@181 286
    /* With any remaining path, find the next component. */
paul@181 287
paul@181 288
    while (**pathname)
paul@181 289
    {
paul@181 290
        retval = image_find_next(fs, ino_dir, pathname, buf, ino);
paul@181 291
        if (retval)
paul@181 292
        {
paul@181 293
            *ino = ino_dir;
paul@181 294
            break;
paul@181 295
        }
paul@181 296
paul@181 297
        /* Move into the found object for searching the next component. */
paul@181 298
paul@181 299
        ino_dir = *ino;
paul@181 300
    }
paul@181 301
paul@181 302
    ext2fs_free_mem(&buf);
paul@181 303
paul@181 304
    return retval;
paul@181 305
}
paul@181 306
paul@181 307
/* Find an object in the given directory with the given name in the filesystem
paul@181 308
   image. */
paul@181 309
paul@181 310
errcode_t image_find_file(ext2_filsys fs, const char *dirname,
paul@181 311
                          const char *basename, ext2_ino_t *ino)
paul@181 312
{
paul@181 313
    char pathname[strlen(dirname) + strlen(basename) + 2];
paul@181 314
    const char *s = pathname;
paul@181 315
paul@181 316
    strcpy(pathname, dirname);
paul@181 317
    strcat(pathname, "/");
paul@181 318
    strcat(pathname, basename);
paul@181 319
paul@181 320
    return image_find_path(fs, &s, ino);
paul@181 321
}
paul@181 322
paul@181 323
/* Obtain the inode for the object with the given pathname in the filesystem
paul@181 324
   image. */
paul@181 325
paul@181 326
errcode_t image_inode(ext2_filsys fs, const char *pathname,
paul@181 327
                      struct ext2_inode *inode)
paul@181 328
{
paul@181 329
    ext2_ino_t ino;
paul@181 330
    errcode_t retval;
paul@181 331
paul@181 332
    retval = image_find_path(fs, &pathname, &ino);
paul@181 333
    if (retval)
paul@181 334
        return retval;
paul@181 335
paul@181 336
    return ext2fs_read_inode(fs, ino, inode);
paul@181 337
}
paul@181 338
paul@249 339
/* Decrement the reference count on an inode. */
paul@249 340
paul@249 341
errcode_t image_inode_decrement(ext2_filsys fs, ext2_ino_t ino)
paul@249 342
{
paul@249 343
    struct ext2_inode_large inode;
paul@249 344
    errcode_t retval;
paul@249 345
paul@249 346
    retval = ext2fs_read_inode_full(fs, ino,
paul@249 347
                                    (struct ext2_inode *) &inode,
paul@249 348
                                    sizeof(inode));
paul@249 349
paul@249 350
    if (retval)
paul@249 351
        return retval;
paul@249 352
paul@249 353
    /* NOTE: FUSE implementation tests for > 1. */
paul@249 354
paul@249 355
    if (inode.i_links_count)
paul@249 356
        inode.i_links_count--;
paul@249 357
paul@249 358
    /* NOTE: Update modification time. */
paul@249 359
paul@249 360
    return ext2fs_write_inode_full(fs, ino,
paul@249 361
                                   (struct ext2_inode *) &inode,
paul@249 362
                                   sizeof(inode));
paul@249 363
}
paul@249 364
paul@226 365
/* List a directory in the filesystem image. */
paul@226 366
paul@226 367
errcode_t image_list_dir(ext2_filsys fs, const char *path,
paul@226 368
                         int (*proc)(struct ext2_dir_entry *, int, int, char *,
paul@226 369
                                     void *),
paul@226 370
                         void *data)
paul@226 371
{
paul@226 372
    char *buf;
paul@226 373
    ext2_ino_t ino;
paul@226 374
    errcode_t retval;
paul@226 375
paul@226 376
    retval = ext2fs_get_mem(fs->blocksize, &buf);
paul@226 377
    if (retval)
paul@226 378
        return retval;
paul@226 379
paul@226 380
    /* Locate the object and test whether it is a directory. */
paul@226 381
paul@226 382
    retval = image_find_path(fs, &path, &ino);
paul@226 383
    if (retval)
paul@226 384
    {
paul@226 385
        ext2fs_free_mem(&buf);
paul@226 386
        return retval;
paul@226 387
    }
paul@226 388
paul@226 389
    if (!_image_isdir(fs, ino))
paul@226 390
        return 1;
paul@226 391
paul@226 392
    /* List the directory contents. */
paul@226 393
paul@226 394
    retval = ext2fs_dir_iterate(fs, ino, 0, buf, proc, data);
paul@226 395
    if (retval)
paul@226 396
    {
paul@226 397
        ext2fs_free_mem(&buf);
paul@226 398
        return retval;
paul@226 399
    }
paul@226 400
paul@226 401
    ext2fs_free_mem(&buf);
paul@226 402
    return 0;
paul@226 403
}
paul@226 404
paul@181 405
/* Make a directory in the given directory in the filesystem image having the
paul@181 406
   given name and metadata. */
paul@181 407
paul@181 408
errcode_t image_make_dir(ext2_filsys fs, ext2_ino_t ino_dir,
paul@181 409
                         const char *basename, __u16 mode,
paul@181 410
                         __u16 uid, __u16 gid, ext2_ino_t *ino)
paul@181 411
{
paul@181 412
    struct ext2_inode inode_dir;
paul@181 413
    errcode_t retval = 0;
paul@181 414
paul@181 415
    /* Create an inode in the directory. */
paul@181 416
paul@181 417
    retval = ext2fs_new_inode(fs, ino_dir, LINUX_S_IFDIR | mode, 0, ino);
paul@181 418
    if (retval)
paul@181 419
        return retval;
paul@181 420
paul@181 421
    /* Make the directory and update the metadata (due to ext2fs_mkdir
paul@181 422
       limitation). */
paul@181 423
paul@181 424
    retval = ext2fs_mkdir(fs, ino_dir, *ino, basename);
paul@181 425
    if (retval)
paul@181 426
        return retval;
paul@181 427
paul@181 428
    retval = ext2fs_read_inode(fs, *ino, &inode_dir);
paul@181 429
    if (retval)
paul@181 430
        return retval;
paul@181 431
paul@181 432
    image_set_metadata(&inode_dir, 0, LINUX_S_IFDIR | mode, uid, gid);
paul@181 433
    return ext2fs_write_inode(fs, *ino, &inode_dir);
paul@181 434
}
paul@181 435
paul@181 436
/* Make a directory in the given directory in the filesystem image, updating
paul@181 437
   the name reference to refer to the next component. */
paul@181 438
paul@181 439
errcode_t image_make_next_dir(ext2_filsys fs, ext2_ino_t ino_dir,
paul@181 440
                              const char **basename, __u16 mode, __u16 uid,
paul@181 441
                              __u16 gid, ext2_ino_t *ino)
paul@181 442
{
paul@181 443
    char *end = (char *) path_component_end(*basename);
paul@181 444
    char endchar = *end;
paul@181 445
    errcode_t retval = 0;
paul@181 446
paul@181 447
    /* Delimit the basename and make a directory using the inode. */
paul@181 448
paul@181 449
    if (endchar)
paul@181 450
        *end = '\0';
paul@181 451
paul@181 452
    /* Do not create directories for empty components. */
paul@181 453
paul@181 454
    if (**basename)
paul@181 455
        retval = image_make_dir(fs, ino_dir, *basename, mode, uid, gid, ino);
paul@181 456
paul@181 457
    /* Restore the path separator and update the current component. */
paul@181 458
paul@181 459
    if (endchar)
paul@181 460
        *end = '/';
paul@181 461
paul@181 462
    if (!retval)
paul@181 463
        *basename = path_component_next(end);
paul@181 464
paul@181 465
    return retval;
paul@181 466
}
paul@181 467
paul@181 468
/* Make directories descending to the given path in the filesystem image. */
paul@181 469
paul@181 470
errcode_t image_make_dirs(ext2_filsys fs, const char **pathname,
paul@181 471
                          ext2_ino_t ino_dir, __u16 mode, __u16 uid, __u16 gid)
paul@181 472
{
paul@181 473
    ext2_ino_t ino;
paul@181 474
    errcode_t retval;
paul@181 475
paul@181 476
    while (**pathname)
paul@181 477
    {
paul@181 478
        retval = image_make_next_dir(fs, ino_dir, pathname, mode, uid, gid, &ino);
paul@181 479
        if (retval)
paul@181 480
            return retval;
paul@181 481
paul@181 482
        /* Move into the created object for handling the next component. */
paul@181 483
paul@181 484
        ino_dir = ino;
paul@181 485
    }
paul@181 486
paul@181 487
    return 0;
paul@181 488
}
paul@181 489
paul@231 490
/* Remove an inode. */
paul@231 491
paul@231 492
errcode_t image_remove_by_inode(ext2_filsys fs, ext2_ino_t ino)
paul@231 493
{
paul@231 494
    struct ext2_inode_large inode;
paul@249 495
    ext2_ino_t ino_parent = 0;
paul@248 496
    errcode_t retval;
paul@248 497
paul@248 498
    if (_image_isdir(fs, ino))
paul@248 499
    {
paul@249 500
        retval = image_dir_get_parent(fs, ino, &ino_parent);
paul@248 501
        if (retval)
paul@248 502
            return retval;
paul@248 503
    }
paul@248 504
paul@248 505
    retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *) &inode,
paul@248 506
                                    sizeof(inode));
paul@231 507
paul@231 508
    /* Handle invalid inodes, ignore unreferenced inodes. */
paul@231 509
paul@248 510
    if (retval)
paul@248 511
        return retval;
paul@231 512
paul@231 513
    if (!inode.i_links_count)
paul@231 514
        return 0;
paul@231 515
paul@231 516
    /* Decrement the reference count. With no more references to the inode,
paul@231 517
       remove its resources. */
paul@231 518
paul@231 519
    inode.i_links_count--;
paul@231 520
paul@231 521
    if (!inode.i_links_count)
paul@231 522
    {
paul@248 523
        /* NOTE: Update deletion time. */
paul@248 524
paul@248 525
        retval = ext2fs_free_ext_attr(fs, ino, &inode);
paul@248 526
paul@248 527
        if (!retval)
paul@231 528
        {
paul@231 529
            /* Deallocate blocks, if appropriate. ~0ULL as the end represents
paul@231 530
               truncation. */
paul@231 531
paul@231 532
            if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *) &inode))
paul@231 533
            {
paul@249 534
                retval = ext2fs_punch(fs, ino, (struct ext2_inode *) &inode,
paul@249 535
                                      NULL, 0, ~0ULL);
paul@231 536
paul@231 537
                /* Update allocation statistics. */
paul@231 538
paul@248 539
                if (!retval)
paul@231 540
                    ext2fs_inode_alloc_stats2(fs, ino, -1,
paul@231 541
                                  LINUX_S_ISDIR(inode.i_mode));
paul@231 542
            }
paul@231 543
        }
paul@231 544
    }
paul@231 545
paul@249 546
    retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *) &inode,
paul@249 547
                                     sizeof(inode));
paul@249 548
paul@249 549
    /* Decrement the parent reference count for directories. */
paul@249 550
paul@249 551
    if (!retval && !inode.i_links_count && ino_parent)
paul@249 552
        retval = image_inode_decrement(fs, ino_parent);
paul@249 553
paul@249 554
    return retval;
paul@231 555
}
paul@231 556
paul@243 557
/* Remove a directory entry using its full path. */
paul@243 558
paul@249 559
errcode_t image_remove_by_path(ext2_filsys fs, const char *path,
paul@249 560
                               ext2_ino_t *ino)
paul@248 561
{
paul@249 562
    return image_access_by_path(fs, path, image_remove_by_inode, ino);
paul@248 563
}
paul@248 564
paul@236 565
/* Rename a file. */
paul@236 566
paul@236 567
errcode_t image_rename(ext2_filsys fs, ext2_ino_t source,
paul@236 568
                       ext2_ino_t source_parent, const char *source_basename,
paul@236 569
                       ext2_ino_t target_parent, const char *target_basename)
paul@236 570
{
paul@236 571
    errcode_t retval;
paul@236 572
    struct ext2_inode source_inode, source_parent_inode, target_parent_inode;
paul@248 573
paul@248 574
    /* Initialise the directory listing processing state to refer to the target
paul@248 575
       parent. */
paul@248 576
paul@248 577
    struct _image_parent_entry_state state = {.ino = target_parent};
paul@236 578
paul@236 579
    /* NOTE: Should check for space. */
paul@236 580
paul@236 581
    /* Obtain the source object. */
paul@236 582
paul@236 583
    retval = ext2fs_read_inode(fs, source, &source_inode);
paul@236 584
paul@236 585
    if (retval)
paul@236 586
        return retval;
paul@236 587
paul@236 588
    /* Link from the target parent. */
paul@236 589
paul@236 590
    retval = _image_link(fs, target_parent, target_basename, source,
paul@236 591
                         image_file_type(source_inode.i_mode));
paul@236 592
paul@236 593
    if (retval)
paul@236 594
        return retval;
paul@236 595
paul@236 596
    if (_image_isdir(fs, source))
paul@236 597
    {
paul@236 598
        /* Update the link count for the target. */
paul@236 599
paul@236 600
        retval = ext2fs_read_inode(fs, target_parent, &target_parent_inode);
paul@236 601
paul@236 602
        if (retval)
paul@236 603
            return retval;
paul@236 604
paul@236 605
        target_parent_inode.i_links_count++;
paul@236 606
paul@236 607
        retval = ext2fs_write_inode(fs, target_parent, &target_parent_inode);
paul@236 608
paul@236 609
        if (retval)
paul@236 610
            return retval;
paul@236 611
paul@236 612
        /* A directory needs its .. entry updating to refer to its new
paul@236 613
           parent. */
paul@236 614
paul@236 615
        retval = ext2fs_dir_iterate(fs, source, 0, NULL,
paul@236 616
                                    _image_update_parent_entry, &state);
paul@236 617
paul@236 618
        /* Update the link count for the source. */
paul@236 619
paul@236 620
        retval = ext2fs_read_inode(fs, source_parent, &source_parent_inode);
paul@236 621
paul@236 622
        if (retval)
paul@236 623
            return retval;
paul@236 624
paul@236 625
        source_parent_inode.i_links_count--;
paul@236 626
paul@236 627
        retval = ext2fs_write_inode(fs, source_parent, &source_parent_inode);
paul@236 628
paul@236 629
        if (retval)
paul@236 630
            return retval;
paul@236 631
    }
paul@236 632
paul@236 633
    /* Unlink from the source parent, doing so by name because the file is now
paul@236 634
       already linked from the target parent, and when the parents are the same,
paul@236 635
       unlinking by inode could just cause the file to disappear from the
paul@236 636
       catalogue. */
paul@236 637
paul@236 638
    retval = image_unlink_by_name(fs, source_parent, source_basename);
paul@236 639
paul@236 640
    if (retval)
paul@236 641
        return retval;
paul@236 642
paul@236 643
    return ext2fs_flush2(fs, 0);
paul@236 644
}
paul@236 645
paul@235 646
/* Set the mode, user and group metadata for a file. */
paul@235 647
paul@235 648
void image_set_metadata(struct ext2_inode *inode, int clean, __u16 mode,
paul@235 649
                        __u16 uid, __u16 gid)
paul@235 650
{
paul@235 651
    if (clean)
paul@235 652
        memset(inode, 0, sizeof(*inode));
paul@235 653
paul@235 654
    inode->i_mode = mode;
paul@235 655
    inode->i_uid = uid;
paul@235 656
    inode->i_gid = gid;
paul@235 657
}
paul@235 658
paul@235 659
/* Copy file metadata into a stat structure. */
paul@235 660
paul@235 661
errcode_t image_stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *st)
paul@235 662
{
paul@235 663
    struct ext2_inode inode;
paul@235 664
    errcode_t retval = ext2fs_read_inode(fs, ino, &inode);
paul@235 665
paul@235 666
    if (retval)
paul@235 667
        return retval;
paul@235 668
paul@235 669
    st->st_dev = 0;                       /* device identifier */
paul@235 670
    st->st_ino = ino;
paul@235 671
    st->st_mode = inode.i_mode;
paul@235 672
    st->st_nlink = inode.i_links_count;
paul@235 673
    st->st_uid = inode_uid(inode);
paul@235 674
    st->st_gid = inode_gid(inode);
paul@235 675
    st->st_rdev = 0;                      /* special file device identifier */
paul@235 676
    st->st_size = EXT2_I_SIZE(&inode);
paul@235 677
    st->st_blksize = fs->blocksize;
paul@235 678
    st->st_blocks = 0;                    /* number of 512 byte blocks allocated */
paul@235 679
    st->st_atim.tv_sec = inode.i_atime;
paul@235 680
    st->st_atim.tv_nsec = 0;              /* nanosecond resolution */
paul@235 681
    st->st_mtim.tv_sec = inode.i_mtime;
paul@235 682
    st->st_mtim.tv_nsec = 0;
paul@235 683
    st->st_ctim.tv_sec = inode.i_ctime;
paul@235 684
    st->st_ctim.tv_nsec = 0;
paul@235 685
paul@235 686
    return 0;
paul@235 687
}
paul@235 688
paul@231 689
/* Unlink a directory entry by name. */
paul@231 690
paul@231 691
errcode_t image_unlink_by_name(ext2_filsys fs, ext2_ino_t ino_parent,
paul@231 692
                               const char *basename)
paul@231 693
{
paul@243 694
    return ext2fs_unlink(fs, ino_parent, basename, 0, 0);
paul@243 695
}
paul@243 696
paul@243 697
/* Unlink a directory entry by full path. */
paul@243 698
paul@243 699
errcode_t image_unlink_by_path(ext2_filsys fs, const char *path)
paul@243 700
{
paul@243 701
    char _path[strlen(path) + 1];
paul@243 702
    const char *remaining = _path;
paul@243 703
    char *basename;
paul@243 704
    ext2_ino_t ino_parent;
paul@243 705
    errcode_t retval;
paul@243 706
paul@245 707
    /* Copy the path and split it. */
paul@245 708
paul@243 709
    strcpy(_path, path);
paul@243 710
    basename = path_split(_path);
paul@243 711
paul@245 712
    /* Determine the parent directory. */
paul@245 713
paul@245 714
    if (basename != _path)
paul@245 715
    {
paul@245 716
        retval = image_find_path(fs, &remaining, &ino_parent);
paul@243 717
paul@245 718
        if (retval)
paul@245 719
            return retval;
paul@245 720
    }
paul@245 721
    else
paul@245 722
        ino_parent = EXT2_ROOT_INO;
paul@236 723
paul@231 724
    return ext2fs_unlink(fs, ino_parent, basename, 0, 0);
paul@231 725
}
paul@231 726
paul@231 727
/* Unlink a directory entry by inode number. */
paul@231 728
paul@231 729
errcode_t image_unlink_by_inode(ext2_filsys fs, ext2_ino_t ino_parent,
paul@231 730
                                ext2_ino_t ino)
paul@231 731
{
paul@231 732
    return ext2fs_unlink(fs, ino_parent, 0, ino, 0);
paul@231 733
}
paul@231 734
paul@243 735
paul@243 736
paul@243 737
/* Test object presence and types in the filesystem image. */
paul@243 738
paul@243 739
int image_exists(ext2_filsys fs, const char *name)
paul@243 740
{
paul@243 741
    ext2_ino_t ino;
paul@243 742
paul@243 743
    return !image_find_path(fs, &name, &ino);
paul@243 744
}
paul@181 745
paul@181 746
int _image_isdir(ext2_filsys fs, ext2_ino_t ino)
paul@181 747
{
paul@181 748
    struct ext2_inode inode;
paul@181 749
paul@181 750
    if (ext2fs_read_inode(fs, ino, &inode))
paul@181 751
        return 0;
paul@181 752
paul@181 753
    return LINUX_S_ISDIR(inode.i_mode);
paul@181 754
}
paul@181 755
paul@181 756
int image_isdir(ext2_filsys fs, const char *name)
paul@181 757
{
paul@181 758
    ext2_ino_t ino;
paul@181 759
paul@181 760
    if (image_find_path(fs, &name, &ino))
paul@181 761
        return 0;
paul@181 762
paul@181 763
    return _image_isdir(fs, ino);
paul@181 764
}
paul@181 765
paul@181 766
int _image_isfile(ext2_filsys fs, ext2_ino_t ino)
paul@181 767
{
paul@181 768
    struct ext2_inode inode;
paul@181 769
paul@181 770
    if (ext2fs_read_inode(fs, ino, &inode))
paul@181 771
        return 0;
paul@181 772
paul@181 773
    return LINUX_S_ISREG(inode.i_mode);
paul@181 774
}
paul@181 775
paul@181 776
int image_isfile(ext2_filsys fs, const char *name)
paul@181 777
{
paul@181 778
    ext2_ino_t ino;
paul@181 779
paul@181 780
    if (image_find_path(fs, &name, &ino))
paul@181 781
        return 0;
paul@181 782
paul@181 783
    return _image_isfile(fs, ino);
paul@181 784
}
paul@236 785
paul@236 786
/* vim: tabstop=4 expandtab shiftwidth=4
paul@236 787
*/