L4Re/departure

Annotated libe2access/host/e2access.c

241:f3ae5c354fe4
2022-02-13 Paul Boddie Added uid, gid, umask options and tidied up argument processing.
paul@226 1
/*
paul@226 2
 * Access a filesystem.
paul@226 3
 *
paul@226 4
 * Copyright (C) 2019 Paul Boddie <paul@boddie.org.uk>
paul@226 5
 *
paul@226 6
 * This program is free software; you can redistribute it and/or
paul@226 7
 * modify it under the terms of the GNU General Public License as
paul@226 8
 * published by the Free Software Foundation; either version 2 of
paul@226 9
 * the License, or (at your option) any later version.
paul@226 10
 *
paul@226 11
 * This program is distributed in the hope that it will be useful,
paul@226 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@226 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@226 14
 * GNU General Public License for more details.
paul@226 15
 *
paul@226 16
 * You should have received a copy of the GNU General Public License
paul@226 17
 * along with this program; if not, write to the Free Software
paul@226 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@226 19
 * Boston, MA  02110-1301, USA
paul@226 20
 */
paul@226 21
paul@226 22
#include <stdio.h>
paul@226 23
#include <stdlib.h>
paul@226 24
#include <string.h>
paul@241 25
#include <unistd.h>
paul@226 26
paul@226 27
#include <sys/types.h>
paul@226 28
#include <sys/stat.h>
paul@226 29
paul@226 30
#include <ext2fs/ext2fs.h>
paul@226 31
paul@226 32
#include "file.h"
paul@226 33
#include "format.h"
paul@226 34
#include "image.h"
paul@226 35
#include "path.h"
paul@226 36
paul@226 37
paul@226 38
paul@226 39
/* Copy buffer size. */
paul@226 40
paul@226 41
const int BUFSIZE = 4096;
paul@226 42
paul@226 43
paul@226 44
paul@241 45
/* Alternative metadata set by options. */
paul@241 46
paul@241 47
struct metadata
paul@241 48
{
paul@241 49
    uid_t uid;
paul@241 50
    gid_t gid;
paul@241 51
    mode_t mask;
paul@241 52
    int have_uid, have_gid;
paul@241 53
};
paul@241 54
paul@241 55
struct metadata md;
paul@241 56
paul@241 57
static int parse_options(int argc, char *argv[])
paul@241 58
{
paul@241 59
    int opt;
paul@241 60
paul@241 61
    md.have_uid = 0;
paul@241 62
    md.have_gid = 0;
paul@241 63
    md.mask = 0000;
paul@241 64
paul@241 65
    while ((opt = getopt(argc, argv, "g:m:u:")) != -1)
paul@241 66
    {
paul@241 67
        switch (opt)
paul@241 68
        {
paul@241 69
            case 'g':
paul@241 70
            md.gid = atoi(optarg);
paul@241 71
            md.have_gid = 1;
paul@241 72
            break;
paul@241 73
paul@241 74
            case 'm':
paul@241 75
            md.mask = strtol(optarg, NULL, 0);
paul@241 76
            break;
paul@241 77
paul@241 78
            case 'u':
paul@241 79
            md.uid = atoi(optarg);
paul@241 80
            md.have_uid = 1;
paul@241 81
            break;
paul@241 82
paul@241 83
            default:
paul@241 84
            fprintf(stderr, "Option %s not recognised.\n", argv[optind]);
paul@241 85
            return -1;
paul@241 86
        }
paul@241 87
    }
paul@241 88
paul@241 89
    return 0;
paul@241 90
}
paul@241 91
paul@241 92
paul@241 93
paul@226 94
/* Show directory entries when iterating. */
paul@226 95
paul@226 96
static int image_list_dir_proc(struct ext2_dir_entry *dirent, int offset,
paul@226 97
                               int blocksize, char *buf, void *priv_data)
