mirror of
https://gitee.com/openharmony/third_party_f2fs-tools
synced 2024-11-23 01:59:54 +00:00
0bed51f3ad
Update allocation policy for ro feature: - hot_data: allocating blocks by LBA ascending order - hot_node: allocating blocks by LBA descending order Signed-off-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
725 lines
18 KiB
C
725 lines
18 KiB
C
/**
|
|
* segment.c
|
|
*
|
|
* Many parts of codes are copied from Linux kernel/fs/f2fs.
|
|
*
|
|
* Copyright (C) 2015 Huawei Ltd.
|
|
* Witten by:
|
|
* Hou Pengyang <houpengyang@huawei.com>
|
|
* Liu Shuoran <liushuoran@huawei.com>
|
|
* Jaegeuk Kim <jaegeuk@kernel.org>
|
|
* Copyright (c) 2020 Google Inc.
|
|
* Robin Hsu <robinhsu@google.com>
|
|
* : add sload compression support
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
#include "fsck.h"
|
|
#include "node.h"
|
|
#include "quotaio.h"
|
|
|
|
int reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
|
|
struct f2fs_summary *sum, int type, bool is_inode)
|
|
{
|
|
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
|
struct seg_entry *se;
|
|
u64 blkaddr, offset;
|
|
u64 old_blkaddr = *to;
|
|
bool is_node = IS_NODESEG(type);
|
|
int left = 0;
|
|
|
|
if (old_blkaddr == NULL_ADDR) {
|
|
if (c.func == FSCK) {
|
|
if (fsck->chk.valid_blk_cnt >= sbi->user_block_count) {
|
|
ERR_MSG("Not enough space\n");
|
|
return -ENOSPC;
|
|
}
|
|
if (is_node && fsck->chk.valid_node_cnt >=
|
|
sbi->total_valid_node_count) {
|
|
ERR_MSG("Not enough space for node block\n");
|
|
return -ENOSPC;
|
|
}
|
|
} else {
|
|
if (sbi->total_valid_block_count >=
|
|
sbi->user_block_count) {
|
|
ERR_MSG("Not enough space\n");
|
|
return -ENOSPC;
|
|
}
|
|
if (is_node && sbi->total_valid_node_count >=
|
|
sbi->total_node_count) {
|
|
ERR_MSG("Not enough space for node block\n");
|
|
return -ENOSPC;
|
|
}
|
|
}
|
|
}
|
|
|
|
blkaddr = SM_I(sbi)->main_blkaddr;
|
|
|
|
if (sbi->raw_super->feature & cpu_to_le32(F2FS_FEATURE_RO)) {
|
|
if (IS_NODESEG(type)) {
|
|
type = CURSEG_HOT_NODE;
|
|
blkaddr = __end_block_addr(sbi);
|
|
left = 1;
|
|
} else if (IS_DATASEG(type)) {
|
|
type = CURSEG_HOT_DATA;
|
|
blkaddr = SM_I(sbi)->main_blkaddr;
|
|
left = 0;
|
|
}
|
|
}
|
|
|
|
if (find_next_free_block(sbi, &blkaddr, left, type, false)) {
|
|
ERR_MSG("Can't find free block");
|
|
ASSERT(0);
|
|
}
|
|
|
|
se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr));
|
|
offset = OFFSET_IN_SEG(sbi, blkaddr);
|
|
se->type = type;
|
|
se->valid_blocks++;
|
|
f2fs_set_bit(offset, (char *)se->cur_valid_map);
|
|
if (need_fsync_data_record(sbi)) {
|
|
se->ckpt_type = type;
|
|
se->ckpt_valid_blocks++;
|
|
f2fs_set_bit(offset, (char *)se->ckpt_valid_map);
|
|
}
|
|
if (c.func == FSCK) {
|
|
f2fs_set_main_bitmap(sbi, blkaddr, type);
|
|
f2fs_set_sit_bitmap(sbi, blkaddr);
|
|
}
|
|
|
|
if (old_blkaddr == NULL_ADDR) {
|
|
sbi->total_valid_block_count++;
|
|
if (is_node) {
|
|
sbi->total_valid_node_count++;
|
|
if (is_inode)
|
|
sbi->total_valid_inode_count++;
|
|
}
|
|
if (c.func == FSCK) {
|
|
fsck->chk.valid_blk_cnt++;
|
|
if (is_node) {
|
|
fsck->chk.valid_node_cnt++;
|
|
if (is_inode)
|
|
fsck->chk.valid_inode_cnt++;
|
|
}
|
|
}
|
|
}
|
|
se->dirty = 1;
|
|
|
|
/* read/write SSA */
|
|
*to = (block_t)blkaddr;
|
|
update_sum_entry(sbi, *to, sum);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int new_data_block(struct f2fs_sb_info *sbi, void *block,
|
|
struct dnode_of_data *dn, int type)
|
|
{
|
|
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
|
struct f2fs_summary sum;
|
|
struct node_info ni;
|
|
unsigned int blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node);
|
|
int ret;
|
|
|
|
if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
|
|
type != CURSEG_HOT_DATA)
|
|
type = CURSEG_HOT_DATA;
|
|
|
|
ASSERT(dn->node_blk);
|
|
memset(block, 0, BLOCK_SZ);
|
|
|
|
get_node_info(sbi, dn->nid, &ni);
|
|
set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
|
|
|
|
dn->data_blkaddr = blkaddr;
|
|
ret = reserve_new_block(sbi, &dn->data_blkaddr, &sum, type, 0);
|
|
if (ret) {
|
|
c.alloc_failed = 1;
|
|
return ret;
|
|
}
|
|
|
|
if (blkaddr == NULL_ADDR)
|
|
inc_inode_blocks(dn);
|
|
else if (blkaddr == NEW_ADDR)
|
|
dn->idirty = 1;
|
|
set_data_blkaddr(dn);
|
|
return 0;
|
|
}
|
|
|
|
u64 f2fs_quota_size(struct quota_file *qf)
|
|
{
|
|
struct node_info ni;
|
|
struct f2fs_node *inode;
|
|
u64 filesize;
|
|
|
|
inode = (struct f2fs_node *) calloc(BLOCK_SZ, 1);
|
|
ASSERT(inode);
|
|
|
|
/* Read inode */
|
|
get_node_info(qf->sbi, qf->ino, &ni);
|
|
ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
|
|
ASSERT(S_ISREG(le16_to_cpu(inode->i.i_mode)));
|
|
|
|
filesize = le64_to_cpu(inode->i.i_size);
|
|
free(inode);
|
|
return filesize;
|
|
}
|
|
|
|
u64 f2fs_read(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
|
|
u64 count, pgoff_t offset)
|
|
{
|
|
struct dnode_of_data dn;
|
|
struct node_info ni;
|
|
struct f2fs_node *inode;
|
|
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);
|
|
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)));
|
|
|
|
/* 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;
|
|
|
|
/* 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(sbi,
|
|
dn.node_blk, dn.inode_blk);
|
|
}
|
|
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);
|
|
|
|
return read_count;
|
|
}
|
|
|
|
/*
|
|
* Do not call this function directly. Instead, call one of the following:
|
|
* u64 f2fs_write();
|
|
* u64 f2fs_write_compress_data();
|
|
* u64 f2fs_write_addrtag();
|
|
*/
|
|
static u64 f2fs_write_ex(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
|
|
u64 count, pgoff_t offset, enum wr_addr_type addr_type)
|
|
{
|
|
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;
|
|
int err;
|
|
bool has_data = (addr_type == WR_NORMAL
|
|
|| addr_type == WR_COMPRESS_DATA);
|
|
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
/*
|
|
* Enforce calling from f2fs_write(), f2fs_write_compress_data(),
|
|
* and f2fs_write_addrtag(). Beside, check if is properly called.
|
|
*/
|
|
ASSERT((!has_data && buffer == NULL) || (has_data && buffer != NULL));
|
|
if (addr_type != WR_NORMAL)
|
|
ASSERT(offset % F2FS_BLKSIZE == 0); /* block boundary only */
|
|
|
|
/* 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);
|
|
err = get_dnode_of_data(sbi, &dn,
|
|
F2FS_BYTES_TO_BLK(offset), ALLOC_NODE);
|
|
if (err)
|
|
break;
|
|
idirty |= dn.idirty;
|
|
free(index_node);
|
|
index_node = (dn.node_blk == dn.inode_blk) ?
|
|
NULL : dn.node_blk;
|
|
remained_blkentries = ADDRS_PER_PAGE(sbi,
|
|
dn.node_blk, dn.inode_blk) -
|
|
dn.ofs_in_node;
|
|
}
|
|
ASSERT(remained_blkentries > 0);
|
|
|
|
if (!has_data) {
|
|
dn.data_blkaddr = addr_type;
|
|
set_data_blkaddr(&dn);
|
|
idirty |= dn.idirty;
|
|
if (dn.ndirty)
|
|
ASSERT(dev_write_block(dn.node_blk,
|
|
dn.node_blkaddr) >= 0);
|
|
written_count = 0;
|
|
break;
|
|
}
|
|
|
|
blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
|
|
if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) {
|
|
err = new_data_block(sbi, blk_buffer,
|
|
&dn, CURSEG_WARM_DATA);
|
|
if (err)
|
|
break;
|
|
blkaddr = dn.data_blkaddr;
|
|
idirty |= dn.idirty;
|
|
}
|
|
|
|
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 (addr_type == WR_NORMAL && 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);
|
|
ASSERT(write_inode(inode, ni.blk_addr) >= 0);
|
|
}
|
|
|
|
free(index_node);
|
|
free(blk_buffer);
|
|
|
|
return written_count;
|
|
}
|
|
|
|
u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
|
|
u64 count, pgoff_t offset)
|
|
{
|
|
return f2fs_write_ex(sbi, ino, buffer, count, offset, WR_NORMAL);
|
|
}
|
|
|
|
u64 f2fs_write_compress_data(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer,
|
|
u64 count, pgoff_t offset)
|
|
{
|
|
return f2fs_write_ex(sbi, ino, buffer, count, offset, WR_COMPRESS_DATA);
|
|
}
|
|
|
|
u64 f2fs_write_addrtag(struct f2fs_sb_info *sbi, nid_t ino, pgoff_t offset,
|
|
unsigned int addrtag)
|
|
{
|
|
ASSERT(addrtag == COMPRESS_ADDR || addrtag == NEW_ADDR
|
|
|| addrtag == NULL_ADDR);
|
|
return f2fs_write_ex(sbi, ino, NULL, F2FS_BLKSIZE, offset, addrtag);
|
|
}
|
|
|
|
/* 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);
|
|
|
|
ASSERT(write_inode(inode, ni.blk_addr) >= 0);
|
|
free(inode);
|
|
}
|
|
|
|
#define MAX_BULKR_RETRY 5
|
|
int bulkread(int fd, void *rbuf, size_t rsize, bool *eof)
|
|
{
|
|
int n = 0;
|
|
int retry = MAX_BULKR_RETRY;
|
|
int cur;
|
|
|
|
if (!rsize)
|
|
return 0;
|
|
|
|
if (eof != NULL)
|
|
*eof = false;
|
|
while (rsize && (cur = read(fd, rbuf, rsize)) != 0) {
|
|
if (cur == -1) {
|
|
if (errno == EINTR && retry--)
|
|
continue;
|
|
return -1;
|
|
}
|
|
retry = MAX_BULKR_RETRY;
|
|
|
|
rsize -= cur;
|
|
n += cur;
|
|
}
|
|
if (eof != NULL)
|
|
*eof = (cur == 0);
|
|
return n;
|
|
}
|
|
|
|
u64 f2fs_fix_mutable(struct f2fs_sb_info *sbi, nid_t ino, pgoff_t offset,
|
|
unsigned int compressed)
|
|
{
|
|
unsigned int i;
|
|
u64 wlen;
|
|
|
|
if (c.compress.readonly)
|
|
return 0;
|
|
|
|
for (i = 0; i < compressed - 1; i++) {
|
|
wlen = f2fs_write_addrtag(sbi, ino,
|
|
offset + (i << F2FS_BLKSIZE_BITS), NEW_ADDR);
|
|
if (wlen)
|
|
return wlen;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline int is_consecutive(u32 prev_addr, u32 cur_addr)
|
|
{
|
|
if (is_valid_data_blkaddr(cur_addr) && (cur_addr == prev_addr + 1))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static inline void copy_extent_info(struct extent_info *t_ext,
|
|
struct extent_info *s_ext)
|
|
{
|
|
t_ext->fofs = s_ext->fofs;
|
|
t_ext->blk = s_ext->blk;
|
|
t_ext->len = s_ext->len;
|
|
}
|
|
|
|
static inline void update_extent_info(struct f2fs_node *inode,
|
|
struct extent_info *ext)
|
|
{
|
|
inode->i.i_ext.fofs = cpu_to_le32(ext->fofs);
|
|
inode->i.i_ext.blk_addr = cpu_to_le32(ext->blk);
|
|
inode->i.i_ext.len = cpu_to_le32(ext->len);
|
|
}
|
|
|
|
static void update_largest_extent(struct f2fs_sb_info *sbi, nid_t ino)
|
|
{
|
|
struct dnode_of_data dn;
|
|
struct node_info ni;
|
|
struct f2fs_node *inode;
|
|
u32 blkaddr, prev_blkaddr, cur_blk = 0, end_blk;
|
|
struct extent_info largest_ext, cur_ext;
|
|
u64 remained_blkentries = 0;
|
|
u32 cluster_size;
|
|
int count;
|
|
void *index_node = NULL;
|
|
|
|
memset(&dn, 0, sizeof(dn));
|
|
largest_ext.len = cur_ext.len = 0;
|
|
|
|
inode = (struct f2fs_node *) calloc(BLOCK_SZ, 1);
|
|
ASSERT(inode);
|
|
|
|
/* Read inode info */
|
|
get_node_info(sbi, ino, &ni);
|
|
ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
|
|
cluster_size = 1 << inode->i.i_log_cluster_size;
|
|
|
|
if (inode->i.i_inline & F2FS_INLINE_DATA)
|
|
goto exit;
|
|
|
|
end_blk = f2fs_max_file_offset(&inode->i) >> F2FS_BLKSIZE_BITS;
|
|
|
|
while (cur_blk <= end_blk) {
|
|
if (remained_blkentries == 0) {
|
|
set_new_dnode(&dn, inode, NULL, ino);
|
|
get_dnode_of_data(sbi, &dn, cur_blk, 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(sbi,
|
|
dn.node_blk, dn.inode_blk);
|
|
}
|
|
ASSERT(remained_blkentries > 0);
|
|
|
|
blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
|
|
if (cur_ext.len > 0) {
|
|
if (is_consecutive(prev_blkaddr, blkaddr))
|
|
cur_ext.len++;
|
|
else {
|
|
if (cur_ext.len > largest_ext.len)
|
|
copy_extent_info(&largest_ext,
|
|
&cur_ext);
|
|
cur_ext.len = 0;
|
|
}
|
|
}
|
|
|
|
if (cur_ext.len == 0 && is_valid_data_blkaddr(blkaddr)) {
|
|
cur_ext.fofs = cur_blk;
|
|
cur_ext.len = 1;
|
|
cur_ext.blk = blkaddr;
|
|
}
|
|
|
|
prev_blkaddr = blkaddr;
|
|
count = blkaddr == COMPRESS_ADDR ? cluster_size : 1;
|
|
cur_blk += count;
|
|
dn.ofs_in_node += count;
|
|
remained_blkentries -= count;
|
|
}
|
|
|
|
exit:
|
|
if (cur_ext.len > largest_ext.len)
|
|
copy_extent_info(&largest_ext, &cur_ext);
|
|
if (largest_ext.len > 0) {
|
|
update_extent_info(inode, &largest_ext);
|
|
ASSERT(write_inode(inode, ni.blk_addr) >= 0);
|
|
}
|
|
|
|
if (index_node)
|
|
free(index_node);
|
|
free(inode);
|
|
}
|
|
|
|
int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
|
|
{
|
|
int fd, n = -1;
|
|
pgoff_t off = 0;
|
|
u8 buffer[BLOCK_SZ];
|
|
struct node_info ni;
|
|
struct f2fs_node *node_blk;
|
|
|
|
if (de->ino == 0)
|
|
return -1;
|
|
|
|
if (de->from_devino) {
|
|
struct hardlink_cache_entry *found_hardlink;
|
|
|
|
found_hardlink = f2fs_search_hardlink(sbi, de);
|
|
if (found_hardlink && found_hardlink->to_ino &&
|
|
found_hardlink->nbuild)
|
|
return 0;
|
|
|
|
found_hardlink->nbuild++;
|
|
}
|
|
|
|
fd = open(de->full_path, O_RDONLY);
|
|
if (fd < 0) {
|
|
MSG(0, "Skip: Fail to open %s\n", de->full_path);
|
|
return -1;
|
|
}
|
|
|
|
/* inline_data support */
|
|
if (de->size <= DEF_MAX_INLINE_DATA) {
|
|
int ret;
|
|
|
|
get_node_info(sbi, de->ino, &ni);
|
|
|
|
node_blk = calloc(BLOCK_SZ, 1);
|
|
ASSERT(node_blk);
|
|
|
|
ret = dev_read_block(node_blk, ni.blk_addr);
|
|
ASSERT(ret >= 0);
|
|
|
|
node_blk->i.i_inline |= F2FS_INLINE_DATA;
|
|
node_blk->i.i_inline |= F2FS_DATA_EXIST;
|
|
|
|
if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) {
|
|
node_blk->i.i_inline |= F2FS_EXTRA_ATTR;
|
|
node_blk->i.i_extra_isize =
|
|
cpu_to_le16(calc_extra_isize());
|
|
}
|
|
n = read(fd, buffer, BLOCK_SZ);
|
|
ASSERT((unsigned long)n == de->size);
|
|
memcpy(inline_data_addr(node_blk), buffer, de->size);
|
|
node_blk->i.i_size = cpu_to_le64(de->size);
|
|
ASSERT(write_inode(node_blk, ni.blk_addr) >= 0);
|
|
free(node_blk);
|
|
#ifdef WITH_SLOAD
|
|
} else if (c.func == SLOAD && c.compress.enabled &&
|
|
c.compress.filter_ops->filter(de->full_path)) {
|
|
bool eof = false;
|
|
u8 *rbuf = c.compress.cc.rbuf;
|
|
unsigned int cblocks = 0;
|
|
|
|
node_blk = calloc(BLOCK_SZ, 1);
|
|
ASSERT(node_blk);
|
|
|
|
/* read inode */
|
|
get_node_info(sbi, de->ino, &ni);
|
|
ASSERT(dev_read_block(node_blk, ni.blk_addr) >= 0);
|
|
/* update inode meta */
|
|
node_blk->i.i_compress_algrithm = c.compress.alg;
|
|
node_blk->i.i_log_cluster_size =
|
|
c.compress.cc.log_cluster_size;
|
|
node_blk->i.i_flags = cpu_to_le32(F2FS_COMPR_FL);
|
|
if (c.compress.readonly)
|
|
node_blk->i.i_inline |= F2FS_COMPRESS_RELEASED;
|
|
ASSERT(write_inode(node_blk, ni.blk_addr) >= 0);
|
|
|
|
while (!eof && (n = bulkread(fd, rbuf, c.compress.cc.rlen,
|
|
&eof)) > 0) {
|
|
int ret = c.compress.ops->compress(&c.compress.cc);
|
|
u64 wlen;
|
|
u32 csize = ALIGN_UP(c.compress.cc.clen +
|
|
COMPRESS_HEADER_SIZE, BLOCK_SZ);
|
|
unsigned int cur_cblk;
|
|
|
|
if (ret || n < c.compress.cc.rlen ||
|
|
n < (int)(csize + BLOCK_SZ *
|
|
c.compress.min_blocks)) {
|
|
wlen = f2fs_write(sbi, de->ino, rbuf, n, off);
|
|
ASSERT((int)wlen == n);
|
|
} else {
|
|
wlen = f2fs_write_addrtag(sbi, de->ino, off,
|
|
WR_COMPRESS_ADDR);
|
|
ASSERT(!wlen);
|
|
wlen = f2fs_write_compress_data(sbi, de->ino,
|
|
(u8 *)c.compress.cc.cbuf,
|
|
csize, off + BLOCK_SZ);
|
|
ASSERT(wlen == csize);
|
|
c.compress.ops->reset(&c.compress.cc);
|
|
cur_cblk = (c.compress.cc.rlen - csize) /
|
|
BLOCK_SZ;
|
|
cblocks += cur_cblk;
|
|
wlen = f2fs_fix_mutable(sbi, de->ino,
|
|
off + BLOCK_SZ + csize,
|
|
cur_cblk);
|
|
ASSERT(!wlen);
|
|
}
|
|
off += n;
|
|
}
|
|
if (n == -1) {
|
|
fprintf(stderr, "Load file '%s' failed: ",
|
|
de->full_path);
|
|
perror(NULL);
|
|
}
|
|
/* read inode */
|
|
get_node_info(sbi, de->ino, &ni);
|
|
ASSERT(dev_read_block(node_blk, ni.blk_addr) >= 0);
|
|
/* update inode meta */
|
|
node_blk->i.i_size = cpu_to_le64(off);
|
|
if (!c.compress.readonly) {
|
|
node_blk->i.i_compr_blocks = cpu_to_le64(cblocks);
|
|
node_blk->i.i_blocks += cpu_to_le64(cblocks);
|
|
}
|
|
ASSERT(write_inode(node_blk, ni.blk_addr) >= 0);
|
|
free(node_blk);
|
|
|
|
if (!c.compress.readonly) {
|
|
sbi->total_valid_block_count += cblocks;
|
|
if (sbi->total_valid_block_count >=
|
|
sbi->user_block_count) {
|
|
ERR_MSG("Not enough space\n");
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
while ((n = read(fd, buffer, BLOCK_SZ)) > 0) {
|
|
f2fs_write(sbi, de->ino, buffer, n, off);
|
|
off += n;
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
if (n < 0)
|
|
return -1;
|
|
|
|
if (!c.compress.enabled || (c.feature & cpu_to_le32(F2FS_FEATURE_RO)))
|
|
update_largest_extent(sbi, de->ino);
|
|
update_free_segments(sbi);
|
|
|
|
MSG(1, "Info: Create %s -> %s\n"
|
|
" -- ino=%x, type=%x, mode=%x, uid=%x, "
|
|
"gid=%x, cap=%"PRIx64", size=%lu, pino=%x\n",
|
|
de->full_path, de->path,
|
|
de->ino, de->file_type, de->mode,
|
|
de->uid, de->gid, de->capabilities, de->size, de->pino);
|
|
return 0;
|
|
}
|