Home | History | Annotate | Download | only in ext2fs
      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 Public
      8  * License.
      9  * %End-Header%
     10  */
     11 
     12 #include <stdio.h>
     13 #include <string.h>
     14 #if HAVE_UNISTD_H
     15 #include <unistd.h>
     16 #endif
     17 
     18 #include "ext2_fs.h"
     19 #include "ext2fs.h"
     20 
     21 struct ext2_file {
     22 	errcode_t		magic;
     23 	ext2_filsys 		fs;
     24 	ext2_ino_t		ino;
     25 	struct ext2_inode	inode;
     26 	int 			flags;
     27 	__u64			pos;
     28 	blk_t			blockno;
     29 	blk_t			physblock;
     30 	char 			*buf;
     31 };
     32 
     33 #define BMAP_BUFFER (file->buf + fs->blocksize)
     34 
     35 errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
     36 			    struct ext2_inode *inode,
     37 			    int flags, ext2_file_t *ret)
     38 {
     39 	ext2_file_t 	file;
     40 	errcode_t	retval;
     41 
     42 	/*
     43 	 * Don't let caller create or open a file for writing if the
     44 	 * filesystem is read-only.
     45 	 */
     46 	if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
     47 	    !(fs->flags & EXT2_FLAG_RW))
     48 		return EXT2_ET_RO_FILSYS;
     49 
     50 	retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
     51 	if (retval)
     52 		return retval;
     53 
     54 	memset(file, 0, sizeof(struct ext2_file));
     55 	file->magic = EXT2_ET_MAGIC_EXT2_FILE;
     56 	file->fs = fs;
     57 	file->ino = ino;
     58 	file->flags = flags & EXT2_FILE_MASK;
     59 
     60 	if (inode) {
     61 		memcpy(&file->inode, inode, sizeof(struct ext2_inode));
     62 	} else {
     63 		retval = ext2fs_read_inode(fs, ino, &file->inode);
     64 		if (retval)
     65 			goto fail;
     66 	}
     67 
     68 	retval = ext2fs_get_array(3, fs->blocksize, &file->buf);
     69 	if (retval)
     70 		goto fail;
     71 
     72 	*ret = file;
     73 	return 0;
     74 
     75 fail:
     76 	if (file->buf)
     77 		ext2fs_free_mem(&file->buf);
     78 	ext2fs_free_mem(&file);
     79 	return retval;
     80 }
     81 
     82 errcode_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  */
     91 ext2_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  */
    102 errcode_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  */
    140 static 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
    165 static 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 
    192 errcode_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 	if (file->buf)
    201 		ext2fs_free_mem(&file->buf);
    202 	ext2fs_free_mem(&file);
    203 
    204 	return retval;
    205 }
    206 
    207 
    208 errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
    209 			   unsigned int wanted, unsigned int *got)
    210 {
    211 	ext2_filsys	fs;
    212 	errcode_t	retval = 0;
    213 	unsigned int	start, c, count = 0;
    214 	__u64		left;
    215 	char		*ptr = (char *) buf;
    216 
    217 	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
    218 	fs = file->fs;
    219 
    220 	while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
    221 		retval = sync_buffer_position(file);
    222 		if (retval)
    223 			goto fail;
    224 		retval = load_buffer(file, 0);
    225 		if (retval)
    226 			goto fail;
    227 
    228 		start = file->pos % fs->blocksize;
    229 		c = fs->blocksize - start;
    230 		if (c > wanted)
    231 			c = wanted;
    232 		left = EXT2_I_SIZE(&file->inode) - file->pos ;
    233 		if (c > left)
    234 			c = left;
    235 
    236 		memcpy(ptr, file->buf+start, c);
    237 		file->pos += c;
    238 		ptr += c;
    239 		count += c;
    240 		wanted -= c;
    241 	}
    242 
    243 fail:
    244 	if (got)
    245 		*got = count;
    246 	return retval;
    247 }
    248 
    249 
    250 errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
    251 			    unsigned int nbytes, unsigned int *written)
    252 {
    253 	ext2_filsys	fs;
    254 	errcode_t	retval = 0;
    255 	unsigned int	start, c, count = 0;
    256 	const char	*ptr = (const char *) buf;
    257 
    258 	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
    259 	fs = file->fs;
    260 
    261 	if (!(file->flags & EXT2_FILE_WRITE))
    262 		return EXT2_ET_FILE_RO;
    263 
    264 	while (nbytes > 0) {
    265 		retval = sync_buffer_position(file);
    266 		if (retval)
    267 			goto fail;
    268 
    269 		start = file->pos % fs->blocksize;
    270 		c = fs->blocksize - start;
    271 		if (c > nbytes)
    272 			c = nbytes;
    273 
    274 		/*
    275 		 * We only need to do a read-modify-update cycle if
    276 		 * we're doing a partial write.
    277 		 */
    278 		retval = load_buffer(file, (c == fs->blocksize));
    279 		if (retval)
    280 			goto fail;
    281 
    282 		file->flags |= EXT2_FILE_BUF_DIRTY;
    283 		memcpy(file->buf+start, ptr, c);
    284 		file->pos += c;
    285 		ptr += c;
    286 		count += c;
    287 		nbytes -= c;
    288 	}
    289 
    290 fail:
    291 	if (written)
    292 		*written = count;
    293 	return retval;
    294 }
    295 
    296 errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
    297 			    int whence, __u64 *ret_pos)
    298 {
    299 	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
    300 
    301 	if (whence == EXT2_SEEK_SET)
    302 		file->pos = offset;
    303 	else if (whence == EXT2_SEEK_CUR)
    304 		file->pos += offset;
    305 	else if (whence == EXT2_SEEK_END)
    306 		file->pos = EXT2_I_SIZE(&file->inode) + offset;
    307 	else
    308 		return EXT2_ET_INVALID_ARGUMENT;
    309 
    310 	if (ret_pos)
    311 		*ret_pos = file->pos;
    312 
    313 	return 0;
    314 }
    315 
    316 errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
    317 			    int whence, ext2_off_t *ret_pos)
    318 {
    319 	__u64		loffset, ret_loffset;
    320 	errcode_t	retval;
    321 
    322 	loffset = offset;
    323 	retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
    324 	if (ret_pos)
    325 		*ret_pos = (ext2_off_t) ret_loffset;
    326 	return retval;
    327 }
    328 
    329 
    330 /*
    331  * This function returns the size of the file, according to the inode
    332  */
    333 errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
    334 {
    335 	if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
    336 		return EXT2_ET_MAGIC_EXT2_FILE;
    337 	*ret_size = EXT2_I_SIZE(&file->inode);
    338 	return 0;
    339 }
    340 
    341 /*
    342  * This function returns the size of the file, according to the inode
    343  */
    344 ext2_off_t ext2fs_file_get_size(ext2_file_t file)
    345 {
    346 	__u64	size;
    347 
    348 	if (ext2fs_file_get_lsize(file, &size))
    349 		return 0;
    350 	if ((size >> 32) != 0)
    351 		return 0;
    352 	return size;
    353 }
    354 
    355 /*
    356  * This function sets the size of the file, truncating it if necessary
    357  *
    358  * XXX still need to call truncate
    359  */
    360 errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
    361 {
    362 	errcode_t	retval;
    363 	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
    364 
    365 	file->inode.i_size = size;
    366 	file->inode.i_size_high = 0;
    367 	if (file->ino) {
    368 		retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
    369 		if (retval)
    370 			return retval;
    371 	}
    372 
    373 	/*
    374 	 * XXX truncate inode if necessary
    375 	 */
    376 
    377 	return 0;
    378 }
    379