paul@226 98
{
paul@226 99
    ext2_filsys fs = (ext2_filsys) priv_data;
paul@226 100
    struct ext2_inode inode;
paul@226 101
paul@226 102
    /* Obtain the inode details for metadata. */
paul@226 103
paul@226 104
    if (ext2fs_read_inode(fs, dirent->inode, &inode))
paul@226 105
        return DIRENT_ABORT;
paul@226 106
paul@226 107
    /* Output details in the style of "ls -l" showing directory, permissions,
paul@226 108
       owner, group and size information. */
paul@226 109
paul@226 110
    printf("%s%s   %5d %5d      %6d ",
paul@226 111
        _image_isdir(fs, dirent->inode) ? "d" : "-",
paul@226 112
        get_permission_string(inode.i_mode),
paul@226 113
        inode.i_uid,
paul@226 114
        inode.i_gid,
paul@226 115
        EXT2_I_SIZE(&inode));
paul@226 116
paul@226 117
    /* Output the name which is presumably not necessarily null-terminated. */
paul@226 118
paul@226 119
    fwrite(dirent->name, sizeof(char), ext2fs_dirent_name_len(dirent), stdout);
paul@226 120
    fputc((int) '\n', stdout);
paul@226 121
    return 0;
paul@226 122
}
paul@226 123
paul@226 124
paul@226 125
paul@226 126
/* Copy a file into the filesystem image. */
paul@226 127
paul@226 128
int copy_file_in(const char *filename, ext2_filsys fs, ext2_ino_t ino_file, int flags)
paul@226 129
{
paul@226 130
    int retval = 0;
paul@226 131
    ext2_file_t file;
paul@226 132
paul@226 133
    /* Copying details. */
paul@226 134
paul@226 135
    FILE *fp;
paul@226 136
    char buf[BUFSIZE];
paul@226 137
    size_t got;
paul@226 138
    unsigned int written;
paul@226 139
paul@226 140
    /* Open a file in the target directory. */
paul@226 141
paul@226 142
    if (ext2fs_file_open(fs, ino_file, flags, &file))
paul@226 143
        return 1;
paul@226 144
paul@226 145
    /* Open the file in the source directory. */
paul@226 146
paul@226 147
    fp = fopen(filename, "r");
paul@226 148
paul@226 149
    /* Copy the file content. */
paul@226 150
paul@226 151
    if (fp != NULL)
paul@226 152
    {
paul@226 153
        while (got = fread(buf, sizeof(char), BUFSIZE, fp))
paul@226 154
        {
paul@226 155
            while (got)
paul@226 156
            {
paul@226 157
                if (ext2fs_file_write(file, buf, got, &written))
paul@226 158
                {
paul@226 159
                    retval = 1;
paul@226 160
                    goto close_files;
paul@226 161
                }
paul@226 162
                got -= written;
paul@226 163
            }
paul@226 164
        }
paul@226 165
    }
paul@226 166
paul@226 167
close_files:
paul@226 168
    fclose(fp);
paul@226 169
    ext2fs_file_flush(file);
paul@226 170
    ext2fs_file_close(file);
paul@226 171
paul@226 172
    return retval;
paul@226 173
}
paul@226 174
paul@226 175
/* Copy a file out of the filesystem image. */
paul@226 176
paul@226 177
int copy_file_out(const char *target, const char *filename, int target_is_file,
paul@226 178
                  ext2_filsys fs, ext2_ino_t ino_file)
