f2fs-tools: f2fs_read() and f2fs_filesize_update() are added

This patch adds f2fs_read() and f2fs_filesize_update(). It also refactors
f2fs_write_block() and renamed as f2fs_write().

Signed-off-by: Hyojun Kim <hyojun@google.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@google.com>
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 1e8ed0b..5628906 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -219,6 +219,12 @@
 void set_data_blkaddr(struct dnode_of_data *);
 block_t new_node_block(struct f2fs_sb_info *,
 					struct dnode_of_data *, unsigned int);
+
+/* segment.c */
+u64 f2fs_read(struct f2fs_sb_info *, nid_t, void *, u64, pgoff_t);
+u64 f2fs_write(struct f2fs_sb_info *, nid_t, void *, u64, pgoff_t);
+void f2fs_filesize_update(struct f2fs_sb_info *, nid_t, u64);
+
 void get_dnode_of_data(struct f2fs_sb_info *, struct dnode_of_data *,
 					pgoff_t, int);
 void make_dentry_ptr(struct f2fs_dentry_ptr *, struct f2fs_node *, void *, int);
diff --git a/fsck/segment.c b/fsck/segment.c
index 2ea5bf1..e13f147 100644
--- a/fsck/segment.c
+++ b/fsck/segment.c
@@ -68,111 +68,193 @@
 	set_data_blkaddr(dn);
 }
 
-static void f2fs_write_block(struct f2fs_sb_info *sbi, nid_t ino, void *buffer,
+u64 f2fs_read(struct f2fs_sb_info *sbi, nid_t ino, void *buffer,
 					u64 count, pgoff_t offset)
 {
-	u64 start = F2FS_BYTES_TO_BLK(offset);
-	u64 len = F2FS_BYTES_TO_BLK(count);
-	u64 end_offset;
-	u64 off_in_block, len_in_block, len_already;
-	struct dnode_of_data dn = {0};
-	void *data_blk;
+	struct dnode_of_data dn;
 	struct node_info ni;
 	struct f2fs_node *inode;
-	int idirty = 0;
-	int ret = -1;
+	char *blk_buffer;
+	u64 filesize;
+	u64 off_in_blk;
+	u64 len_in_blk;
+	u64 read_count;
+	u64 remained_blkentries;
+	block_t blkaddr;
+	void *index_node = NULL;
 
+	memset(&dn, 0, sizeof(dn));
+
+	/* Memory allocation for block buffer and inode. */
+	blk_buffer = calloc(BLOCK_SZ, 2);
+	ASSERT(blk_buffer);
+	inode = (struct f2fs_node*)(blk_buffer + BLOCK_SZ);
+
+	/* Read inode */
 	get_node_info(sbi, ino, &ni);
-	inode = calloc(BLOCK_SZ, 1);
-	ASSERT(inode);
+	ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
+	ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode)));
+	ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode)));
 
-	ret = dev_read_block(inode, ni.blk_addr);
-	ASSERT(ret >= 0);
+	/* Adjust count with file length. */
+	filesize = le64_to_cpu(inode->i.i_size);
+	if (offset > filesize)
+		count = 0;
+	else if (count + offset > filesize)
+		count = filesize - offset;
 
