[f2fs] Correct attribute handlings

Set correct time to time values in Inode.
Change calculation of size of allocated blocks.

Test: fx test fs-tests
Failed tests: RwFullDiskTest.PartialWriteSucceedsForFullDisk/F2fs
Failed tests: WatcherTest.Add/F2fs, WatcherTest.Existing/F2fs, WatcherTest.Removed/F2fs

Change-Id: I9181d611e1291ce00a4533f445d40c8386fbe4fb
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/f2fs/+/536511
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/dir.cc b/dir.cc
index 1290ff5..33445df 100644
--- a/dir.cc
+++ b/dir.cc
@@ -10,13 +10,7 @@
 
 Dir::Dir(F2fs *fs, ino_t ino) : VnodeF2fs(fs, ino) {}
 
-uint64_t Dir::DirBlocks() {
-  // return ((uint64_t) (i_size_read(inode) + kPageCacheSize - 1))
-  //             >> kPageCacheShift;
-  // return 0;
-  // return Inode().i_blocks - 1;
-  return (i_size_ + kBlockSize - 1) / kBlockSize;
-}
+uint64_t Dir::DirBlocks() { return GetBlocks(); }
 
 unsigned int Dir::DirBuckets(unsigned int level) {
   if (level < kMaxDirHashDepth / 2)
@@ -222,11 +216,9 @@
   FlushDirtyDataPage(Vfs(), page);
 #endif
 
-  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;
+  clock_gettime(CLOCK_REALTIME, &i_mtime_);
+  i_ctime_ = i_mtime_;
+
   MarkInodeDirty(this);
   F2fsPutPage(page, 1);
 }
@@ -305,11 +297,9 @@
     ClearInodeFlag(&vnode->fi_, InodeInfoFlag::kNewInode);
   }
 
