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 <limits.h>
7#include <stdint.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <sys/stat.h>
11#include <unistd.h>
12
13#include <zircon/syscalls.h>
14#include <unittest/unittest.h>
15
16#include "filesystems.h"
17
18bool test_fcntl_append(void) {
19    BEGIN_TEST;
20
21    int fd = open("::file", O_APPEND | O_RDWR | O_CREAT, 0644);
22    ASSERT_GT(fd, 0);
23
24    // Do a quick check that O_APPEND is appending
25    char buf[5];
26    memset(buf, 'a', sizeof(buf));
27    ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
28    ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf));
29    ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
30    ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf));
31    struct stat sb;
32    ASSERT_EQ(fstat(fd, &sb), 0);
33    ASSERT_EQ(sb.st_size, sizeof(buf) * 2);
34
35    // Use F_GETFL; observe O_APPEND
36    int flags = fcntl(fd, F_GETFL);
37    ASSERT_GT(flags, -1, "Fcntl failed");
38    ASSERT_EQ(flags & O_ACCMODE, O_RDWR, "Access mode flags did not match");
39    ASSERT_EQ(flags & ~O_ACCMODE, O_APPEND, "Status flags did not match");
40
41    // Use F_SETFL; turn off O_APPEND
42    ASSERT_EQ(fcntl(fd, F_SETFL, flags & ~O_APPEND), 0, "Fcntl failed");
43
44    // Use F_GETFL; observe O_APPEND has been turned off
45    flags = fcntl(fd, F_GETFL);
46    ASSERT_GT(flags, -1, "Fcntl failed");
47    ASSERT_EQ(flags & O_ACCMODE, O_RDWR, "Access mode flags did not match");
48    ASSERT_EQ(flags & ~O_ACCMODE, 0, "Status flags did not match");
49
50    // Write to the file, verify it is no longer appending.
51    ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
52    ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf));
53    ASSERT_EQ(fstat(fd, &sb), 0);
54    ASSERT_EQ(sb.st_size, sizeof(buf) * 2);
55
56    // Clean up
57    ASSERT_EQ(close(fd), 0);
58    ASSERT_EQ(unlink("::file"), 0);
59    END_TEST;
60}
61
62bool test_fcntl_access_bits(void) {
63    BEGIN_TEST;
64
65    int fd = open("::file", O_APPEND | O_RDWR | O_CREAT, 0644);
66    ASSERT_GT(fd, 0);
67
68    // Do a quick check that we can write
69    char buf[5];
70    memset(buf, 'a', sizeof(buf));
71    ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
72    ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf));
73    struct stat sb;
74    ASSERT_EQ(fstat(fd, &sb), 0);
75    ASSERT_EQ(sb.st_size, sizeof(buf));
76
77    // Use F_GETFL; observe O_APPEND
78    int flags = fcntl(fd, F_GETFL);
79    ASSERT_GT(flags, -1, "Fcntl failed");
80    ASSERT_EQ(flags & O_ACCMODE, O_RDWR, "Access mode flags did not match");
81    ASSERT_EQ(flags & ~O_ACCMODE, O_APPEND, "Status flags did not match");
82
83    // Use F_SETFL; try to turn off everything except O_APPEND
84    // (if fcntl paid attention to access bits, this would make the file
85    // read-only).
86    ASSERT_EQ(fcntl(fd, F_SETFL, O_APPEND), 0, "Fcntl failed");
87
88    // We're still appending -- AND writable, because the access bits haven't
89    // changed.
90    ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
91    ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf));
92    ASSERT_EQ(fstat(fd, &sb), 0);
93    ASSERT_EQ(sb.st_size, sizeof(buf) * 2);
94
95    // Clean up
96    ASSERT_EQ(close(fd), 0);
97    ASSERT_EQ(unlink("::file"), 0);
98    END_TEST;
99}
100
101RUN_FOR_ALL_FILESYSTEMS(fcntl_tests,
102    RUN_TEST_MEDIUM(test_fcntl_append)
103    RUN_TEST_MEDIUM(test_fcntl_access_bits)
104)
105