1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr_file_io.h"
18#include "apr_file_info.h"
19#include "apr_strings.h"
20#include "apr_errno.h"
21#include "apr_general.h"
22#include "apr_poll.h"
23#include "apr_lib.h"
24#include "testutil.h"
25
26#define FILENAME "data/file_datafile.txt"
27#define NEWFILENAME "data/new_datafile.txt"
28#define NEWFILEDATA "This is new text in a new file."
29
30static const struct view_fileinfo
31{
32    apr_int32_t bits;
33    char *description;
34} vfi[] = {
35    {APR_FINFO_MTIME,  "MTIME"},
36    {APR_FINFO_CTIME,  "CTIME"},
37    {APR_FINFO_ATIME,  "ATIME"},
38    {APR_FINFO_SIZE,   "SIZE"},
39    {APR_FINFO_DEV,    "DEV"},
40    {APR_FINFO_INODE,  "INODE"},
41    {APR_FINFO_NLINK,  "NLINK"},
42    {APR_FINFO_TYPE,   "TYPE"},
43    {APR_FINFO_USER,   "USER"},
44    {APR_FINFO_GROUP,  "GROUP"},
45    {APR_FINFO_UPROT,  "UPROT"},
46    {APR_FINFO_GPROT,  "GPROT"},
47    {APR_FINFO_WPROT,  "WPROT"},
48    {0,                NULL}
49};
50
51static void finfo_equal(abts_case *tc, apr_finfo_t *f1, apr_finfo_t *f2)
52{
53    /* Minimum supported flags across all platforms (APR_FINFO_MIN) */
54    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_TYPE",
55             (f1->valid & f2->valid & APR_FINFO_TYPE));
56    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in filetype",
57             f1->filetype == f2->filetype);
58    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_SIZE",
59             (f1->valid & f2->valid & APR_FINFO_SIZE));
60    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in size",
61             f1->size == f2->size);
62    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_ATIME",
63             (f1->valid & f2->valid & APR_FINFO_ATIME));
64    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in atime",
65             f1->atime == f2->atime);
66    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_MTIME",
67             (f1->valid & f2->valid & APR_FINFO_MTIME));
68    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in mtime",
69             f1->mtime == f2->mtime);
70    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_CTIME",
71             (f1->valid & f2->valid & APR_FINFO_CTIME));
72    ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in ctime",
73             f1->ctime == f2->ctime);
74
75    if (f1->valid & f2->valid & APR_FINFO_NAME)
76        ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in name",
77                 !strcmp(f1->name, f2->name));
78    if (f1->fname && f2->fname)
79        ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in fname",
80                 !strcmp(f1->fname, f2->fname));
81
82    /* Additional supported flags not supported on all platforms */
83    if (f1->valid & f2->valid & APR_FINFO_USER)
84        ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in user",
85                 !apr_uid_compare(f1->user, f2->user));
86    if (f1->valid & f2->valid & APR_FINFO_GROUP)
87        ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in group",
88                 !apr_gid_compare(f1->group, f2->group));
89    if (f1->valid & f2->valid & APR_FINFO_INODE)
90        ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in inode",
91                 f1->inode == f2->inode);
92    if (f1->valid & f2->valid & APR_FINFO_DEV)
93        ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in device",
94                 f1->device == f2->device);
95    if (f1->valid & f2->valid & APR_FINFO_NLINK)
96        ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in nlink",
97                 f1->nlink == f2->nlink);
98    if (f1->valid & f2->valid & APR_FINFO_CSIZE)
99        ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in csize",
100                 f1->csize == f2->csize);
101    if (f1->valid & f2->valid & APR_FINFO_PROT)
102        ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in protection",
103                 f1->protection == f2->protection);
104}
105
106static void test_info_get(abts_case *tc, void *data)
107{
108    apr_file_t *thefile;
109    apr_finfo_t finfo;
110    apr_status_t rv;
111
112    rv = apr_file_open(&thefile, FILENAME, APR_FOPEN_READ, APR_OS_DEFAULT, p);
113    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
114
115    rv = apr_file_info_get(&finfo, APR_FINFO_NORM, thefile);
116    if (APR_STATUS_IS_INCOMPLETE(rv)) {
117        char *str;
118	int i;
119        str = apr_pstrdup(p, "APR_INCOMPLETE:  Missing ");
120        for (i = 0; vfi[i].bits; ++i) {
121            if (vfi[i].bits & ~finfo.valid) {
122                str = apr_pstrcat(p, str, vfi[i].description, " ", NULL);
123            }
124        }
125        ABTS_FAIL(tc, str);
126    }
127    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
128    apr_file_close(thefile);
129}
130
131static void test_stat(abts_case *tc, void *data)
132{
133    apr_finfo_t finfo;
134    apr_status_t rv;
135
136    rv = apr_stat(&finfo, FILENAME, APR_FINFO_NORM, p);
137    if (APR_STATUS_IS_INCOMPLETE(rv)) {
138        char *str;
139	int i;
140        str = apr_pstrdup(p, "APR_INCOMPLETE:  Missing ");
141        for (i = 0; vfi[i].bits; ++i) {
142            if (vfi[i].bits & ~finfo.valid) {
143                str = apr_pstrcat(p, str, vfi[i].description, " ", NULL);
144            }
145        }
146        ABTS_FAIL(tc, str);
147    }
148    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
149}
150
151static void test_stat_eq_finfo(abts_case *tc, void *data)
152{
153    apr_file_t *thefile;
154    apr_finfo_t finfo;
155    apr_finfo_t stat_finfo;
156    apr_status_t rv;
157
158    rv = apr_file_open(&thefile, FILENAME, APR_FOPEN_READ, APR_OS_DEFAULT, p);
159    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
160    rv = apr_file_info_get(&finfo, APR_FINFO_NORM, thefile);
161
162    /* Opening the file may have toggled the atime member (time last
163     * accessed), so fetch our apr_stat() after getting the fileinfo
164     * of the open file...
165     */
166    rv = apr_stat(&stat_finfo, FILENAME, APR_FINFO_NORM, p);
167    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
168
169    apr_file_close(thefile);
170
171    finfo_equal(tc, &stat_finfo, &finfo);
172}
173
174static void test_buffered_write_size(abts_case *tc, void *data)
175{
176    const apr_size_t data_len = strlen(NEWFILEDATA);
177    apr_file_t *thefile;
178    apr_finfo_t finfo;
179    apr_status_t rv;
180    apr_size_t bytes;
181
182    rv = apr_file_open(&thefile, NEWFILENAME,
183                       APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE
184                       | APR_FOPEN_BUFFERED | APR_FOPEN_DELONCLOSE,
185                       APR_OS_DEFAULT, p);
186    APR_ASSERT_SUCCESS(tc, "open file", rv);
187
188    /* A funny thing happened to me the other day: I wrote something
189     * into a buffered file, then asked for its size using
190     * apr_file_info_get; and guess what? The size was 0! That's not a
191     * nice way to behave.
192     */
193    bytes = data_len;
194    rv = apr_file_write(thefile, NEWFILEDATA, &bytes);
195    APR_ASSERT_SUCCESS(tc, "write file contents", rv);
196    ABTS_TRUE(tc, data_len == bytes);
197
198    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, thefile);
199    APR_ASSERT_SUCCESS(tc, "get file size", rv);
200    ABTS_TRUE(tc, bytes == (apr_size_t) finfo.size);
201    apr_file_close(thefile);
202}
203
204static void test_mtime_set(abts_case *tc, void *data)
205{
206    apr_file_t *thefile;
207    apr_finfo_t finfo;
208    apr_time_t epoch = 0;
209    apr_status_t rv;
210
211    /* This test sort of depends on the system clock being at least
212     * marginally ccorrect; We'll be setting the modification time to
213     * the epoch.
214     */
215    rv = apr_file_open(&thefile, NEWFILENAME,
216                       APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE
217                       | APR_FOPEN_BUFFERED | APR_FOPEN_DELONCLOSE,
218                       APR_OS_DEFAULT, p);
219    APR_ASSERT_SUCCESS(tc, "open file", rv);
220
221    /* Check that the current mtime is not the epoch */
222    rv = apr_stat(&finfo, NEWFILENAME, APR_FINFO_MTIME, p);
223    if (APR_STATUS_IS_INCOMPLETE(rv)) {
224        char *str;
225	int i;
226        str = apr_pstrdup(p, "APR_INCOMPLETE:  Missing ");
227        for (i = 0; vfi[i].bits; ++i) {
228            if (vfi[i].bits & ~finfo.valid) {
229                str = apr_pstrcat(p, str, vfi[i].description, " ", NULL);
230            }
231        }
232        ABTS_FAIL(tc, str);
233    }
234    APR_ASSERT_SUCCESS(tc, "get initial mtime", rv);
235    ABTS_TRUE(tc, finfo.mtime != epoch);
236
237    /* Reset the mtime to the epoch and verify the result.
238     * Note: we blindly assume that if the first apr_stat succeeded,
239     * the second one will, too.
240     */
241    rv = apr_file_mtime_set(NEWFILENAME, epoch, p);
242    APR_ASSERT_SUCCESS(tc, "set mtime", rv);
243
244    rv = apr_stat(&finfo, NEWFILENAME, APR_FINFO_MTIME, p);
245    APR_ASSERT_SUCCESS(tc, "get modified mtime", rv);
246    ABTS_TRUE(tc, finfo.mtime == epoch);
247
248    apr_file_close(thefile);
249}
250
251abts_suite *testfileinfo(abts_suite *suite)
252{
253    suite = ADD_SUITE(suite)
254
255    abts_run_test(suite, test_info_get, NULL);
256    abts_run_test(suite, test_stat, NULL);
257    abts_run_test(suite, test_stat_eq_finfo, NULL);
258    abts_run_test(suite, test_buffered_write_size, NULL);
259    abts_run_test(suite, test_mtime_set, NULL);
260
261    return suite;
262}
263
264