| // 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 <sys/stat.h> |
| |
| #include <algorithm> |
| |
| #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; |
| // // // SbInfo &sbi = Vfs()->GetSbInfo(); |
| // // Page *node_page; |
| // // block_t old_blk_addr; |
| // // DnodeOfData dn; |
| // // int err; |
| |
| // // Vfs()->Segmgr().BalanceFs(); |
| |
| // // sb_start_pagefault(nullptr); |
| |
| // // // mutex_lock_op(sbi, LockType::kDataNew); |
| |
| // // /* block allocation */ |
| // // SetNewDnode(&dn, vnode, NULL, NULL, 0); |
| // // err = Vfs()->Nodemgr().GetDnodeOfData(&dn, page->index, 0); |
| // // if (err) { |
| // // // mutex_unlock_op(sbi, LockType::kDataNew); |
| // // goto out; |
| // // } |
| |
| // // old_blk_addr = dn.data_blkaddr; |
| // // node_page = dn.node_page; |
| |
| // // if (old_blk_addr == kNullAddr) { |
| // // err = VnodeF2fs::ReserveNewBlock(&dn); |
| // // if (err) { |
| // // F2fsPutDnode(&dn); |
| // // // mutex_unlock_op(sbi, LockType::kDataNew); |
| // // goto out; |
| // // } |
| // // } |
| // // F2fsPutDnode(&dn); |
| |
| // // // mutex_unlock_op(sbi, LockType::kDataNew); |
| |
| // // // 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 |
| |
| #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) { |
| // SbInfo &sbi = Vfs()->GetSbInfo(); |
| // 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++) { |
| // DnodeOfData dn; |
| |
| // mutex_lock_op(&sbi, LockType::kDataNew); |
| |
| // SetNewDnode(&dn, this, NULL, NULL, 0); |
| // ret = Vfs()->Nodemgr().GetDnodeOfData(&dn, index, 0); |
| // if (ret) { |
| // mutex_unlock_op(&sbi, LockType::kDataNew); |
| // break; |
| // } |
| |
| // if (dn.data_blkaddr == kNullAddr) { |
| // ret = ReserveNewBlock(&dn); |
| // if (ret) { |
| // F2fsPutDnode(&dn); |
| // mutex_unlock_op(&sbi, LockType::kDataNew); |
| // break; |
| // } |
| // } |
| // F2fsPutDnode(&dn); |
| |
| // mutex_unlock_op(&sbi, LockType::kDataNew); |
| |
| // 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().BalanceFs(); |
| // 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; |
| // InodeInfo *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 / kBlockSize; |
| uint64_t blk_end = (off + len) / kBlockSize; |
| size_t off_in_block = off % kBlockSize; |
| 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_ + kBlockSize - 1) / kBlockSize; |
| |
| fbl::AutoLock lock(&i_mutex_); |
| |
| if (off >= i_size_) { |
| *out_actual = 0; |
| return ZX_OK; |
| } |
| |
| 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) { |
| 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); |
| if (n == npages - 1) { |
| if (i_size_ % kBlockSize > 0) |
| cur_len = std::min(cur_len, static_cast<size_t>(i_size_ % kBlockSize)); |
| } |
| |
| 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; |
| |
| if (!is_empty_page) { |
| 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::DoWrite(const void *data, size_t len, size_t offset, size_t *out_actual) { |
| uint64_t blk_start = offset / kBlockSize; |
| uint64_t blk_end = (offset + len) / kBlockSize; |
| size_t off_in_block = offset % kBlockSize; |
| size_t off_in_buf = 0; |
| Page *data_page = nullptr; |
| void *data_buf = nullptr; |
| uint64_t n; |
| size_t left = len; |
| |
| for (n = blk_start; n <= blk_end; n++) { |
| size_t cur_len = std::min(static_cast<size_t>(kBlockSize - off_in_block), left); |
| if (zx_status_t ret = WriteBegin(n * kBlockSize + off_in_block, cur_len, &data_page); |
| ret != ZX_OK) { |
| if (off_in_buf > 0) { |
| clock_gettime(CLOCK_REALTIME, &i_mtime_); |
| i_ctime_ = i_mtime_; |
| |
| 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) { |
| clock_gettime(CLOCK_REALTIME, &i_mtime_); |
| i_ctime_ = i_mtime_; |
| |
| MarkInodeDirty(this); |
| } |
| *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) { |
| fbl::AutoLock lock(&i_mutex_); |
| return DoWrite(data, len, offset, out_actual); |
| } |
| |
| zx_status_t File::Append(const void *data, size_t len, size_t *out_end, size_t *out_actual) { |
| fbl::AutoLock lock(&i_mutex_); |
| size_t off = i_size_; |
| zx_status_t ret = DoWrite(data, len, off, out_actual); |
| |
| *out_end = off + *out_actual; |
| |
| return ret; |
| } |
| |
| 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 |