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