busybox/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * fileio.c --- Simple file I/O routines
   4 *
   5 * Copyright (C) 1997 Theodore Ts'o.
   6 *
   7 * %Begin-Header%
   8 * This file may be redistributed under the terms of the GNU Public
   9 * License.
  10 * %End-Header%
  11 */
  12
  13#include <stdio.h>
  14#include <string.h>
  15#if HAVE_UNISTD_H
  16#include <unistd.h>
  17#endif
  18
  19#include "ext2_fs.h"
  20#include "ext2fs.h"
  21
  22struct ext2_file {
  23        errcode_t               magic;
  24        ext2_filsys             fs;
  25        ext2_ino_t              ino;
  26        struct ext2_inode       inode;
  27        int                     flags;
  28        __u64                   pos;
  29        blk_t                   blockno;
  30        blk_t                   physblock;
  31        char                    *buf;
  32};
  33
  34#define BMAP_BUFFER (file->buf + fs->blocksize)
  35
  36errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
  37                            struct ext2_inode *inode,
  38                            int flags, ext2_file_t *ret)
  39{
  40        ext2_file_t     file;
  41        errcode_t       retval;
  42
  43        /*
  44         * Don't let caller create or open a file for writing if the
  45         * filesystem is read-only.
  46         */
  47        if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
  48            !(fs->flags & EXT2_FLAG_RW))
  49                return EXT2_ET_RO_FILSYS;
  50
  51        retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
  52        if (retval)
  53                return retval;
  54
  55        memset(file, 0, sizeof(struct ext2_file));
  56        file->magic = EXT2_ET_MAGIC_EXT2_FILE;
  57        file->fs = fs;
  58        file->ino = ino;
  59        file->flags = flags & EXT2_FILE_MASK;
  60
  61        if (inode) {
  62                memcpy(&file->inode, inode, sizeof(struct ext2_inode));
  63        } else {
  64                retval = ext2fs_read_inode(fs, ino, &file->inode);
  65                if (retval)
  66                        goto fail;
  67        }
  68
  69        retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf);
  70        if (retval)
  71                goto fail;
  72
  73        *ret = file;
  74        return 0;
  75
  76fail:
  77        ext2fs_free_mem(&file->buf);
  78        ext2fs_free_mem(&file);
  79        return retval;
  80}
  81
  82errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
  83                           int flags, ext2_file_t *ret)
  84{
  85        return ext2fs_file_open2(fs, ino, NULL, flags, ret);
  86}
  87
  88/*
  89 * This function returns the filesystem handle of a file from the structure
  90 */
  91ext2_filsys ext2fs_file_get_fs(ext2_file_t file)
  92{
  93        if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
  94                return 0;
  95        return file->fs;
  96}
  97
  98/*
  99 * This function flushes the dirty block buffer out to disk if
 100 * necessary.
 101 */
 102errcode_t ext2fs_file_flush(ext2_file_t file)
 103{
 104        errcode_t       retval;
 105        ext2_filsys fs;
 106
 107        EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
 108        fs = file->fs;
 109
 110        if (!(file->flags & EXT2_FILE_BUF_VALID) ||
 111            !(file->flags & EXT2_FILE_BUF_DIRTY))
 112                return 0;
 113
 114        /*
 115         * OK, the physical block hasn't been allocated yet.
 116         * Allocate it.
 117         */
 118        if (!file->physblock) {
 119                retval = ext2fs_bmap(fs, file->ino, &file->inode,
 120                                     BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0,
 121                                     file->blockno, &file->physblock);
 122                if (retval)
 123                        return retval;
 124        }
 125
 126        retval = io_channel_write_blk(fs->io, file->physblock,
 127                                      1, file->buf);
 128        if (retval)
 129                return retval;
 130
 131        file->flags &= ~EXT2_FILE_BUF_DIRTY;
 132
 133        return retval;
 134}
 135
 136/*
 137 * This function synchronizes the file's block buffer and the current
 138 * file position, possibly invalidating block buffer if necessary
 139 */
 140static errcode_t sync_buffer_position(ext2_file_t file)
 141{
 142        blk_t   b;
 143        errcode_t       retval;
 144
 145        b = file->pos / file->fs->blocksize;
 146        if (b != file->blockno) {
 147                retval = ext2fs_file_flush(file);
 148                if (retval)
 149                        return retval;
 150                file->flags &= ~EXT2_FILE_BUF_VALID;
 151        }
 152        file->blockno = b;
 153        return 0;
 154}
 155
 156/*
 157 * This function loads the file's block buffer with valid data from
 158 * the disk as necessary.
 159 *
 160 * If dontfill is true, then skip initializing the buffer since we're
 161 * going to be replacing its entire contents anyway.  If set, then the
 162 * function basically only sets file->physblock and EXT2_FILE_BUF_VALID
 163 */
 164#define DONTFILL 1
 165static errcode_t load_buffer(ext2_file_t file, int dontfill)
 166{
 167        ext2_filsys     fs = file->fs;
 168        errcode_t       retval;
 169
 170        if (!(file->flags & EXT2_FILE_BUF_VALID)) {
 171                retval = ext2fs_bmap(fs, file->ino, &file->inode,
 172                                     BMAP_BUFFER, 0, file->blockno,
 173                                     &file->physblock);
 174                if (retval)
 175                        return retval;
 176                if (!dontfill) {
 177                        if (file->physblock) {
 178                                retval = io_channel_read_blk(fs->io,
 179                                                             file->physblock,
 180                                                             1, file->buf);
 181                                if (retval)
 182                                        return retval;
 183                        } else
 184                                memset(file->buf, 0, fs->blocksize);
 185                }
 186                file->flags |= EXT2_FILE_BUF_VALID;
 187        }
 188        return 0;
 189}
 190
 191
 192errcode_t ext2fs_file_close(ext2_file_t file)
 193{
 194        errcode_t       retval;
 195
 196        EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
 197
 198        retval = ext2fs_file_flush(file);
 199
 200        ext2fs_free_mem(&file->buf);
 201        ext2fs_free_mem(&file);
 202
 203        return retval;
 204}
 205
 206
 207errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
 208                           unsigned int wanted, unsigned int *got)
 209{
 210        ext2_filsys     fs;
 211        errcode_t       retval = 0;
 212        unsigned int    start, c, count = 0;
 213        __u64           left;
 214        char            *ptr = (char *) buf;
 215
 216        EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
 217        fs = file->fs;
 218
 219        while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
 220                retval = sync_buffer_position(file);
 221                if (retval)
 222                        goto fail;
 223                retval = load_buffer(file, 0);
 224                if (retval)
 225                        goto fail;
 226
 227                start = file->pos % fs->blocksize;
 228                c = fs->blocksize - start;
 229                if (c > wanted)
 230                        c = wanted;
 231                left = EXT2_I_SIZE(&file->inode) - file->pos;
 232                if (c > left)
 233                        c = left;
 234
 235                memcpy(ptr, file->buf+start, c);
 236                file->pos += c;
 237                ptr += c;
 238                count += c;
 239                wanted -= c;
 240        }
 241
 242fail:
 243        if (got)
 244                *got = count;
 245        return retval;
 246}
 247
 248
 249errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
 250                            unsigned int nbytes, unsigned int *written)
 251{
 252        ext2_filsys     fs;
 253        errcode_t       retval = 0;
 254        unsigned int    start, c, count = 0;
 255        const char      *ptr = (const char *) buf;
 256
 257        EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
 258        fs = file->fs;
 259
 260        if (!(file->flags & EXT2_FILE_WRITE))
 261                return EXT2_ET_FILE_RO;
 262
 263        while (nbytes > 0) {
 264                retval = sync_buffer_position(file);
 265                if (retval)
 266                        goto fail;
 267
 268                start = file->pos % fs->blocksize;
 269                c = fs->blocksize - start;
 270                if (c > nbytes)
 271                        c = nbytes;
 272
 273                /*
 274                 * We only need to do a read-modify-update cycle if
 275                 * we're doing a partial write.
 276                 */
 277                retval = load_buffer(file, (c == fs->blocksize));
 278                if (retval)
 279                        goto fail;
 280
 281                file->flags |= EXT2_FILE_BUF_DIRTY;
 282                memcpy(file->buf+start, ptr, c);
 283                file->pos += c;
 284                ptr += c;
 285                count += c;
 286                nbytes -= c;
 287        }
 288
 289fail:
 290        if (written)
 291                *written = count;
 292        return retval;
 293}
 294
 295errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
 296                            int whence, __u64 *ret_pos)
 297{
 298        EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
 299
 300        if (whence == EXT2_SEEK_SET)
 301                file->pos = offset;
 302        else if (whence == EXT2_SEEK_CUR)
 303                file->pos += offset;
 304        else if (whence == EXT2_SEEK_END)
 305                file->pos = EXT2_I_SIZE(&file->inode) + offset;
 306        else
 307                return EXT2_ET_INVALID_ARGUMENT;
 308
 309        if (ret_pos)
 310                *ret_pos = file->pos;
 311
 312        return 0;
 313}
 314
 315errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
 316                            int whence, ext2_off_t *ret_pos)
 317{
 318        __u64           loffset, ret_loffset;
 319        errcode_t       retval;
 320
 321        loffset = offset;
 322        retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
 323        if (ret_pos)
 324                *ret_pos = (ext2_off_t) ret_loffset;
 325        return retval;
 326}
 327
 328
 329/*
 330 * This function returns the size of the file, according to the inode
 331 */
 332errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
 333{
 334        if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
 335                return EXT2_ET_MAGIC_EXT2_FILE;
 336        *ret_size = EXT2_I_SIZE(&file->inode);
 337        return 0;
 338}
 339
 340/*
 341 * This function returns the size of the file, according to the inode
 342 */
 343ext2_off_t ext2fs_file_get_size(ext2_file_t file)
 344{
 345        __u64   size;
 346
 347        if (ext2fs_file_get_lsize(file, &size))
 348                return 0;
 349        if ((size >> 32) != 0)
 350                return 0;
 351        return size;
 352}
 353
 354/*
 355 * This function sets the size of the file, truncating it if necessary
 356 *
 357 * XXX still need to call truncate
 358 */
 359errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
 360{
 361        errcode_t       retval;
 362        EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
 363
 364        file->inode.i_size = size;
 365        file->inode.i_size_high = 0;
 366        if (file->ino) {
 367                retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
 368                if (retval)
 369                        return retval;
 370        }
 371
 372        /*
 373         * XXX truncate inode if necessary
 374         */
 375
 376        return 0;
 377}
 378