-	if (S_ISDIR(le16_to_cpu(inode->i.i_mode)) ||
-			S_ISLNK(le16_to_cpu(inode->i.i_mode)))
-		ASSERT(0);
-
-	off_in_block = offset & ((1 << F2FS_BLKSIZE_BITS) - 1);
-	len_in_block = (1 << F2FS_BLKSIZE_BITS) - off_in_block;
-	if (len_in_block > count)
-		len_in_block = count;
-	len_already = 0;
-
-	/*
-	 * When calculate how many blocks this 'count' stride accross,
-	 * We should take offset in a block in account.
-	 */
-	len = F2FS_BYTES_TO_BLK(count + off_in_block
-			+ ((1 << F2FS_BLKSIZE_BITS) - 1));
-
-	data_blk = calloc(BLOCK_SZ, 1);
-	ASSERT(data_blk);
-
-	set_new_dnode(&dn, inode, NULL, ino);
-
-	while (len) {
-		if (dn.node_blk != dn.inode_blk)
-			free(dn.node_blk);
-
-		set_new_dnode(&dn, inode, NULL, ino);
-		get_dnode_of_data(sbi, &dn, start, ALLOC_NODE);
-
-		end_offset = ADDRS_PER_PAGE(dn.node_blk);
-
-		while (dn.ofs_in_node < end_offset && len) {
-			block_t blkaddr;
-
-			blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
-
-			/* A new page from WARM_DATA */
-			if (blkaddr == NULL_ADDR) {
-				new_data_block(sbi, data_blk, &dn,
-							CURSEG_WARM_DATA);
-				blkaddr = dn.data_blkaddr;
-				idirty |= dn.idirty;
-			}
-
-			/* Copy data from buffer to file */
-			ret = dev_read_block(data_blk, blkaddr);
-			ASSERT(ret >= 0);
-
-			memcpy(data_blk + off_in_block, buffer, len_in_block);
-
-			ret = dev_write_block(data_blk, blkaddr);
-			ASSERT(ret >= 0);
-
-			off_in_block = 0;
-			len_already += len_in_block;
-			if ((count - len_already) > (1 << F2FS_BLKSIZE_BITS))
-				len_in_block = 1 << F2FS_BLKSIZE_BITS;
-			else
-				len_in_block = count - len_already;
-			len--;
-			start++;
-			dn.ofs_in_node++;
+	/* Main loop for file blocks */
+	read_count = remained_blkentries = 0;
+	while (count > 0) {
+		if (remained_blkentries == 0) {
+			set_new_dnode(&dn, inode, NULL, ino);
+			get_dnode_of_data(sbi, &dn, F2FS_BYTES_TO_BLK(offset),
+					LOOKUP_NODE);
+			if (index_node)
+				free(index_node);
+			index_node = (dn.node_blk == dn.inode_blk) ?
+							NULL : dn.node_blk;
+			remained_blkentries = ADDRS_PER_PAGE(dn.node_blk);
 		}
-		/* Update the direct node */
-		if (dn.ndirty) {
-			ret = dev_write_block(dn.node_blk, dn.node_blkaddr);
-			ASSERT(ret >= 0);
+		ASSERT(remained_blkentries > 0);
+
+		blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
+		if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR)
+			break;
+
+		off_in_blk = offset % BLOCK_SZ;
+		len_in_blk = BLOCK_SZ - off_in_blk;
+		if (len_in_blk > count)
+			len_in_blk = count;
+
+		/* Read data from single block. */
+		if (len_in_blk < BLOCK_SZ) {
+			ASSERT(dev_read_block(blk_buffer, blkaddr) >= 0);
+			memcpy(buffer, blk_buffer + off_in_blk, len_in_blk);
+		} else {
+			/* Direct read */
+			ASSERT(dev_read_block(buffer, blkaddr) >= 0);
 		}
+
+		offset += len_in_blk;
+		count -= len_in_blk;
+		buffer += len_in_blk;
+		read_count += len_in_blk;
+
+		dn.ofs_in_node++;
+		remained_blkentries--;
 	}
+	if (index_node)
+		free(index_node);
+	free(blk_buffer);
 
-	/* Update the inode info */
-	if (le64_to_cpu(inode->i.i_size) < offset + count) {
-		inode->i.i_size = cpu_to_le64(offset + count);
+	return read_count;
+}
+
+u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, void *buffer,
+					u64 count, pgoff_t offset)
+{
+	struct dnode_of_data dn;
+	struct node_info ni;
+	struct f2fs_node *inode;
+	char *blk_buffer;
+	u64 off_in_blk;
+	u64 len_in_blk;
+	u64 written_count;
+	u64 remained_blkentries;
+	block_t blkaddr;
+	void* index_node = NULL;
+	int idirty = 0;
+
+	/* Memory allocation for block buffer and inode. */
+	blk_buffer = calloc(BLOCK_SZ, 2);
+	ASSERT(blk_buffer);
+	inode = (struct f2fs_node*)(blk_buffer + BLOCK_SZ);
+
+	/* Read inode */
+	get_node_info(sbi, ino, &ni);
+	ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
+	ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode)));
+	ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode)));
+
+	/* Main loop for file blocks */
+	written_count = remained_blkentries = 0;
+	while (count > 0) {
+		if (remained_blkentries == 0) {
+			set_new_dnode(&dn, inode, NULL, ino);
+			get_dnode_of_data(sbi, &dn, F2FS_BYTES_TO_BLK(offset),
+					ALLOC_NODE);
+			idirty |= dn.idirty;
+			if (index_node)
+				free(index_node);
+			index_node = (dn.node_blk == dn.inode_blk) ?
+							NULL : dn.node_blk;
+			remained_blkentries = ADDRS_PER_PAGE(dn.node_blk);
+		}
+		ASSERT(remained_blkentries > 0);
+
+		blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
+		if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) {
+			new_data_block(sbi, blk_buffer, &dn, CURSEG_WARM_DATA);
+			blkaddr = dn.data_blkaddr;
+		}
+
+		off_in_blk = offset % BLOCK_SZ;
+		len_in_blk = BLOCK_SZ - off_in_blk;
+		if (len_in_blk > count)
+			len_in_blk = count;
+
+		/* Write data to single block. */
+		if (len_in_blk < BLOCK_SZ) {
+			ASSERT(dev_read_block(blk_buffer, blkaddr) >= 0);
+			memcpy(blk_buffer + off_in_blk, buffer, len_in_blk);
+			ASSERT(dev_write_block(blk_buffer, blkaddr) >= 0);
+		} else {
+			/* Direct write */
+			ASSERT(dev_write_block(buffer, blkaddr) >= 0);
+		}
+
+		offset += len_in_blk;
+		count -= len_in_blk;
+		buffer += len_in_blk;
+		written_count += len_in_blk;
+
+		dn.ofs_in_node++;
+		if ((--remained_blkentries == 0 || count == 0) && (dn.ndirty))
+			ASSERT(dev_write_block(dn.node_blk, dn.node_blkaddr) >= 0);
+	}
+	if (offset > le64_to_cpu(inode->i.i_size)) {
+		inode->i.i_size = cpu_to_le64(offset);
 		idirty = 1;
 	}