-  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;
+  clock_gettime(CLOCK_REALTIME, &i_mtime_);
+  i_ctime_ = i_mtime_;
+
   if (fi_.i_current_depth != current_depth) {
     fi_.i_current_depth = current_depth;
     need_dir_update = true;
@@ -471,11 +461,8 @@
   FlushDirtyDataPage(Vfs(), page);
 #endif
 
-  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;
+  clock_gettime(CLOCK_REALTIME, &i_mtime_);
+  i_ctime_ = i_mtime_;
 
   if (vnode && S_ISDIR(vnode->i_mode_)) {
     DropNlink();
@@ -485,13 +472,8 @@
   }
 
   if (vnode) {
-    cur_time = time(nullptr);
-    i_ctime_.tv_sec = cur_time;
-    i_ctime_.tv_nsec = 0;
-    i_mtime_.tv_sec = cur_time;
-    i_mtime_.tv_nsec = 0;
-    vnode->i_ctime_.tv_sec = cur_time;
-    vnode->i_ctime_.tv_nsec = 0;
+    clock_gettime(CLOCK_REALTIME, &i_mtime_);
+    i_ctime_ = vnode->i_ctime_ = i_mtime_;
     vnode->DropNlink();
     if (S_ISDIR(vnode->i_mode_)) {
       vnode->DropNlink();
diff --git a/file.cc b/file.cc
index 3b9b8a1..5817b38 100644
--- a/file.cc
+++ b/file.cc
@@ -385,11 +385,8 @@
     if (zx_status_t ret = WriteBegin(n * kBlockSize + 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;
+        clock_gettime(CLOCK_REALTIME, &i_mtime_);
+        i_ctime_ = i_mtime_;
 
         MarkInodeDirty(this);
       }
@@ -420,11 +417,8 @@
   }
 
   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;
+    clock_gettime(CLOCK_REALTIME, &i_mtime_);
+    i_ctime_ = i_mtime_;
 
     MarkInodeDirty(this);
   }
diff --git a/mkfs.cc b/mkfs.cc
index a5be15f..8fcb2b8 100644
--- a/mkfs.cc
+++ b/mkfs.cc
@@ -743,12 +743,14 @@
   raw_node->i.i_size = CpuToLe(1 * blk_size_bytes); /* dentry */
   raw_node->i.i_blocks = CpuToLe(uint64_t{2});
 
-  raw_node->i.i_atime = CpuToLe(static_cast<uint64_t>(time(nullptr)));
-  raw_node->i.i_atime_nsec = 0;
-  raw_node->i.i_ctime = CpuToLe(static_cast<uint64_t>(time(nullptr)));
-  raw_node->i.i_ctime_nsec = 0;
-  raw_node->i.i_mtime = CpuToLe(static_cast<uint64_t>(time(nullptr)));
-  raw_node->i.i_mtime_nsec = 0;
+  timespec cur_time;
+  clock_gettime(CLOCK_REALTIME, &cur_time);
+  raw_node->i.i_atime = cur_time.tv_sec;
+  raw_node->i.i_atime_nsec = cur_time.tv_nsec;
+  raw_node->i.i_ctime = cur_time.tv_sec;
+  raw_node->i.i_ctime_nsec = cur_time.tv_nsec;
+  raw_node->i.i_mtime = cur_time.tv_sec;
+  raw_node->i.i_mtime_nsec = cur_time.tv_nsec;
   raw_node->i.i_generation = 0;
   raw_node->i.i_xattr_nid = 0;
   raw_node->i.i_flags = 0;
diff --git a/namei.cc b/namei.cc
index a237dc8..43924cf 100644
--- a/namei.cc
+++ b/namei.cc
@@ -13,7 +13,6 @@
   nid_t ino;
   fbl::RefPtr<VnodeF2fs> vnode_refptr;
   VnodeF2fs *vnode = nullptr;
-  auto cur_time = time(nullptr);
 
   do {
     fbl::AutoLock lock(&sbi.fs_lock[static_cast<int>(LockType::kNodeNew)]);
@@ -42,13 +41,8 @@
   vnode->i_nlink_ = 0;
   vnode->i_blocks_ = 0;
 
-  cur_time = time(nullptr);
-  vnode->i_mtime_.tv_sec = cur_time;
-  vnode->i_mtime_.tv_nsec = 0;
-  vnode->i_atime_.tv_sec = cur_time;
-  vnode->i_atime_.tv_nsec = 0;
-  vnode->i_ctime_.tv_sec = cur_time;
-  vnode->i_ctime_.tv_nsec = 0;
+  clock_gettime(CLOCK_REALTIME, &vnode->i_mtime_);
+  vnode->i_atime_ = vnode->i_ctime_ = vnode->i_mtime_;
 
   vnode->i_generation_ = sbi.s_next_generation++;
 
@@ -133,13 +127,11 @@
 
 zx_status_t Dir::Link(std::string_view name, fbl::RefPtr<fs::Vnode> _target) {
   VnodeF2fs *target = static_cast<VnodeF2fs *>(_target.get());
-  auto cur_time = time(nullptr);
 
   fbl::AutoLock lock(&i_mutex_);
   fbl::AutoLock tlock(&target->i_mutex_);
 
-  target->i_ctime_.tv_sec = cur_time;
-  target->i_ctime_.tv_nsec = 0;
+  clock_gettime(CLOCK_REALTIME, &target->i_ctime_);
 
 #if 0  // porting needed
   // AtomicInc(&inode->i_count);
@@ -385,7 +377,9 @@
   DirEntry *old_dir_entry = nullptr;
   DirEntry *old_entry;
   DirEntry *new_entry;
-  auto cur_time = time(nullptr);
+  timespec cur_time;
+
+  clock_gettime(CLOCK_REALTIME, &cur_time);
 
   if (new_dir->i_nlink_ == 0)
     return ZX_ERR_NOT_FOUND;
@@ -482,9 +476,7 @@
 
       new_dir->SetLink(new_entry, new_page, old_vnode);
 
-      cur_time = time(nullptr);
-      new_vnode->i_ctime_.tv_sec = cur_time;
-      new_vnode->i_ctime_.tv_nsec = 0;
+      new_vnode->i_ctime_ = cur_time;
       if (old_dir_entry)
         new_vnode->DropNlink();
       new_vnode->DropNlink();
@@ -518,8 +510,7 @@
       }
     }
 
-    old_vnode->i_ctime_.tv_sec = cur_time;
-    old_vnode->i_ctime_.tv_nsec = 0;
+    old_vnode->i_ctime_ = cur_time;
     SetInodeFlag(&old_vnode->fi_, InodeInfoFlag::kNeedCp);
     MarkInodeDirty(old_vnode);
 
diff --git a/vnode.cc b/vnode.cc
index 90ac2ff..50595b5 100644
--- a/vnode.cc
+++ b/vnode.cc
@@ -157,10 +157,32 @@
   a->mode = i_mode_;
   a->inode = ino_;
   a->content_size = i_size_;
-  a->storage_size = i_blocks_ * kBlockSize;
+  a->storage_size = GetBlocks() * kBlockSize;
   a->link_count = i_nlink_;
-  a->creation_time = inode_.i_ctime;
-  a->modification_time = inode_.i_mtime;
+  a->creation_time = zx_time_add_duration(ZX_SEC(i_ctime_.tv_sec), i_ctime_.tv_nsec);
+  a->modification_time = zx_time_add_duration(ZX_SEC(i_mtime_.tv_sec), i_mtime_.tv_nsec);
+
+  return ZX_OK;
+}
+
+zx_status_t VnodeF2fs::SetAttributes(fs::VnodeAttributesUpdate attr) {
+  bool need_inode_sync = false;
+
+  if (attr.has_creation_time()) {
+    i_ctime_ = zx_timespec_from_duration(attr.take_creation_time());
+    need_inode_sync = true;
+  }
+  if (attr.has_modification_time()) {
+    i_mtime_ = zx_timespec_from_duration(attr.take_modification_time());
+    need_inode_sync = true;
+  }
+  if (attr.any()) {
+    return ZX_ERR_INVALID_ARGS;
+  }
+
+  if (need_inode_sync) {
+    MarkInodeDirty(this);
+  }
 
   return ZX_OK;
 }
@@ -354,11 +376,8 @@
   zx_status_t ret;
 
   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;
+    clock_gettime(CLOCK_REALTIME, &i_mtime_);
+    i_ctime_ = i_mtime_;
     MarkInodeDirty(this);
   }
 