paul@226 179
{
paul@226 180
    int retval = 0;
paul@226 181
    ext2_file_t file;
paul@226 182
paul@226 183
    /* Copying details. */
paul@226 184
paul@226 185
    FILE *fp;
paul@226 186
    char buf[BUFSIZE];
paul@226 187
    unsigned int got;
paul@226 188
    size_t written;
paul@226 189
paul@226 190
    /* Open the file in the source directory. */
paul@226 191
paul@226 192
    if (ext2fs_file_open(fs, ino_file, 0, &file))
paul@226 193
        return 1;
paul@226 194
paul@226 195
    /* Open a file in the target directory. */
paul@226 196
paul@226 197
    if (target_is_file)
paul@226 198
        fp = fopen(target, "w");
paul@226 199
    else
paul@226 200
        fp = open_file_in_dir(target, path_basename(filename), "w");
paul@226 201
paul@226 202
    /* Copy the file content. */
paul@226 203
paul@226 204
    if (fp != NULL)
paul@226 205
    {
paul@226 206
        do
paul@226 207
        {
paul@226 208
            if (ext2fs_file_read(file, buf, BUFSIZE, &got))
paul@226 209
            {
paul@226 210
                retval = 1;
paul@226 211
                goto close_files;
paul@226 212
            }
paul@226 213
paul@226 214
            while (got)
paul@226 215
            {
paul@226 216
                written = fwrite(buf, sizeof(char), got, fp);
paul@226 217
                got -= written;
paul@226 218
            }
paul@226 219
paul@226 220
        } while (got);
paul@226 221
    }
paul@226 222
paul@226 223
close_files:
paul@226 224
    fclose(fp);
paul@226 225
    ext2fs_file_close(file);
paul@226 226
paul@226 227
    return retval;
paul@226 228
}
paul@226 229
paul@226 230
paul@226 231
paul@226 232
/* Copy source files from the external environment into the filesystem image. */
paul@226 233
paul@226 234
int copy_in(ext2_filsys fs, int argc, char *argv[])
paul@226 235
{
paul@226 236
    errcode_t retval;
paul@226 237
paul@226 238
    /* Target filename details. */
paul@226 239
paul@226 240
    const char *target = argv[argc - 1];
paul@226 241
    const char *target_remaining = argv[argc - 1];
paul@226 242
    const char *basename;
paul@226 243
    int target_is_file;
paul@226 244
paul@226 245
    /* Target file and directory details. */
paul@226 246
paul@226 247
    int target_is_new;
paul@226 248
    ext2_ino_t ino_file, ino_target;
paul@226 249
    int flags;
paul@226 250
paul@226 251
    /* Source file details. */
paul@226 252
paul@226 253
    struct stat st;
paul@226 254
    int i;
paul@226 255
paul@226 256
    /* Locate the target and test whether it is a file or a directory. */
paul@226 257
paul@226 258
    if (image_find_path(fs, &target_remaining, &ino_target))
paul@226 259
    {
paul@226 260
        /* Only a non-existent file in an existing directory is permitted. */
paul@226 261
paul@226 262
        if (!_image_isdir(fs, ino_target) || !path_is_leafname(target_remaining))
paul@226 263
        {
paul@226 264
            printf("Target %s not found.\n", target);
paul@226 265
            return 1;
paul@226 266
        }
paul@226 267
paul@226 268
        target_is_file = 1;
paul@226 269
        target_is_new = 1;
paul@226 270
    }
paul@226 271
    else
paul@226 272
    {
paul@226 273
        target_is_file = _image_isfile(fs, ino_target);
paul@226 274
        target_is_new = 0;
paul@226 275
    }
paul@226 276
paul@226 277
    /* Only permit a target file when one source file is given. */
paul@226 278
paul@226 279
    if (target_is_file)
paul@226 280
    {
paul@226 281
        if (argc > 2)
paul@226 282
        {
paul@226 283
            printf("Target %s can only be a file when copying a single file.\n", target);
paul@226 284
            return 1;
paul@226 285
        }
paul@226 286
    }
paul@226 287
    else if (!_image_isdir(fs, ino_target))
paul@226 288
    {
paul@226 289
        printf("Target %s is not a directory.\n", target);
paul@226 290
        return 1;
paul@226 291
    }
paul@226 292
paul@226 293
    /* Copy each source object to the target directory. */
paul@226 294
paul@226 295
    for (i = 0; i < argc - 1; i++)
paul@226 296
    {
paul@226 297
        if (target_is_file)
paul@226 298
            basename = target_remaining;
paul@226 299
        else
paul@226 300
        {
paul@226 301
            basename = path_basename(argv[i]);
paul@226 302
            target_is_new = image_find_file(fs, target, basename, &ino_file);
paul@226 303
        }
paul@226 304
paul@226 305
        /* Directories are created with the same metadata. */
paul@226 306
paul@226 307
        if (isdir(argv[i]))
paul@226 308
        {
paul@226 309
            if (!target_is_new)
paul@226 310
            {
paul@226 311
                printf("Target %s cannot be created since it already exists.\n", target);
paul@226 312
                return 1;
paul@226 313
            }
paul@226 314
paul@226 315
            /* Obtain the metadata. */
paul@226 316
paul@226 317
            if (lstat(argv[i], &st))
paul@226 318
                return 1;
paul@226 319
paul@241 320
            retval = image_make_dir(fs, ino_target, basename,
paul@241 321
                                    st.st_mode & ~md.mask,
paul@241 322
                                    md.have_uid ? md.uid : st.st_uid,
paul@241 323
                                    md.have_gid ? md.gid : st.st_gid,
paul@241 324
                                    &ino_file);
paul@226 325
paul@226 326
            if (retval)
paul@226 327
            {
paul@226 328
                printf("Failed to create directory %s (%d).\n", argv[i], retval);
paul@226 329
                return 1;
paul@226 330
            }
paul@226 331
        }
paul@226 332
paul@226 333
        /* Files are copied. */
paul@226 334
paul@226 335
        else if (isfile(argv[i]))
paul@226 336
        {
paul@226 337
            flags = EXT2_FILE_WRITE;
paul@226 338
paul@226 339
            /* Obtain the inode for the target file. */
paul@226 340
paul@226 341
            if (target_is_new)
paul@226 342
            {
paul@226 343
                /* Populate the inode details. */
paul@226 344
paul@226 345
                if (lstat(argv[i], &st))
paul@226 346
                    return 1;
paul@226 347
paul@241 348
                retval = image_create_file(fs, ino_target, basename,
paul@241 349
                                           st.st_mode & ~md.mask,
paul@241 350
                                           md.have_uid ? md.uid : st.st_uid,
paul@241 351
                                           md.have_gid ? md.gid : st.st_gid,
paul@241 352
                                           &ino_file);
paul@226 353
                if (retval)
paul@226 354
                {
paul@226 355
                    printf("Failed to create file %s (%d).\n", argv[i], retval);
paul@226 356
                    return 1;
paul@226 357
                }
paul@226 358
paul@226 359
                flags |= EXT2_FILE_CREATE;
paul@226 360
            }
paul@226 361
paul@226 362
            /* NOTE: Overwrite/update metadata where appropriate. */
paul@226 363
paul@226 364
            if (copy_file_in(argv[i], fs, ino_file, flags))
paul@226 365
            {
paul@226 366
                printf("Failed to write to %s.\n", argv[i]);
paul@226 367
                return 1;
paul@226 368
            }
paul@226 369
        }
paul@226 370
    }
paul@226 371
paul@226 372
    return 0;
paul@226 373
}
paul@226 374
paul@226 375
/* Copy source files out of the filesystem image into the external environment. */
paul@226 376
paul@226 377
int copy_out(ext2_filsys fs, int argc, char *argv[])
paul@226 378
{
paul@226 379
    /* Target filename details. */
paul@226 380
paul@226 381
    char *target = argv[argc - 1];
paul@226 382
    int target_is_file;
paul@226 383
paul@226 384
    /* Target file and directory details. */
paul@226 385
paul@226 386
    ext2_file_t file;
paul@226 387
    ext2_ino_t ino_file;
paul@226 388
paul@226 389
    /* Source file details. */
paul@226 390
paul@226 391
    const char *path;
paul@226 392
    int i;
paul@226 393
paul@226 394
    /* Locate the target and test whether it is a directory. */
paul@226 395
paul@226 396
    if (!isdir(target))
paul@226 397
    {
paul@226 398
        /* Only a new or existing file in an existing directory is permitted. */
paul@226 399
paul@226 400
        if (isfile(target) || isdir_dirname(target))
paul@226 401
            target_is_file = 1;
paul@226 402
        else
paul@226 403
        {
paul@226 404
            printf("Target %s is not a directory.\n", target);
paul@226 405
            return 1;
paul@226 406
        }
paul@226 407
paul@226 408
        /* Only permit a target file when one source file is given. */
paul@226 409
paul@226 410
        if (argc > 2)
paul@226 411
        {
paul@226 412
            printf("Target %s can only be a file when copying a single file.\n", target);
paul@226 413
            return 1;
paul@226 414
        }
paul@226 415
    }
paul@226 416
    else
paul@226 417
        target_is_file = 0;
paul@226 418
paul@226 419
    /* For each source filename, test whether it references a file. */
paul@226 420
paul@226 421
    for (i = 0; i < argc - 1; i++)
paul@226 422
    {
paul@226 423
        if (!image_isfile(fs, argv[i]))
paul@226 424
        {
paul@226 425
            printf("Source %s is not a file.\n", argv[i]);
paul@226 426
            return 1;
paul@226 427
        }
paul@226 428
    }
paul@226 429
paul@226 430
    /* Copy each source file to the target directory. */
paul@226 431
paul@226 432
    for (i = 0; i < argc - 1; i++)
paul@226 433
    {
paul@226 434
        path = argv[i];
paul@226 435
paul@226 436
        if (image_find_path(fs, &path, &ino_file))
paul@226 437
            return 1;
paul@226 438
paul@226 439
        if (copy_file_out(target, argv[i], target_is_file, fs, ino_file))
paul@226 440
        {
paul@226 441
            printf("Failed to read from %s.\n", argv[i]);
paul@226 442
            return 1;
paul@226 443
        }
paul@226 444
paul@226 445
        /* NOTE: Overwrite/update metadata where appropriate. */
paul@226 446
    }
paul@226 447
paul@226 448
    return 0;
paul@226 449
}
paul@226 450
paul@226 451
/* List directories in the filesystem image. */
paul@226 452
paul@226 453
int list_dirs(ext2_filsys fs, int argc, char *argv[])
paul@226 454
{
paul@226 455
    int i;
paul@226 456
    const char *path;
paul@226 457
paul@226 458
    for (i = 0; i < argc; i++)
paul@226 459
    {
paul@226 460
        path = argv[i];
paul@226 461
paul@226 462
        /* List the directory contents. */
paul@226 463
paul@226 464
        puts(path);
paul@226 465
paul@226 466
        if (image_list_dir(fs, path, image_list_dir_proc, fs))
paul@226 467
            return 1;
paul@226 468
    }
paul@226 469
paul@226 470
    return 0;
paul@226 471
}
paul@226 472
paul@226 473
/* Make directories in the filesystem image. */
paul@226 474
paul@226 475
int make_dirs(ext2_filsys fs, int argc, char *argv[])
paul@226 476
{
paul@226 477
    int i;
paul@226 478
    char *path_end;
paul@226 479
    const char *path;
paul@226 480
    ext2_ino_t ino;
paul@226 481
paul@226 482
    /* Make each directory component in the given pathname. */
paul@226 483
paul@226 484
    for (i = 0; i < argc; i++)
paul@226 485
    {
paul@226 486
        path = argv[i];
paul@226 487
        if (!*path)
paul@226 488
            continue;
paul@226 489
paul@226 490
        /* Search for the remaining components. */
paul@226 491
paul@226 492
        if (!image_find_path(fs, &path, &ino))
paul@226 493
            continue;
paul@226 494
paul@226 495
        /* From the first unrecognised component, make the remaining
paul@226 496
           directories. */
paul@226 497
paul@241 498
        if (image_make_dirs(fs, &path, ino, 0777 & ~md.mask, 0, 0))
paul@226 499
            return 1;
paul@226 500
    }
paul@226 501
paul@226 502
    return 0;
paul@226 503
}
paul@226 504
paul@241 505
/* Help message and main program. */
paul@241 506
paul@241 507
char help_text[] = "\
paul@241 508
Usage: %s [ <options> ] <image file> <operation> <filename>...\n\
paul@241 509
\n\
paul@241 510
File ownership options:\n\
paul@241 511
\n\
paul@241 512
  -g GID        Set group identifier for new files\n\
