blob: bed3d5488289f9d931c258c86fe42a9b2d36b73c [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <sys/stat.h>
#include "f2fs.h"
namespace f2fs {
File::File(F2fs *fs) : VnodeF2fs(fs) {}
File::File(F2fs *fs, ino_t ino) : VnodeF2fs(fs, ino) {}
#if 0 // porting needed
// int File::F2fsVmPageMkwrite(vm_area_struct *vma, vm_fault *vmf) {
// return 0;
// // Page *page = vmf->page;
// // VnodeF2fs *vnode = this;
// // // f2fs_sb_info &sbi = Vfs()->SbInfo();
// // Page *node_page;
// // block_t old_blk_addr;
// // dnode_of_data dn;
// // int err;
// // Vfs()->Segmgr().F2fsBalanceFs();
// // sb_start_pagefault(nullptr);
// // // mutex_lock_op(sbi, DATA_NEW);
// // /* block allocation */
// // SetNewDnode(&dn, vnode, NULL, NULL, 0);
// // err = Vfs()->Nodemgr().GetDnodeOfData(&dn, page->index, 0);
// // if (err) {
// // // mutex_unlock_op(sbi, DATA_NEW);
// // goto out;
// // }
// // old_blk_addr = dn.data_blkaddr;
// // node_page = dn.node_page;
// // if (old_blk_addr == NULL_ADDR) {
// // err = VnodeF2fs::ReserveNewBlock(&dn);
// // if (err) {
// // F2fsPutDnode(&dn);
// // // mutex_unlock_op(sbi, DATA_NEW);
// // goto out;
// // }
// // }
// // F2fsPutDnode(&dn);
// // // mutex_unlock_op(sbi, DATA_NEW);
// // // lock_page(page);
// // // if (page->mapping != inode->i_mapping ||
// // // page_offset(page) >= i_size_read(inode) ||
// // // !PageUptodate(page)) {
// // // unlock_page(page);
// // // err = -EFAULT;
// // // goto out;
// // // }
// // /*
// // * check to see if the page is mapped already (no holes)
// // */
// // if (PageMappedToDisk(page))
// // goto out;
// // /* fill the page */
// // WaitOnPageWriteback(page);
// // /* page is wholly or partially inside EOF */
// // if (((page->index + 1) << kPageCacheShift) > i_size) {
// // unsigned offset;
// // offset = i_size & ~PAGE_CACHE_MASK;
// // ZeroUserSegment(page, offset, kPageCacheSize);
// // }
// // // set_page_dirty(page);
// // FlushDirtyDataPage(Vfs(), page);
// // SetPageUptodate(page);
// // file_update_time(vma->vm_file);
// // out:
// // sb_end_pagefault(nullptr);
// // return block_page_mkwrite_return(err);
// }
#endif
int File::NeedToSyncDir(f2fs_sb_info *sbi, VnodeF2fs *vnode) {
#if 0 // porting needed
// dentry *dentry;
#endif
nid_t pino = 0;
vnode = static_cast<VnodeF2fs *>(Igrab((void *)vnode));
#if 0 // porting needed
// dentry = d_find_any_alias(vnode);
// if (!dentry) {
// // Iput(inode);
// return 0;
// }
// pino = dentry->d_parent->d_inode->i_ino;
// dput(dentry);
#endif
Iput(vnode);
return !Vfs()->Nodemgr().IsCheckpointedNode(pino);
}
zx_status_t File::SyncFile(loff_t start, loff_t end, int datasync) {
f2fs_sb_info &sbi = Vfs()->SbInfo();
uint64_t cur_version;
zx_status_t ret = 0;
bool need_cp = false;
#if 0 // porting needed
// WritebackControl wbc;
// wbc.sync_mode = WB_SYNC_ALL;
// wbc.nr_to_write = LONG_MAX;
// wbc.for_reclaim = 0;
// ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
// if (ret)
// return ret;
#endif
fbl::AutoLock lock(&i_mutex_);
#if 0 // porting needed
// if (inode->i_sb->s_flags & MS_RDONLY)
// goto out;
// if (datasync && !(i_state & I_DIRTY_DATASYNC)) {
// goto out;
//}
#endif
do {
fbl::AutoLock cplock(&sbi.cp_mutex);
cur_version = LeToCpu(F2FS_CKPT(&sbi)->checkpoint_ver);
} while(false);
#if 0 // porting needed
// if (fi.data_version != cur_version &&
// !(i_state & I_DIRTY)) {
// goto out;
//}
#endif
fi_.data_version--;
if (!S_ISREG(i_mode_) || i_nlink_ != 1)
need_cp = true;
if (is_inode_flag_set(&fi_, FI_NEED_CP))
need_cp = true;
if (!Vfs()->SpaceForRollForward())
need_cp = true;
if (NeedToSyncDir(&sbi, this))
need_cp = true;
#if 0 // porting needed
// WriteInode(nullptr);
#endif
// TODO(unknown): fsync recovery
#if 0 // porting needed
// if (need_cp) {
#else
if (true) {
#endif
WriteInode(nullptr);
/* all the dirty node pages should be flushed for POR */
ret = Vfs()->SyncFs(1);
ClearInodeFlag(&fi_, FI_NEED_CP);
} else {
Page *node_page = nullptr;
int mark = !Vfs()->Nodemgr().IsCheckpointedNode(Ino());
if (ret = Vfs()->Nodemgr().GetNodePage(Ino(), &node_page); ret != ZX_OK) {
return ret;
}
Vfs()->Nodemgr().SetFsyncMark(node_page, 1);
Vfs()->Nodemgr().SetDentryMark(node_page, mark);
UpdateInode(node_page);
F2fsPutPage(node_page, 1);
#if 0 // porting needed
// while (Vfs()->Nodemgr().SyncNodePages(Ino(), &wbc) == 0)
// WriteInode(nullptr);
// filemap_fdatawait_range(nullptr,//sbi->node_inode->i_mapping,
// 0, LONG_MAX);
#endif
}
#if 0 // porting needed
// out:
#endif
return ret;
}
#if 0 // porting needed
// int File::F2fsFileMmap(/*file *file,*/ vm_area_struct *vma) {
// // file_accessed(file);
// // vma->vm_ops = &f2fs_file_vm_ops;
// return 0;
// }
// void File::FillZero(pgoff_t index, loff_t start, loff_t len) {
// Page *page = nullptr;
// zx_status_t err;
// if (!len)
// return;
// err = GetNewDataPage(index, false, page);
// if (!err) {
// WaitOnPageWriteback(page);
// zero_user(page, start, len);
// #if 0 // porting needed
// // set_page_dirty(page);
// #else
// FlushDirtyDataPage(Vfs(), page);
// #endif
// F2fsPutPage(page, 1);
// }
// }
// int File::PunchHole(loff_t offset, loff_t len, int mode) {
// pgoff_t pg_start, pg_end;
// loff_t off_start, off_end;
// int ret = 0;
// pg_start = ((uint64_t)offset) >> kPageCacheShift;
// pg_end = ((uint64_t)offset + len) >> kPageCacheShift;
// off_start = offset & (kPageCacheSize - 1);
// off_end = (offset + len) & (kPageCacheSize - 1);
// if (pg_start == pg_end) {
// FillZero(pg_start, off_start, off_end - off_start);
// } else {
// if (off_start)
// FillZero(pg_start++, off_start, kPageCacheSize - off_start);
// if (off_end)
// FillZero(pg_end, 0, off_end);
// if (pg_start < pg_end) {
// loff_t blk_start, blk_end;
// blk_start = pg_start << kPageCacheShift;
// blk_end = pg_end << kPageCacheShift;
// #if 0 // porting needed
// // truncate_inode_pages_range(nullptr, blk_start,
// // blk_end - 1);
// #endif
// ret = TruncateHole(pg_start, pg_end);
// }
// }
// if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size <= (offset + len)) {
// i_size = offset;
// MarkInodeDirty(this);
// }
// return ret;
// }
// int File::ExpandInodeData(loff_t offset, off_t len, int mode) {
// f2fs_sb_info &sbi = Vfs()->SbInfo();
// pgoff_t index, pg_start, pg_end;
// loff_t new_size = i_size;
// loff_t off_start, off_end;
// int ret = 0;
// #if 0 // porting needed
// // ret = inode_newsize_ok(this, (len + offset));
// // if (ret)
// // return ret;
// #endif
// pg_start = ((uint64_t)offset) >> kPageCacheShift;
// pg_end = ((uint64_t)offset + len) >> kPageCacheShift;
// off_start = offset & (kPageCacheSize - 1);
// off_end = (offset + len) & (kPageCacheSize - 1);
// for (index = pg_start; index <= pg_end; index++) {
// dnode_of_data dn;
// mutex_lock_op(&sbi, DATA_NEW);
// SetNewDnode(&dn, this, NULL, NULL, 0);
// ret = Vfs()->Nodemgr().GetDnodeOfData(&dn, index, 0);
// if (ret) {
// mutex_unlock_op(&sbi, DATA_NEW);
// break;
// }
// if (dn.data_blkaddr == NULL_ADDR) {
// ret = ReserveNewBlock(&dn);
// if (ret) {
// F2fsPutDnode(&dn);
// mutex_unlock_op(&sbi, DATA_NEW);
// break;
// }
// }
// F2fsPutDnode(&dn);
// mutex_unlock_op(&sbi, DATA_NEW);
// if (pg_start == pg_end)
// new_size = offset + len;
// else if (index == pg_start && off_start)
// new_size = (index + 1) << kPageCacheShift;
// else if (index == pg_end)
// new_size = (index << kPageCacheShift) + off_end;
// else
// new_size += kPageCacheSize;
// }
// if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size < new_size) {
// i_size = new_size;
// MarkInodeDirty(this);
// }
// return ret;
// }
// long File::F2fsFallocate(int mode, loff_t offset, loff_t len) {
// long ret;
// if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
// return -EOPNOTSUPP;
// if (mode & FALLOC_FL_PUNCH_HOLE)
// ret = PunchHole(offset, len, mode);
// else
// ret = ExpandInodeData(offset, len, mode);
// Vfs()->Segmgr().F2fsBalanceFs();
// return ret;
// }
// #define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL))
// #define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL)
// inline uint32_t File::F2fsMaskFlags(umode_t mode, uint32_t flags) {
// if (S_ISDIR(mode))
// return flags;
// else if (S_ISREG(mode))
// return flags & F2FS_REG_FLMASK;
// else
// return flags & F2FS_OTHER_FLMASK;
// }
// long File::F2fsIoctl(/*file *filp,*/ unsigned int cmd, uint64_t arg) {
// inode *inode = filp->f_dentry->d_inode;
// f2fs_inode_info *fi = F2FS_I(inode);
// unsigned int flags;
// int ret;
// switch (cmd) {
// case FS_IOC_GETFLAGS:
// flags = fi->i_flags & FS_FL_USER_VISIBLE;
// return put_user(flags, (int __user *) arg);
// case FS_IOC_SETFLAGS:
// {
// unsigned int oldflags;
// ret = mnt_want_write(filp->f_path.mnt);
// if (ret)
// return ret;
// if (!inode_owner_or_capable(inode)) {
// ret = -EACCES;
// goto out;
// }
// if (get_user(flags, (int __user *) arg)) {
// ret = -EFAULT;
// goto out;
// }
// flags = f2fs_mask_flags(inode->i_mode, flags);
// mutex_lock(&inode->i_mutex_);
// oldflags = fi->i_flags;
// if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
// if (!capable(CAP_LINUX_IMMUTABLE)) {
// mutex_unlock(&inode->i_mutex_);
// ret = -EPERM;
// goto out;
// }
// }
// flags = flags & FS_FL_USER_MODIFIABLE;
// flags |= oldflags & ~FS_FL_USER_MODIFIABLE;
// fi->i_flags = flags;
// mutex_unlock(&inode->i_mutex_);
// f2fs_set_inode_flags(inode);
// inode->i_ctime = CURRENT_TIME;
// MarkInodeDirty(inode);
// out:
// mnt_drop_write(filp->f_path.mnt);
// return ret;
// }
// default:
// return -ENOTTY;
// }
// }
#endif
zx_status_t File::Read(void *data, size_t len, size_t off, size_t *out_actual) {
uint64_t blk_start = off / kF2fsBlockSize;
uint64_t blk_end = (off + len) / kF2fsBlockSize;
size_t off_in_block = off % kF2fsBlockSize;
size_t off_in_buf = 0;
Page *data_page = nullptr;
void *data_buf = nullptr;
uint64_t n;
size_t left = len;
uint64_t npages = (i_size_ + kF2fsBlockSize - 1) / kF2fsBlockSize;
fbl::AutoLock lock(&i_mutex_);
if (off >= i_size_) {
*out_actual = 0;
return ZX_OK;
}
for (n = blk_start; n <= blk_end; n++) {
if (zx_status_t ret = GetLockDataPage(n, &data_page); ret != ZX_OK) {
*out_actual = off_in_buf;
return ret;
}
size_t cur_len = std::min(static_cast<size_t>(kF2fsBlockSize - off_in_block), left);
if (n == npages - 1) {
if (i_size_ % kF2fsBlockSize > 0)
cur_len = std::min(cur_len, static_cast<size_t>(i_size_ % kF2fsBlockSize));
}
data_buf = PageAddress(data_page);
memcpy(static_cast<char *>(data) + off_in_buf, data_buf, cur_len);
off_in_buf += cur_len;
left -= cur_len;
off_in_block = 0;
F2fsPutPage(data_page, 1);
data_page = nullptr;
if (left == 0)
break;
if (n == npages - 1)
break;
}
*out_actual = off_in_buf;
return ZX_OK;
}
zx_status_t File::Write(const void *data, size_t len, size_t offset, size_t *out_actual) {
uint64_t blk_start = offset / kF2fsBlockSize;
uint64_t blk_end = (offset + len) / kF2fsBlockSize;
size_t off_in_block = offset % kF2fsBlockSize;
size_t off_in_buf = 0;
Page *data_page = nullptr;
void *data_buf = nullptr;
uint64_t n;
size_t left = len;
fbl::AutoLock lock(&i_mutex_);
for (n = blk_start; n <= blk_end; n++) {
size_t cur_len = std::min(static_cast<size_t>(kF2fsBlockSize - off_in_block), left);
if (zx_status_t ret = WriteBegin(n * kF2fsBlockSize + off_in_block, cur_len, &data_page); ret != ZX_OK) {
if (off_in_buf > 0) {
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);
}
*out_actual = off_in_buf;
return ret;
}
data_buf = PageAddress(data_page);
memcpy(static_cast<char *>(data_buf) + off_in_block, static_cast<const char *>(data) + off_in_buf, cur_len);
off_in_buf += cur_len;
left -= cur_len;
off_in_block = 0;
i_size_ = std::max(static_cast<size_t>(i_size_), offset + off_in_buf);
#if 0 // porting needed
// set_page_dirty(data_page, Vfs());
#else
FlushDirtyDataPage(Vfs(), data_page);
#endif
F2fsPutPage(data_page, 1);
data_page = nullptr;
if (left == 0)
break;
}
if (off_in_buf > 0) {
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);
}
*out_actual = off_in_buf;
return ZX_OK;
}
} // namespace f2fs