[f2fs] Support truncate cmd
Add truncate handler to File class
Test: fx test truncate-tests
Change-Id: I80b6d6cc77720adb78a9dd9c554bbf0c4337bf38
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/f2fs/+/536506
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/f2fs.h b/f2fs.h
index d3ffa4f..5c391ef 100644
--- a/f2fs.h
+++ b/f2fs.h
@@ -125,8 +125,8 @@
// dentry *F2fsFhToDentry(fid *fid, int fh_len, int fh_type);
// dentry *F2fsFhToParent(fid *fid, int fh_len, int fh_type);
// int ParseOptions(char *options);
- // loff_t MaxFileSize(unsigned bits);
#endif
+ loff_t MaxFileSize(unsigned bits);
int SanityCheckRawSuper();
int SanityCheckCkpt();
void InitSbInfo();
diff --git a/file.cc b/file.cc
index 61f5b54..8b1b4d5 100644
--- a/file.cc
+++ b/file.cc
@@ -436,9 +436,15 @@
}
for (n = blk_start; n <= blk_end; n++) {
+ bool is_empty_page = false;
+
if (zx_status_t ret = GetLockDataPage(n, &data_page); ret != ZX_OK) {
- *out_actual = off_in_buf;
- return ret;
+ if (ret == ZX_ERR_NOT_FOUND) { // truncated page
+ is_empty_page = true;
+ } else {
+ *out_actual = off_in_buf;
+ return ret;
+ }
}
size_t cur_len = std::min(static_cast<size_t>(kBlockSize - off_in_block), left);
@@ -447,16 +453,22 @@
cur_len = std::min(cur_len, static_cast<size_t>(i_size_ % kBlockSize));
}
- data_buf = PageAddress(data_page);
- memcpy(static_cast<char *>(data) + off_in_buf, static_cast<char *>(data_buf) + off_in_block,
- cur_len);
+ if (is_empty_page) {
+ memset(static_cast<char *>(data) + off_in_buf, 0, cur_len);
+ } else {
+ data_buf = PageAddress(data_page);
+ memcpy(static_cast<char *>(data) + off_in_buf, static_cast<char *>(data_buf) + off_in_block,
+ cur_len);
+ }
off_in_buf += cur_len;
left -= cur_len;
off_in_block = 0;
- F2fsPutPage(data_page, 1);
- data_page = nullptr;
+ if (!is_empty_page) {
+ F2fsPutPage(data_page, 1);
+ data_page = nullptr;
+ }
if (left == 0)
break;
@@ -535,4 +547,16 @@
return ZX_OK;
}
+zx_status_t File::Truncate(size_t len) {
+ if (len == i_size_)
+ return ZX_OK;
+
+ if (len > static_cast<size_t>(Vfs()->MaxFileSize(Vfs()->RawSb().log_blocksize)))
+ return ZX_ERR_INVALID_ARGS;
+
+ i_size_ = len;
+
+ return DoTruncate();
+}
+
} // namespace f2fs
diff --git a/file.h b/file.h
index 29e9946..b99e9c3 100644
--- a/file.h
+++ b/file.h
@@ -29,6 +29,7 @@
zx_status_t Read(void* data, size_t len, size_t off, size_t* out_actual);
zx_status_t Write(const void* data, size_t len, size_t offset, size_t* out_actual);
+ zx_status_t Truncate(size_t len);
};
} // namespace f2fs
diff --git a/super.cc b/super.cc
index e5ec589..642718e 100644
--- a/super.cc
+++ b/super.cc
@@ -257,27 +257,27 @@
// }
// return 0;
// }
-
-// loff_t F2fs::MaxFileSize(unsigned bits) {
-// loff_t result = kAddrsPerInode;
-// loff_t leaf_count = kAddrsPerBlock;
-
-// /* two direct node blocks */
-// result += (leaf_count * 2);
-
-// /* two indirect node blocks */
-// leaf_count *= kNidsPerBlock;
-// result += (leaf_count * 2);
-
-// /* one double indirect node block */
-// leaf_count *= kNidsPerBlock;
-// result += leaf_count;
-
-// result <<= bits;
-// return result;
-// }
#endif
+loff_t F2fs::MaxFileSize(unsigned bits) {
+ loff_t result = kAddrsPerInode;
+ loff_t leaf_count = kAddrsPerBlock;
+
+ /* two direct node blocks */
+ result += (leaf_count * 2);
+
+ /* two indirect node blocks */
+ leaf_count *= kNidsPerBlock;
+ result += (leaf_count * 2);
+
+ /* one double indirect node block */
+ leaf_count *= kNidsPerBlock;
+ result += leaf_count;
+
+ result <<= bits;
+ return result;
+}
+
int F2fs::SanityCheckRawSuper() {
unsigned int blocksize;
diff --git a/vnode.cc b/vnode.cc
index fb99172..c4cc0c0 100644
--- a/vnode.cc
+++ b/vnode.cc
@@ -350,23 +350,21 @@
return ZX_OK;
}
-#if 0 // porting needed
-// void VnodeF2fs::F2fsTruncate() {
-// if (!(S_ISREG(i_mode_) || S_ISDIR(i_mode_) || S_ISLNK(i_mode_)))
-// return;
+zx_status_t VnodeF2fs::DoTruncate() {
+ zx_status_t ret;
-// if (!TruncateBlocks(i_size)) {
-// auto cur_time = time(nullptr);
-// i_mtime.tv_sec = cur_time;
-// i_mtime.tv_nsec = 0;
-// i_ctime.tv_sec = cur_time;
-// i_ctime.tv_nsec = 0;
-// MarkInodeDirty(this);
-// }
+ if (ret = TruncateBlocks(i_size_); ret == ZX_OK) {
+ auto cur_time = time(nullptr);
+ i_mtime_.tv_sec = cur_time;
+ i_mtime_.tv_nsec = 0;
+ i_ctime_.tv_sec = cur_time;
+ i_ctime_.tv_nsec = 0;
+ MarkInodeDirty(this);
+ }
-// Vfs()->Segmgr().BalanceFs();
-// }
-#endif
+ Vfs()->Segmgr().BalanceFs();
+ return ret;
+}
int VnodeF2fs::TruncateDataBlocksRange(DnodeOfData *dn, int count) {
int nr_free = 0, ofs = dn->ofs_in_node;
@@ -402,7 +400,7 @@
void VnodeF2fs::TruncateDataBlocks(DnodeOfData *dn) { TruncateDataBlocksRange(dn, kAddrsPerBlock); }
void VnodeF2fs::TruncatePartialDataPage(uint64_t from) {
- unsigned offset = from & (kPageCacheSize - 1);
+ size_t offset = from & (kPageCacheSize - 1);
Page *page = nullptr;
if (!offset)
@@ -424,51 +422,51 @@
F2fsPutPage(page, 1);
}
-#if 0 // porting needed
-// int VnodeF2fs::TruncateBlocks(uint64_t from) {
-// SbInfo &sbi = Vfs()->GetSbInfo();
-// unsigned int blocksize = sbi.blocksize;
-// DnodeOfData dn;
-// pgoff_t free_from;
-// int count = 0;
-// int err;
+zx_status_t VnodeF2fs::TruncateBlocks(uint64_t from) {
+ SbInfo &sbi = Vfs()->GetSbInfo();
+ unsigned int blocksize = sbi.blocksize;
+ DnodeOfData dn;
+ pgoff_t free_from;
+ int count = 0;
+ zx_status_t err;
-// free_from = (pgoff_t)((from + blocksize - 1) >> (sbi.log_blocksize));
+ free_from = static_cast<pgoff_t>((from + blocksize - 1) >> (sbi.log_blocksize));
-// mutex_lock_op(&sbi, LockType::kDataTrunc);
+ mutex_lock_op(&sbi, LockType::kDataTrunc);
-// SetNewDnode(&dn, this, NULL, NULL, 0);
-// err = Vfs()->Nodemgr().GetDnodeOfData(&dn, free_from, kRdOnlyNode);
-// if (err) {
-// if (err == ZX_ERR_NOT_FOUND)
-// goto free_next;
-// mutex_unlock_op(&sbi, LockType::kDataTrunc);
-// return err;
-// }
+ do {
+ SetNewDnode(&dn, this, nullptr, nullptr, 0);
+ err = Vfs()->Nodemgr().GetDnodeOfData(&dn, free_from, kRdOnlyNode);
+ if (err) {
+ if (err == ZX_ERR_NOT_FOUND)
+ break;
+ mutex_unlock_op(&sbi, LockType::kDataTrunc);
+ return err;
+ }
-// if (IsInode(dn.node_page))
-// count = kAddrsPerInode;
-// else
-// count = kAddrsPerBlock;
+ if (IsInode(dn.node_page))
+ count = kAddrsPerInode;
+ else
+ count = kAddrsPerBlock;
-// count -= dn.ofs_in_node;
-// BUG_ON(count < 0);
-// if (dn.ofs_in_node || IsInode(dn.node_page)) {
-// TruncateDataBlocksRange(&dn, count);
-// free_from += count;
-// }
+ count -= dn.ofs_in_node;
+ ZX_ASSERT(count >= 0);
+ if (dn.ofs_in_node || IsInode(dn.node_page)) {
+ TruncateDataBlocksRange(&dn, count);
+ free_from += count;
+ }
-// F2fsPutDnode(&dn);
-// free_next:
-// err = Vfs()->Nodemgr().TruncateInodeBlocks(this, free_from);
-// mutex_unlock_op(&sbi, LockType::kDataTrunc);
+ F2fsPutDnode(&dn);
+ } while (false);
-// /* lastly zero out the first data page */
-// TruncatePartialDataPage(from);
+ err = Vfs()->Nodemgr().TruncateInodeBlocks(this, free_from);
+ mutex_unlock_op(&sbi, LockType::kDataTrunc);
-// return err;
-// }
-#endif
+ /* lastly zero out the first data page */
+ TruncatePartialDataPage(from);
+
+ return err;
+}
zx_status_t VnodeF2fs::TruncateHole(pgoff_t pg_start, pgoff_t pg_end) {
pgoff_t index;
diff --git a/vnode.h b/vnode.h
index 7cd5d09..1077770 100644
--- a/vnode.h
+++ b/vnode.h
@@ -58,15 +58,11 @@
static zx_status_t Vget(F2fs *fs, uint64_t ino, fbl::RefPtr<VnodeF2fs> *out);
void UpdateInode(Page *node_page);
zx_status_t WriteInode(WritebackControl *wbc);
-#if 0 // porting needed
- // void F2fsTruncate();
-#endif
+ zx_status_t DoTruncate();
int TruncateDataBlocksRange(DnodeOfData *dn, int count);
void TruncateDataBlocks(DnodeOfData *dn);
void TruncatePartialDataPage(uint64_t from);
-#if 0 // porting needed
- // int TruncateBlocks(uint64_t from);
-#endif
+ zx_status_t TruncateBlocks(uint64_t from);
zx_status_t TruncateHole(pgoff_t pg_start, pgoff_t pg_end);
#if 0 // porting needed
// void F2fsEvictInode();