paul@241 513
  -u UID        Set user identifier for new files\n\
paul@241 514
\n\
paul@241 515
File permission options:\n\
paul@241 516
\n\
paul@241 517
  -m MASK       Set mode/permissions mask for new directories\n\
paul@241 518
";
paul@241 519
paul@226 520
int main(int argc, char *argv[])
paul@226 521
{
paul@226 522
    int flags = EXT2_FLAG_RW; // | EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS;
paul@226 523
    ext2_filsys fs = NULL;
paul@226 524
    errcode_t retval;
paul@226 525
    int exitcode = 0;
paul@226 526
paul@241 527
    /* Program argument details. */
paul@241 528
paul@241 529
    char **args;
paul@241 530
    char *fsname, *operation, *filename;
paul@241 531
    int num_args;
paul@241 532
paul@241 533
    /* Parse program options and initialise the argument details. */
paul@241 534
paul@241 535
    if (parse_options(argc, argv))
paul@241 536
        return 1;
paul@241 537
paul@241 538
    args = &argv[optind];
paul@241 539
    num_args = argc - optind;
paul@241 540
paul@241 541
    if (num_args < 3)
paul@226 542
    {
paul@241 543
        printf(help_text, argv[0]);
paul@226 544
        return 1;
paul@226 545
    }
paul@226 546
paul@241 547
    /* Open the filesystem image using the POSIX file access mechanism. */
paul@226 548
paul@241 549
    fsname = args[0];
paul@226 550
paul@226 551
    retval = ext2fs_open(fsname, flags, 0, 0, unix_io_manager, &fs);
paul@226 552
    if (retval)
paul@226 553
    {
paul@226 554
        printf("Could not open filesystem using %s\n", fsname);
paul@226 555
        return 1;
paul@226 556
    }
paul@226 557
paul@226 558
    //fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
paul@226 559
paul@226 560
    retval = ext2fs_read_bitmaps(fs);
paul@226 561
    if (retval)
paul@226 562
    {
paul@226 563
        printf("Could not read bitmaps from %s\n", fsname);
paul@226 564
        return 1;
paul@226 565
    }
paul@226 566
paul@226 567
    /* Perform the requested operation. */
paul@226 568
paul@241 569
    operation = args[1];
paul@241 570
    args = &args[2];
paul@241 571
    num_args -= 2;
paul@226 572
paul@241 573
    if (!strcmp(operation, "copy-out"))
paul@226 574
    {
paul@241 575
        exitcode = copy_out(fs, num_args, args);
paul@226 576
    }
paul@241 577
    else if (!strcmp(operation, "copy-in"))
paul@226 578
    {
paul@241 579
        exitcode = copy_in(fs, num_args, args);
paul@226 580
    }
paul@241 581
    else if (!strcmp(operation, "list-dirs"))
paul@226 582
    {
paul@241 583
        exitcode = list_dirs(fs, num_args, args);
paul@226 584
    }
paul@241 585
    else if (!strcmp(operation, "make-dirs"))
paul@226 586
    {
paul@241 587
        exitcode = make_dirs(fs, num_args, args);
paul@226 588
    }
paul@226 589
    else
paul@226 590
    {
paul@226 591
        printf("Operation %s is not recognised.\n", operation);
paul@226 592
        exitcode = 1;
paul@226 593
    }
paul@226 594
paul@226 595
    /* Close the filesystem image. */
paul@226 596
paul@226 597
    retval = ext2fs_flush(fs);
paul@226 598
    if (retval)
paul@226 599
    {
paul@226 600
        printf("Error flushing filesystem in %s\n", fsname);
paul@226 601
        exitcode = 1;
paul@226 602
    }
paul@226 603
paul@226 604
    retval = ext2fs_close(fs);
paul@226 605
    if (retval)
paul@226 606
    {
paul@226 607
        printf("Error closing filesystem in %s\n", fsname);
paul@226 608
        exitcode = 1;
paul@226 609
    }
paul@226 610
paul@226 611
    ext2fs_free(fs);
paul@226 612
    return exitcode;
paul@226 613
}
paul@241 614
paul@241 615
/* vim: tabstop=4 expandtab shiftwidth=4
paul@241 616
*/