@@ -528,6 +547,8 @@
 
 void VnodeF2fs::SetNlink(uint32_t nlink) { i_nlink_ = nlink; }
 
+uint64_t VnodeF2fs::GetBlocks() { return (i_size_ + kBlockSize - 1) / kBlockSize; }
+
 void MarkInodeDirty(VnodeF2fs *vnode) { vnode->WriteInode(nullptr); }
 
 void VnodeF2fs::Sync(SyncCallback closure) {
diff --git a/vnode.h b/vnode.h
index 4b8d8e8..399657a 100644
--- a/vnode.h
+++ b/vnode.h
@@ -44,6 +44,7 @@
   Inode GetInode() const { return inode_; }
 
   zx_status_t GetAttributes(fs::VnodeAttributes *a) final;
+  zx_status_t SetAttributes(fs::VnodeAttributesUpdate attr) final;
 
   zx_status_t GetNodeInfoForProtocol([[maybe_unused]] fs::VnodeProtocol protocol,
                                      [[maybe_unused]] fs::Rights rights,
@@ -112,6 +113,8 @@
   void ClearNlink();
   void SetNlink(uint32_t nlink);
 
+  uint64_t GetBlocks();
+
   // TODO(unknown): non-public member variables
   fbl::Mutex v_lock_;
   mtx_t i_mutex_;