fileio.c revision d1154eb460efe588eaed3d439c1caaca149fa362
1/* 2 * fileio.c --- Simple file I/O routines 3 * 4 * Copyright (C) 1997 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Library 8 * General Public License, version 2. 9 * %End-Header% 10 */ 11 12#include "config.h" 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 blk64_t blockno; 30 blk64_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_array(3, fs->blocksize, &file->buf); 70 if (retval) 71 goto fail; 72 73 *ret = file; 74 return 0; 75 76fail: 77 if (file->buf) 78 ext2fs_free_mem(&file->buf); 79 ext2fs_free_mem(&file); 80 return retval; 81} 82 83errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, 84 int flags, ext2_file_t *ret) 85{ 86 return ext2fs_file_open2(fs, ino, NULL, flags, ret); 87} 88 89/* 90 * This function returns the filesystem handle of a file from the structure 91 */ 92ext2_filsys ext2fs_file_get_fs(ext2_file_t file) 93{ 94 if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) 95 return 0; 96 return file->fs; 97} 98 99/* 100 * This function returns the pointer to the inode of a file from the structure 101 */ 102struct ext2_inode *ext2fs_file_get_inode(ext2_file_t file) 103{ 104 if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) 105 return NULL; 106 return &file->inode; 107} 108 109/* 110 * This function flushes the dirty block buffer out to disk if 111 * necessary. 112 */ 113errcode_t ext2fs_file_flush(ext2_file_t file) 114{ 115 errcode_t retval; 116 ext2_filsys fs; 117 118 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); 119 fs = file->fs; 120 121 if (!(file->flags & EXT2_FILE_BUF_VALID) || 122 !(file->flags & EXT2_FILE_BUF_DIRTY)) 123 return 0; 124 125 /* 126 * OK, the physical block hasn't been allocated yet. 127 * Allocate it. 128 */ 129 if (!file->physblock) { 130 retval = ext2fs_bmap2(fs, file->ino, &file->inode, 131 BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0, 132 file->blockno, 0, &file->physblock); 133 if (retval) 134 return retval; 135 } 136 137 retval = io_channel_write_blk(fs->io, file->physblock, 138 1, file->buf); 139 if (retval) 140 return retval; 141 142 file->flags &= ~EXT2_FILE_BUF_DIRTY; 143 144 return retval; 145} 146 147/* 148 * This function synchronizes the file's block buffer and the current 149 * file position, possibly invalidating block buffer if necessary 150 */ 151static errcode_t sync_buffer_position(ext2_file_t file) 152{ 153 blk_t b; 154 errcode_t retval; 155 156 b = file->pos / file->fs->blocksize; 157 if (b != file->blockno) { 158 retval = ext2fs_file_flush(file); 159 if (retval) 160 return retval; 161 file->flags &= ~EXT2_FILE_BUF_VALID; 162 } 163 file->blockno = b; 164 return 0; 165} 166 167/* 168 * This function loads the file's block buffer with valid data from 169 * the disk as necessary. 170 * 171 * If dontfill is true, then skip initializing the buffer since we're 172 * going to be replacing its entire contents anyway. If set, then the 173 * function basically only sets file->physblock and EXT2_FILE_BUF_VALID 174 */ 175#define DONTFILL 1 176static errcode_t load_buffer(ext2_file_t file, int dontfill) 177{ 178 ext2_filsys fs = file->fs; 179 errcode_t retval; 180 181 if (!(file->flags & EXT2_FILE_BUF_VALID)) { 182 retval = ext2fs_bmap2(fs, file->ino, &file->inode, 183 BMAP_BUFFER, 0, file->blockno, 0, 184 &file->physblock); 185 if (retval) 186 return retval; 187 if (!dontfill) { 188 if (file->physblock) { 189 retval = io_channel_read_blk(fs->io, 190 file->physblock, 191 1, file->buf); 192 if (retval) 193 return retval; 194 } else 195 memset(file->buf, 0, fs->blocksize); 196 } 197 file->flags |= EXT2_FILE_BUF_VALID; 198 } 199 return 0; 200} 201 202 203errcode_t ext2fs_file_close(ext2_file_t file) 204{ 205 errcode_t retval; 206 207 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); 208 209 retval = ext2fs_file_flush(file); 210 211 if (file->buf) 212 ext2fs_free_mem(&file->buf); 213 ext2fs_free_mem(&file); 214 215 return retval; 216} 217 218 219errcode_t ext2fs_file_read(ext2_file_t file, void *buf, 220 unsigned int wanted, unsigned int *got) 221{ 222 ext2_filsys fs; 223 errcode_t retval = 0; 224 unsigned int start, c, count = 0; 225 __u64 left; 226 char *ptr = (char *) buf; 227 228 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); 229 fs = file->fs; 230 231 while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) { 232 retval = sync_buffer_position(file); 233 if (retval) 234 goto fail; 235 retval = load_buffer(file, 0); 236 if (retval) 237 goto fail; 238 239 start = file->pos % fs->blocksize; 240 c = fs->blocksize - start; 241 if (c > wanted) 242 c = wanted; 243 left = EXT2_I_SIZE(&file->inode) - file->pos ; 244 if (c > left) 245 c = left; 246 247 memcpy(ptr, file->buf+start, c); 248 file->pos += c; 249 ptr += c; 250 count += c; 251 wanted -= c; 252 } 253 254fail: 255 if (got) 256 *got = count; 257 return retval; 258} 259 260 261errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, 262 unsigned int nbytes, unsigned int *written) 263{ 264 ext2_filsys fs; 265 errcode_t retval = 0; 266 unsigned int start, c, count = 0; 267 const char *ptr = (const char *) buf; 268 269 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); 270 fs = file->fs; 271 272 if (!(file->flags & EXT2_FILE_WRITE)) 273 return EXT2_ET_FILE_RO; 274 275 while (nbytes > 0) { 276 retval = sync_buffer_position(file); 277 if (retval) 278 goto fail; 279 280 start = file->pos % fs->blocksize; 281 c = fs->blocksize - start; 282 if (c > nbytes) 283 c = nbytes; 284 285 /* 286 * We only need to do a read-modify-update cycle if 287 * we're doing a partial write. 288 */ 289 retval = load_buffer(file, (c == fs->blocksize)); 290 if (retval) 291 goto fail; 292 293 file->flags |= EXT2_FILE_BUF_DIRTY; 294 memcpy(file->buf+start, ptr, c); 295 file->pos += c; 296 ptr += c; 297 count += c; 298 nbytes -= c; 299 } 300 301fail: 302 if (written) 303 *written = count; 304 return retval; 305} 306 307errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, 308 int whence, __u64 *ret_pos) 309{ 310 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); 311 312 if (whence == EXT2_SEEK_SET) 313 file->pos = offset; 314 else if (whence == EXT2_SEEK_CUR) 315 file->pos += offset; 316 else if (whence == EXT2_SEEK_END) 317 file->pos = EXT2_I_SIZE(&file->inode) + offset; 318 else 319 return EXT2_ET_INVALID_ARGUMENT; 320 321 if (ret_pos) 322 *ret_pos = file->pos; 323 324 return 0; 325} 326 327errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, 328 int whence, ext2_off_t *ret_pos) 329{ 330 __u64 loffset, ret_loffset; 331 errcode_t retval; 332 333 loffset = offset; 334 retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset); 335 if (ret_pos) 336 *ret_pos = (ext2_off_t) ret_loffset; 337 return retval; 338} 339 340 341/* 342 * This function returns the size of the file, according to the inode 343 */ 344errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size) 345{ 346 if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) 347 return EXT2_ET_MAGIC_EXT2_FILE; 348 *ret_size = EXT2_I_SIZE(&file->inode); 349 return 0; 350} 351 352/* 353 * This function returns the size of the file, according to the inode 354 */ 355ext2_off_t ext2fs_file_get_size(ext2_file_t file) 356{ 357 __u64 size; 358 359 if (ext2fs_file_get_lsize(file, &size)) 360 return 0; 361 if ((size >> 32) != 0) 362 return 0; 363 return size; 364} 365 366/* 367 * This function sets the size of the file, truncating it if necessary 368 * 369 */ 370errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size) 371{ 372 ext2_off64_t old_size; 373 errcode_t retval; 374 blk64_t old_truncate, truncate_block; 375 376 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); 377 378 truncate_block = ((size + file->fs->blocksize - 1) >> 379 EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; 380 old_size = EXT2_I_SIZE(&file->inode); 381 old_truncate = ((old_size + file->fs->blocksize - 1) >> 382 EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; 383 384 file->inode.i_size = size & 0xffffffff; 385 file->inode.i_size_high = (size >> 32); 386 if (file->ino) { 387 retval = ext2fs_write_inode(file->fs, file->ino, &file->inode); 388 if (retval) 389 return retval; 390 } 391 392 if (truncate_block <= old_truncate) 393 return 0; 394 395 return ext2fs_punch(file->fs, file->ino, &file->inode, 0, 396 truncate_block, ~0ULL); 397} 398 399errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size) 400{ 401 return ext2fs_file_set_size2(file, size); 402} 403