1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <fcntl.h> 6#include <math.h> 7#include <stdint.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <unistd.h> 11 12#include <sys/stat.h> 13#include <sys/time.h> 14 15#include <lib/fdio/vfs.h> 16#include <zircon/syscalls.h> 17#include <zircon/time.h> 18#include <zircon/types.h> 19 20#include "filesystems.h" 21 22#define ROUND_DOWN(t, granularity) ((t) - ((t) % (granularity))) 23 24zx_time_t nstimespec(struct timespec ts) { 25 // assumes very small number of seconds in deltas 26 return zx_time_add_duration(ZX_SEC(ts.tv_sec), ts.tv_nsec); 27} 28 29bool test_attr(void) { 30 BEGIN_TEST; 31 zx_time_t now = zx_clock_get(ZX_CLOCK_UTC); 32 ASSERT_NE(now, 0u, "zx_clock_get only returns zero on error"); 33 34 int fd1 = open("::file.txt", O_CREAT | O_RDWR, 0644); 35 ASSERT_GT(fd1, 0, ""); 36 37 struct timespec ts[2]; 38 ts[0].tv_nsec = UTIME_OMIT; 39 ts[1].tv_sec = (long)(now / ZX_SEC(1)); 40 ts[1].tv_nsec = (long)(now % ZX_SEC(1)); 41 42 // make sure we get back "now" from stat() 43 ASSERT_EQ(futimens(fd1, ts), 0, ""); 44 struct stat statb1; 45 ASSERT_EQ(fstat(fd1, &statb1), 0, ""); 46 now = ROUND_DOWN(now, test_info->nsec_granularity); 47 ASSERT_EQ(statb1.st_mtim.tv_sec, (long)(now / ZX_SEC(1)), ""); 48 ASSERT_EQ(statb1.st_mtim.tv_nsec, (long)(now % ZX_SEC(1)), ""); 49 ASSERT_EQ(close(fd1), 0, ""); 50 51 zx_nanosleep(zx_deadline_after(test_info->nsec_granularity)); 52 53 ASSERT_EQ(utimes("::file.txt", NULL), 0, ""); 54 struct stat statb2; 55 ASSERT_EQ(stat("::file.txt", &statb2), 0, ""); 56 ASSERT_GT(nstimespec(statb2.st_mtim), nstimespec(statb1.st_mtim), ""); 57 58 ASSERT_EQ(unlink("::file.txt"), 0, ""); 59 60 END_TEST; 61} 62 63bool test_blksize(void) { 64 BEGIN_TEST; 65 66 int fd = open("::file.txt", O_CREAT | O_RDWR, 0644); 67 ASSERT_GT(fd, 0, ""); 68 69 struct stat buf; 70 ASSERT_EQ(fstat(fd, &buf), 0, ""); 71 ASSERT_GT(buf.st_blksize, 0, "blksize should be greater than zero"); 72 ASSERT_EQ(buf.st_blksize % VNATTR_BLKSIZE, 0, "blksize should be a multiple of VNATTR_BLKSIZE"); 73 ASSERT_EQ(buf.st_blocks, 0, "Number of allocated blocks should be zero"); 74 75 char data = {'a'}; 76 ASSERT_EQ(write(fd, &data, 1), 1, "Couldn't write a single byte to file"); 77 ASSERT_EQ(fstat(fd, &buf), 0, ""); 78 ASSERT_GT(buf.st_blksize, 0, "blksize should be greater than zero"); 79 ASSERT_EQ(buf.st_blksize % VNATTR_BLKSIZE, 0, "blksize should be a multiple of VNATTR_BLKSIZE"); 80 ASSERT_GT(buf.st_blocks, 0, "Number of allocated blocks should greater than zero"); 81 ASSERT_EQ(close(fd), 0, ""); 82 83 blkcnt_t nblocks = buf.st_blocks; 84 ASSERT_EQ(stat("::file.txt", &buf), 0, ""); 85 ASSERT_EQ(buf.st_blocks, nblocks, "Block count changed when closing file"); 86 87 ASSERT_EQ(unlink("::file.txt"), 0, ""); 88 89 END_TEST; 90} 91 92bool test_parent_directory_time(void) { 93 BEGIN_TEST; 94 95 if (strcmp(test_info->name, "FAT") == 0) { 96 // FAT does not update parent directory times when children 97 // are updated 98 printf("FAT parent directory timestamps aren't updated; skipping test...\n"); 99 return true; 100 } 101 102 zx_time_t now = zx_clock_get(ZX_CLOCK_UTC); 103 ASSERT_NE(now, 0u, "zx_clock_get only returns zero on error"); 104 105 // Create a parent directory to contain new contents 106 zx_nanosleep(zx_deadline_after(test_info->nsec_granularity)); 107 ASSERT_EQ(mkdir("::parent", 0666), 0, ""); 108 ASSERT_EQ(mkdir("::parent2", 0666), 0, ""); 109 110 // Ensure the parent directory's create + modified times 111 // were initialized correctly. 112 struct stat statb; 113 ASSERT_EQ(stat("::parent", &statb), 0, ""); 114 ASSERT_GT(nstimespec(statb.st_ctim), now, ""); 115 ASSERT_GT(nstimespec(statb.st_mtim), now, ""); 116 now = nstimespec(statb.st_ctim); 117 118 // Create a file in the parent directory 119 zx_nanosleep(zx_deadline_after(test_info->nsec_granularity)); 120 int fd = open("::parent/child", O_CREAT | O_RDWR); 121 ASSERT_GT(fd, 0, ""); 122 ASSERT_EQ(close(fd), 0, ""); 123 124 // Time moved forward in both the child... 125 ASSERT_EQ(stat("::parent/child", &statb), 0, ""); 126 ASSERT_GT(nstimespec(statb.st_mtim), now, ""); 127 // ... and the parent 128 ASSERT_EQ(stat("::parent", &statb), 0, ""); 129 ASSERT_GT(nstimespec(statb.st_mtim), now, ""); 130 now = nstimespec(statb.st_mtim); 131 132 // Link the child into a second directory 133 zx_nanosleep(zx_deadline_after(test_info->nsec_granularity)); 134 ASSERT_EQ(link("::parent/child", "::parent2/child"), 0, ""); 135 // Source directory is not impacted 136 ASSERT_EQ(stat("::parent", &statb), 0, ""); 137 ASSERT_EQ(nstimespec(statb.st_mtim), now, ""); 138 // Target directory is updated 139 ASSERT_EQ(stat("::parent2", &statb), 0, ""); 140 ASSERT_GT(nstimespec(statb.st_mtim), now, ""); 141 now = nstimespec(statb.st_mtim); 142 143 // Unlink the child, and the parent's time should 144 // move forward again 145 zx_nanosleep(zx_deadline_after(test_info->nsec_granularity)); 146 ASSERT_EQ(unlink("::parent2/child"), 0, ""); 147 ASSERT_EQ(stat("::parent2", &statb), 0, ""); 148 ASSERT_GT(nstimespec(statb.st_mtim), now, ""); 149 now = nstimespec(statb.st_mtim); 150 151 // Rename the child, and both the source and dest 152 // directories should be updated 153 zx_nanosleep(zx_deadline_after(test_info->nsec_granularity)); 154 ASSERT_EQ(rename("::parent/child", "::parent2/child"), 0, ""); 155 ASSERT_EQ(stat("::parent", &statb), 0, ""); 156 ASSERT_GT(nstimespec(statb.st_mtim), now, ""); 157 ASSERT_EQ(stat("::parent2", &statb), 0, ""); 158 ASSERT_GT(nstimespec(statb.st_mtim), now, ""); 159 160 // Clean up 161 ASSERT_EQ(unlink("::parent2/child"), 0, ""); 162 ASSERT_EQ(rmdir("::parent2"), 0, ""); 163 ASSERT_EQ(rmdir("::parent"), 0, ""); 164 165 END_TEST; 166} 167 168RUN_FOR_ALL_FILESYSTEMS(attr_tests, 169 RUN_TEST_MEDIUM(test_attr) 170 RUN_TEST_MEDIUM(test_blksize) 171 RUN_TEST_MEDIUM(test_parent_directory_time) 172) 173