master
c 469 lines 12.5 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 utimensat.c
8
9 Abstract:
10
11 This file contains test routines for the utimensat and utimes system
12 calls.
13
14 --*/
15
16 #include "lxtcommon.h"
17 #include "unittests.h"
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <sys/types.h>
21 #include <sys/time.h>
22
23 #if !defined(__amd64__) && !defined(__aarch64__)
24 #include <sys/capability.h>
25 #endif
26
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <libmount/libmount.h>
32 #include "lxtfs.h"
33 #include "lxtmount.h"
34
35 #define LXT_NAME "Utimensat"
36
37 LXT_VARIATION_HANDLER TestBasicFunctions;
38 LXT_VARIATION_HANDLER TestUtimes;
39 LXT_VARIATION_HANDLER TestInvalid;
40 LXT_VARIATION_HANDLER TestPermissions;
41
42 //
43 // Global constants
44 //
45
46 const char ChildFileName[] = "testfile";
47 const char ChildFileFullPath[] = "/data/test_utimensat/testfile";
48 const char DirPath[] = "/data/test_utimensat";
49 const char LinkFileName[] = "testlink";
50 const char LinkFullPath[] = "/data/test_utimensat/testlink";
51
52 static const LXT_VARIATION g_LxtVariations[] = {
53 {"Test basic functionality", TestBasicFunctions},
54 {"Test utimes", TestUtimes},
55 {"Test invalid parameters", TestInvalid},
56 {"Test permissions", TestPermissions},
57 };
58
59 typedef struct timespec TIMESPEC, *PTIMESPEC;
60
61 enum
62 {
63 PermissionsRoot,
64 PermissionsOmit,
65 PermissionsNow,
66 PermissionsSet,
67 PermissionsNowOmit,
68 PermissionsOmitNow,
69 PermissionsBeyondMax
70 };
71
72 typedef struct _PERMISSIONS_TEST_CASE
73 {
74 TIMESPEC SetTime[2];
75 } PERMISSIONS_TEST_CASE, *PPERMISSIONS_TEST_CASE;
76
77 PERMISSIONS_TEST_CASE PermissionsTest[] = {
78 {{{1234567, 98765432}, {4444444, 5555555}}},
79 {{{1234567, UTIME_OMIT}, {4444444, UTIME_OMIT}}},
80 {{{1234567, UTIME_NOW}, {4444444, UTIME_NOW}}},
81 {{{1234567, 11111111}, {4444444, 22222222}}},
82 {{{1234567, UTIME_NOW}, {4444444, UTIME_OMIT}}},
83 {{{1234567, UTIME_OMIT}, {4444444, UTIME_NOW}}}};
84
85 typedef struct _PERMISSIONS_FILE
86 {
87 const char* Filename;
88 uid_t Owner;
89 mode_t Mode;
90 int Changeable[PermissionsBeyondMax];
91 } PERMISSIONS_FILE, *PPERMISSIONS_FILE;
92
93 enum
94 {
95 OwnerMatchingThread = 1000,
96 OwnerNotMatchingThread
97 };
98
99 PERMISSIONS_FILE PermissionsFiles[] = {
100 {"OwnedAndWritable", OwnerMatchingThread, 0666, {0, 0, 0, 0, 0, 0}},
101 {"OwnedReadonly", OwnerMatchingThread, 0444, {0, 0, 0, 0, 0, 0}},
102 {"OwnedWriteonly", OwnerMatchingThread, 0222, {0, 0, 0, 0, 0, 0}},
103 {"UnownedAndWritable", OwnerNotMatchingThread, 0666, {0, 0, 0, EPERM, EPERM, EPERM}},
104 {"UnownedAndReadonly", OwnerNotMatchingThread, 0444, {0, 0, EACCES, EPERM, EPERM, EPERM}},
105 {"UnownedAndWriteonly", OwnerNotMatchingThread, 0222, {0, 0, 0, EPERM, EPERM, EPERM}}};
106
107 int TestBasicFunctions(PLXT_ARGS Args)
108
109 /*++
110
111 Routine Description:
112
113 This routine executes basic test functions, including setting timestamps
114 to a range of values including UTIME_NOW or UTIME_OMIT, on a range of
115 different ways to specify the target file, including relative, absolute,
116 descriptor, via symbolic link, on a symbolic link, and validating that
117 the expected outcome occurs.
118
119 Arguments:
120
121 Args - Supplies a pointer to test arguments.
122
123 Return Value:
124
125 0 if all variations complete successfully, -1 if they do not.
126
127 --*/
128
129 {
130
131 int Flags;
132 int Result;
133
134 //
135 // If running on wslfs, timestamps use NT precision.
136 //
137
138 Flags = 0;
139 if (g_LxtFsInfo.Flags.DrvFsBehavior != 0)
140 {
141 LxtLogInfo("Using NT precision timestamps.");
142 Flags = FS_UTIME_NT_PRECISION;
143 }
144
145 Result = LxtFsUtimeBasicCommon(DirPath, Flags);
146
147 ErrorExit:
148 return Result;
149 }
150
151 int TestUtimes(PLXT_ARGS Args)
152
153 /*++
154
155 Routine Description:
156
157 This routine executes test functions specific to the utimes syscall.
158
159 Arguments:
160
161 Args - Supplies a pointer to test arguments.
162
163 Return Value:
164
165 0 if all variations complete successfully, -1 if they do not.
166
167 --*/
168
169 {
170
171 int Fd;
172 int Result;
173 struct timeval SetTimeVal[2];
174
175 LxtCheckErrno(Fd = creat(ChildFileName, 0777));
176 memset(SetTimeVal, 0, sizeof(SetTimeVal));
177 LxtCheckResult(utimes(ChildFileName, SetTimeVal));
178 LxtCheckResult(utimes(ChildFileName, NULL));
179
180 //
181 // Invalid parameter variations.
182 //
183
184 memset(SetTimeVal, 0, sizeof(SetTimeVal));
185 SetTimeVal[1].tv_usec = 999999 + 1;
186 LxtCheckErrnoFailure(utimes(ChildFileName, SetTimeVal), EINVAL);
187 memset(SetTimeVal, 0, sizeof(SetTimeVal));
188 SetTimeVal[1].tv_usec = 999999 + 1;
189 LxtCheckErrnoFailure(utimes(ChildFileName, SetTimeVal), EINVAL);
190
191 ErrorExit:
192 if (Fd != -1)
193 {
194 LxtClose(Fd);
195 }
196
197 return Result;
198 }
199
200 int TestInvalid(PLXT_ARGS Args)
201
202 /*++
203
204 Routine Description:
205
206 This routine executes test functions which are expected to fail, and
207 validates that they fail with the correct error. This includes invalid
208 descriptors, flags, nonexistent files, or invalid timestamps.
209
210 Arguments:
211
212 Args - Supplies a pointer to test arguments.
213
214 Return Value:
215
216 0 if all variations complete successfully, -1 if they do not.
217
218 --*/
219
220 {
221
222 int Fd;
223 int Result;
224 TIMESPEC SetTime[2];
225 struct timeval SetTimeVal[2];
226
227 Fd = -1;
228 memset(SetTime, 0, sizeof(SetTime));
229 memset(SetTimeVal, 0, sizeof(SetTimeVal));
230
231 LxtCheckErrnoFailure(utimensat(12345, ChildFileName, SetTime, 0), EBADF);
232
233 LxtCheckErrnoFailure(utimensat(0, ChildFileFullPath, SetTime, 0x70000000), EINVAL);
234
235 LxtCheckErrnoFailure(utimensat(AT_FDCWD, "bogus", SetTime, 0), ENOENT);
236
237 LxtCheckResult(Fd = open(ChildFileFullPath, O_RDWR));
238
239 LxtCheckErrnoFailure(utimensat(Fd, "bogus", SetTime, 0), ENOTDIR);
240 #pragma GCC diagnostic push
241 #pragma GCC diagnostic ignored "-Wnonnull"
242
243 LxtCheckErrnoFailure(utimensat(Fd, NULL, SetTime, AT_SYMLINK_NOFOLLOW), EINVAL);
244
245 #pragma GCC diagnostic pop
246
247 LxtClose(Fd);
248 Fd = -1;
249
250 SetTime[0].tv_nsec = 1000000000;
251 LxtCheckErrnoFailure(utimensat(0, ChildFileFullPath, SetTime, 0), EINVAL);
252
253 SetTimeVal[0].tv_usec = 1000000;
254 LxtCheckErrnoFailure(utimes(ChildFileFullPath, SetTimeVal), EINVAL);
255
256 SetTimeVal[0].tv_usec = UTIME_NOW;
257 LxtCheckErrnoFailure(utimes(ChildFileFullPath, SetTimeVal), EINVAL);
258
259 SetTimeVal[0].tv_usec = UTIME_OMIT;
260 LxtCheckErrnoFailure(utimes(ChildFileFullPath, SetTimeVal), EINVAL);
261
262 SetTimeVal[0].tv_usec = 0;
263 LxtCheckErrnoFailure(utimes("bogus", SetTimeVal), ENOENT);
264 Result = 0;
265
266 ErrorExit:
267 if (Fd != -1)
268 {
269 LxtClose(Fd);
270 }
271
272 return Result;
273 }
274
275 int TestPermissions(PLXT_ARGS Args)
276
277 /*++
278
279 Routine Description:
280
281 This routine executes test functions that should succeed or fail
282 depending on inputs and user states, and validates that they fail with
283 the expected code. In particular, validates that callers without
284 privilege or file ownership cannot set file timestamps, and those
285 without privilege, file ownership or write access cannot set timestamps
286 to current.
287
288 Arguments:
289
290 Args - Supplies a pointer to test arguments.
291
292 Return Value:
293
294 0 if all variations complete successfully, -1 if they do not.
295
296 --*/
297
298 {
299
300 pid_t ChildPid;
301 int Fd;
302 unsigned int FileNumber;
303 int Result;
304 unsigned int TestCase;
305
306 Result = chdir(DirPath);
307 if (Result < 0)
308 {
309 LxtLogError("Could not change directory: %d", Result);
310 Result = -1;
311 goto ErrorExit;
312 }
313
314 for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)
315 {
316 Fd = open(PermissionsFiles[FileNumber].Filename, O_CREAT | O_RDWR, PermissionsFiles[FileNumber].Mode);
317 if ((Fd < 0) && (errno != EEXIST))
318 {
319 LxtLogError("Could not create file: %d", Result);
320 Result = -1;
321 goto ErrorExit;
322 }
323
324 Result = fchown(Fd, PermissionsFiles[FileNumber].Owner, PermissionsFiles[FileNumber].Owner);
325 if (Result < 0)
326 {
327 LxtLogError("Could not change owner: %d", Result);
328 Result = -1;
329 goto ErrorExit;
330 }
331
332 Result = fchmod(Fd, PermissionsFiles[FileNumber].Mode);
333 if (Result < 0)
334 {
335 LxtLogError("Could not change mode: %d", Result);
336 Result = -1;
337 goto ErrorExit;
338 }
339
340 close(Fd);
341 Fd = 0;
342 }
343
344 //
345 // While still root, check that we can perform all of the things it should
346 // be able to do.
347 //
348
349 TestCase = PermissionsRoot;
350
351 for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)
352 {
353 Result = utimensat(AT_FDCWD, PermissionsFiles[FileNumber].Filename, PermissionsTest[TestCase].SetTime, 0);
354 if (PermissionsFiles[FileNumber].Changeable[TestCase] == 0)
355 {
356 if (Result < 0)
357 {
358 LxtLogError("Could not change time as expected, file %s case %i, Result %d", PermissionsFiles[FileNumber].Filename, TestCase, Result);
359 Result = -1;
360 goto ErrorExit;
361 }
362 }
363 else
364 {
365 if ((Result == 0) || (errno != PermissionsFiles[FileNumber].Changeable[TestCase]))
366 {
367
368 LxtLogError(
369 "Could change time, or failed with the wrong code, file %s case %i, Result %d, errno %i, expected %i",
370 PermissionsFiles[FileNumber].Filename,
371 TestCase,
372 Result,
373 errno,
374 PermissionsFiles[FileNumber].Changeable[TestCase]);
375 Result = -1;
376 goto ErrorExit;
377 }
378 }
379 }
380
381 //
382 // Set to user that should owns some of the files, and lacks privilege
383 // to access others
384 //
385
386 LxtCheckErrno(ChildPid = fork());
387 if (ChildPid == 0)
388 {
389 Result = setuid(OwnerMatchingThread);
390 if (Result < 0)
391 {
392 LxtLogError("Could not setuid: %d", Result);
393 Result = -1;
394 goto ErrorExit;
395 }
396
397 //
398 // Advance to the next test (the first one was performed above), and continue
399 //
400
401 for (TestCase += 1; TestCase < PermissionsBeyondMax; TestCase += 1)
402 {
403 for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)
404 {
405 Result = utimensat(AT_FDCWD, PermissionsFiles[FileNumber].Filename, PermissionsTest[TestCase].SetTime, 0);
406 if (PermissionsFiles[FileNumber].Changeable[TestCase] == 0)
407 {
408 if (Result < 0)
409 {
410 LxtLogError("Could not change time as expected, file %s case %i, Result %d", PermissionsFiles[FileNumber].Filename, TestCase, Result);
411 Result = -1;
412 goto ErrorExit;
413 }
414 }
415 else
416 {
417 if ((Result == 0) || (errno != PermissionsFiles[FileNumber].Changeable[TestCase]))
418 {
419
420 LxtLogError(
421 "Could change time, or failed with the wrong code, file %s case %i, Result %d, errno %i, expected %i",
422 PermissionsFiles[FileNumber].Filename,
423 TestCase,
424 Result,
425 errno,
426 PermissionsFiles[FileNumber].Changeable[TestCase]);
427 Result = -1;
428 goto ErrorExit;
429 }
430 }
431 }
432 }
433
434 exit(0);
435 }
436
437 LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
438 Result = 0;
439
440 ErrorExit:
441 for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)
442 {
443 unlink(PermissionsFiles[FileNumber].Filename);
444 }
445
446 return Result;
447 }
448
449 int UtimensatTestEntry(int Argc, char* Argv[])
450 {
451 LXT_ARGS Args;
452 int Fd;
453 int Result;
454
455 Fd = -1;
456 LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
457 LxtCheckResult(LxtFsUtimeCreateTestFiles(DirPath, 0));
458 LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
459
460 ErrorExit:
461 if (Fd >= 0)
462 {
463 close(Fd);
464 }
465
466 LxtFsUtimeCleanupTestFiles(DirPath);
467 LxtUninitialize();
468 return !LXT_SUCCESS(Result);
469 }