-
 	if (idirty) {
 		ASSERT(inode == dn.inode_blk);
 		write_inode(ni.blk_addr, inode);
 	}
+	if (index_node)
+		free(index_node);
+	free(blk_buffer);
 
-	if (dn.node_blk && dn.node_blk != dn.inode_blk)
-		free(dn.node_blk);
-	free(data_blk);
+	return written_count;
+}
+
+/* This function updates only inode->i.i_size */
+void f2fs_filesize_update(struct f2fs_sb_info *sbi, nid_t ino, u64 filesize)
+{
+	struct node_info ni;
+	struct f2fs_node *inode;
+
+	inode = calloc(BLOCK_SZ, 1);
+	ASSERT(inode);
+	get_node_info(sbi, ino, &ni);
+
+	ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
+	ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode)));
+	ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode)));
+
+	inode->i.i_size = cpu_to_le64(filesize);
+
+	write_inode(ni.blk_addr, inode);
 	free(inode);
 }
 
@@ -221,7 +303,7 @@
 		free(node_blk);
 	} else {
 		while ((n = read(fd, buffer, BLOCK_SZ)) > 0) {
-			f2fs_write_block(sbi, de->ino, buffer, n, off);
+			f2fs_write(sbi, de->ino, buffer, n, off);
 			off += n;
 		}
 	}