1 /* Tests for truncate(2) call family - by D.C. van Moolenbroek */
2 #define _POSIX_SOURCE 1
11 #define TESTFILE "testfile"
13 #define THRESHOLD 1048576
16 #define MIN(x,y) (((x)<(y))?(x):(y))
21 _PROTOTYPE(int main
, (int argc
, char *argv
[]));
22 _PROTOTYPE(void prepare
, (void));
23 _PROTOTYPE(int make_file
, (off_t size
));
24 _PROTOTYPE(void check_file
, (int fd
, off_t size
, off_t hole_start
,
26 _PROTOTYPE(void all_sizes
,
27 (_PROTOTYPE(void (*call
), (off_t osize
, off_t nsize
))));
28 _PROTOTYPE(void test50a
, (void));
29 _PROTOTYPE(void test50b
, (void));
30 _PROTOTYPE(void test50c
, (void));
31 _PROTOTYPE(void test50d
, (void));
32 _PROTOTYPE(void sub50e
, (off_t osize
, off_t nsize
));
33 _PROTOTYPE(void test50e
, (void));
34 _PROTOTYPE(void sub50f
, (off_t osize
, off_t nsize
));
35 _PROTOTYPE(void test50f
, (void));
36 _PROTOTYPE(void sub50g
, (off_t osize
, off_t nsize
));
37 _PROTOTYPE(void test50g
, (void));
38 _PROTOTYPE(void sub50h
, (off_t osize
, off_t nsize
));
39 _PROTOTYPE(void test50h
, (void));
40 _PROTOTYPE(void sub50i
, (off_t size
, off_t off
, size_t len
, int type
));
41 _PROTOTYPE(void test50i
, (void));
43 /* Some of the sizes have been chosen in such a way that they should be on the
44 * edge of direct/single indirect/double indirect switchovers for a MINIX
45 * file system with 4K block size.
47 static off_t sizes
[] = {
48 0L, 1L, 511L, 512L, 513L, 1023L, 1024L, 1025L, 2047L, 2048L, 2049L, 3071L,
49 3072L, 3073L, 4095L, 4096L, 4097L, 16383L, 16384L, 16385L, 28671L, 28672L,
50 28673L, 65535L, 65536L, 65537L, 4222975L, 4222976L, 4222977L
53 static unsigned char *data
;
63 if (argc
== 2) m
= atoi(argv
[1]);
64 for (j
= 0; j
< ITERATIONS
; j
++) {
65 if (m
& 00001) test50a();
66 if (m
& 00002) test50b();
67 if (m
& 00004) test50c();
68 if (m
& 00010) test50d();
69 if (m
& 00020) test50e();
70 if (m
& 00040) test50f();
71 if (m
& 00100) test50g();
72 if (m
& 00200) test50h();
73 if (m
& 00400) test50i();
77 return(-1); /* impossible */
86 for (i
= 0; i
< sizeof(sizes
) / sizeof(sizes
[0]); i
++)
87 if (largest
< sizes
[i
]) largest
= sizes
[i
];
89 /* internal integrity check: this is needed for early tests */
90 assert(largest
>= TESTSIZE
);
92 data
= malloc(largest
);
93 if (data
== NULL
) e(1000);
97 for (i
= 0; i
< largest
; i
++)
98 data
[i
] = (unsigned char) (rand() % 255 + 1);
102 _PROTOTYPE(void (*call
), (off_t osize
, off_t nsize
));
106 for (i
= 0; i
< sizeof(sizes
) / sizeof(sizes
[0]); i
++)
107 for (j
= 0; j
< sizeof(sizes
) / sizeof(sizes
[0]); j
++)
108 call(sizes
[i
], sizes
[j
]);
117 if ((fd
= open(TESTFILE
, O_RDWR
|O_CREAT
|O_EXCL
, 0600)) < 0) e(1001);
121 r
= write(fd
, data
+ off
, size
- off
);
123 if (r
!= size
- off
) e(1002);
131 void check_file(fd
, hole_start
, hole_end
, size
)
137 static unsigned char buf
[16384];
142 /* The size must match. */
143 if (fstat(fd
, &statbuf
) != 0) e(1003);
144 if (statbuf
.st_size
!= size
) e(1004);
146 if (lseek(fd
, 0L, SEEK_SET
) != 0L) e(1005);
148 /* All bytes in the file must be equal to what we wrote, except for the bytes
149 * in the hole, which must be zero.
151 for (off
= 0; off
< size
; off
+= chunk
) {
152 chunk
= MIN(sizeof(buf
), size
- off
);
154 if (read(fd
, buf
, chunk
) != chunk
) e(1006);
156 for (i
= 0; i
< chunk
; i
++) {
157 if (off
+ i
>= hole_start
&& off
+ i
< hole_end
) {
158 if (buf
[i
] != 0) e(1007);
161 if (buf
[i
] != data
[off
+i
]) e(1008);
166 /* We must get back EOF at the end. */
167 if (read(fd
, buf
, sizeof(buf
)) != 0) e(1009);
177 if ((fd
= open(TESTFILE
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0) e(1);
179 if (write(fd
, data
, TESTSIZE
) != TESTSIZE
) e(2);
181 /* Negative sizes should result in EINVAL. */
182 if (truncate(TESTFILE
, -1) != -1) e(3);
183 if (errno
!= EINVAL
) e(4);
185 /* Make sure the file size did not change. */
186 if (fstat(fd
, &statbuf
) != 0) e(5);
187 if (statbuf
.st_size
!= TESTSIZE
) e(6);
190 if (unlink(TESTFILE
) != 0) e(7);
192 /* An empty path should result in ENOENT. */
193 if (truncate("", 0) != -1) e(8);
194 if (errno
!= ENOENT
) e(9);
196 /* A non-existing file name should result in ENOENT. */
197 if (truncate(TESTFILE
"2", 0) != -1) e(10);
198 if (errno
!= ENOENT
) e(11);
208 if ((fd
= open(TESTFILE
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0) e(1);
210 if (write(fd
, data
, TESTSIZE
) != TESTSIZE
) e(2);
212 /* Negative sizes should result in EINVAL. */
213 if (ftruncate(fd
, -1) != -1) e(3);
214 if (errno
!= EINVAL
) e(4);
216 /* Make sure the file size did not change. */
217 if (fstat(fd
, &statbuf
) != 0) e(5);
218 if (statbuf
.st_size
!= TESTSIZE
) e(6);
222 /* Calls on an invalid file descriptor should return EBADF or EINVAL. */
223 if (ftruncate(fd
, 0) != -1) e(7);
224 if (errno
!= EBADF
&& errno
!= EINVAL
) e(8);
226 if ((fd
= open(TESTFILE
, O_RDONLY
)) < 0) e(9);
228 /* Calls on a file opened read-only should return EBADF or EINVAL. */
229 if (ftruncate(fd
, 0) != -1) e(10);
230 if (errno
!= EBADF
&& errno
!= EINVAL
) e(11);
234 if (unlink(TESTFILE
) != 0) e(12);
246 if ((fd
= open(TESTFILE
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0) e(1);
248 if (write(fd
, data
, TESTSIZE
) != TESTSIZE
) e(2);
251 if (lseek(fd
, off
, SEEK_SET
) != off
) e(3);
255 /* Negative sizes should result in EINVAL. */
256 flock
.l_whence
= SEEK_SET
;
258 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(4);
259 if (errno
!= EINVAL
) e(5);
261 flock
.l_whence
= SEEK_CUR
;
262 flock
.l_start
= -off
- 1;
263 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(6);
264 if (errno
!= EINVAL
) e(7);
266 flock
.l_whence
= SEEK_END
;
267 flock
.l_start
= -TESTSIZE
- 1;
268 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(8);
269 if (errno
!= EINVAL
) e(9);
271 /* Make sure the file size did not change. */
272 if (fstat(fd
, &statbuf
) != 0) e(10);
273 if (statbuf
.st_size
!= TESTSIZE
) e(11);
275 /* Proper negative values should work, however. */
276 flock
.l_whence
= SEEK_CUR
;
278 if (fcntl(fd
, F_FREESP
, &flock
) != 0) e(12);
280 if (fstat(fd
, &statbuf
) != 0) e(13);
281 if (statbuf
.st_size
!= off
- 1) e(14);
283 flock
.l_whence
= SEEK_END
;
284 flock
.l_start
= -off
+ 1;
285 if (fcntl(fd
, F_FREESP
, &flock
) != 0) e(15);
287 if (fstat(fd
, &statbuf
) != 0) e(16);
288 if (statbuf
.st_size
!= 0L) e(17);
292 /* Calls on an invalid file descriptor should return EBADF or EINVAL. */
293 flock
.l_whence
= SEEK_SET
;
295 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(18);
296 if (errno
!= EBADF
&& errno
!= EINVAL
) e(19);
298 if ((fd
= open(TESTFILE
, O_RDONLY
)) < 0) e(20);
300 /* Calls on a file opened read-only should return EBADF or EINVAL. */
301 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(21);
302 if (errno
!= EBADF
&& errno
!= EINVAL
) e(22);
306 if (unlink(TESTFILE
) != 0) e(23);
318 if ((fd
= open(TESTFILE
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0) e(1);
320 if (write(fd
, data
, TESTSIZE
) != TESTSIZE
) e(2);
323 if (lseek(fd
, off
, SEEK_SET
) != off
) e(3);
325 /* The given length must be positive. */
326 flock
.l_whence
= SEEK_CUR
;
329 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(4);
330 if (errno
!= EINVAL
) e(5);
332 /* Negative start positions are not allowed. */
333 flock
.l_whence
= SEEK_SET
;
336 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(6);
337 if (errno
!= EINVAL
) e(7);
339 flock
.l_whence
= SEEK_CUR
;
340 flock
.l_start
= -off
- 1;
341 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(8);
342 if (errno
!= EINVAL
) e(9);
344 flock
.l_whence
= SEEK_END
;
345 flock
.l_start
= -TESTSIZE
- 1;
346 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(10);
347 if (errno
!= EINVAL
) e(11);
349 /* Start positions at or beyond the end of the file are no good, either. */
350 flock
.l_whence
= SEEK_SET
;
351 flock
.l_start
= TESTSIZE
;
352 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(12);
353 if (errno
!= EINVAL
) e(13);
355 flock
.l_start
= TESTSIZE
+ 1;
356 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(13);
357 if (errno
!= EINVAL
) e(14);
359 flock
.l_whence
= SEEK_CUR
;
360 flock
.l_start
= TESTSIZE
- off
;
361 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(15);
362 if (errno
!= EINVAL
) e(16);
364 flock
.l_whence
= SEEK_END
;
366 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(17);
367 if (errno
!= EINVAL
) e(18);
369 /* End positions beyond the end of the file may be silently bounded. */
370 flock
.l_whence
= SEEK_SET
;
372 flock
.l_len
= TESTSIZE
+ 1;
373 if (fcntl(fd
, F_FREESP
, &flock
) != 0) e(19);
375 flock
.l_whence
= SEEK_CUR
;
376 flock
.l_len
= TESTSIZE
- off
+ 1;
377 if (fcntl(fd
, F_FREESP
, &flock
) != 0) e(20);
379 flock
.l_whence
= SEEK_END
;
382 if (fcntl(fd
, F_FREESP
, &flock
) != 0) e(21);
384 /* However, this must never cause the file size to change. */
385 if (fstat(fd
, &statbuf
) != 0) e(22);
386 if (statbuf
.st_size
!= TESTSIZE
) e(23);
390 /* Calls on an invalid file descriptor should return EBADF or EINVAL. */
391 flock
.l_whence
= SEEK_SET
;
393 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(24);
394 if (errno
!= EBADF
&& errno
!= EINVAL
) e(25);
396 if ((fd
= open(TESTFILE
, O_RDONLY
)) < 0) e(26);
398 /* Calls on a file opened read-only should return EBADF or EINVAL. */
399 if (fcntl(fd
, F_FREESP
, &flock
) != -1) e(27);
400 if (errno
!= EBADF
&& errno
!= EINVAL
) e(28);
404 if (unlink(TESTFILE
) != 0) e(29);
407 void sub50e(osize
, nsize
)
413 fd
= make_file(osize
);
415 if (truncate(TESTFILE
, nsize
) != 0) e(1);
417 check_file(fd
, osize
, nsize
, nsize
);
420 if (truncate(TESTFILE
, osize
) != 0) e(2);
422 check_file(fd
, nsize
, osize
, osize
);
427 if (unlink(TESTFILE
) != 0) e(3);
435 /* truncate(2) on a file that is open. */
439 void sub50f(osize
, nsize
)
445 fd
= make_file(osize
);
449 if (truncate(TESTFILE
, nsize
) != 0) e(1);
451 if ((fd
= open(TESTFILE
, O_RDONLY
)) < 0) e(2);
453 check_file(fd
, osize
, nsize
, nsize
);
458 if (truncate(TESTFILE
, osize
) != 0) e(3);
460 if ((fd
= open(TESTFILE
, O_RDONLY
)) < 0) e(4);
462 check_file(fd
, nsize
, osize
, osize
);
467 if (unlink(TESTFILE
) != 0) e(5);
474 /* truncate(2) on a file that is not open. */
478 void sub50g(osize
, nsize
)
484 fd
= make_file(osize
);
486 if (ftruncate(fd
, nsize
) != 0) e(1);
488 check_file(fd
, osize
, nsize
, nsize
);
491 if (ftruncate(fd
, osize
) != 0) e(2);
493 check_file(fd
, nsize
, osize
, osize
);
498 if (unlink(TESTFILE
) != 0) e(3);
505 /* ftruncate(2) on an open file. */
509 void sub50h(osize
, nsize
)
516 fd
= make_file(osize
);
518 flock
.l_whence
= SEEK_SET
;
519 flock
.l_start
= nsize
;
521 if (fcntl(fd
, F_FREESP
, &flock
) != 0) e(1);
523 check_file(fd
, osize
, nsize
, nsize
);
526 flock
.l_whence
= SEEK_SET
;
527 flock
.l_start
= osize
;
529 if (fcntl(fd
, F_FREESP
, &flock
) != 0) e(2);
531 check_file(fd
, nsize
, osize
, osize
);
536 if (unlink(TESTFILE
) != 0) e(3);
543 /* fcntl(2) with F_FREESP and l_len=0. */
547 void sub50i(size
, off
, len
, type
)
556 fd
= make_file(size
);
560 flock
.l_whence
= SEEK_SET
;
564 if (lseek(fd
, off
, SEEK_SET
) != off
) e(1);
565 flock
.l_whence
= SEEK_CUR
;
569 flock
.l_whence
= SEEK_END
;
570 flock
.l_start
= off
- size
;
577 if (fcntl(fd
, F_FREESP
, &flock
) != 0) e(2);
579 check_file(fd
, off
, off
+ len
, size
);
581 /* Repeat the call in order to see whether the file system can handle holes
582 * while freeing up. If not, the server would typically crash; we need not
583 * check the results again.
585 flock
.l_whence
= SEEK_SET
;
587 if (fcntl(fd
, F_FREESP
, &flock
) != 0) e(3);
591 if (unlink(TESTFILE
) != 0) e(4);
601 /* fcntl(2) with F_FREESP and l_len>0. */
603 /* This loop determines the size of the test file. */
604 for (i
= 0; i
< sizeof(sizes
) / sizeof(sizes
[0]); i
++) {
605 /* Big files simply take too long. We have to compromise here. */
606 if (sizes
[i
] >= THRESHOLD
) continue;
608 /* This loop determines one of the two values for the offset. */
609 for (j
= 0; j
< sizeof(sizes
) / sizeof(sizes
[0]); j
++) {
610 if (sizes
[j
] >= sizes
[i
]) continue;
612 /* This loop determines the other. */
613 for (k
= 0; k
< sizeof(sizes
) / sizeof(sizes
[0]); k
++) {
614 if (sizes
[k
] > sizes
[j
]) continue;
616 /* Construct an offset by adding the two sizes. */
617 off
= sizes
[j
] + sizes
[k
];
619 if (j
+ 1 < sizeof(sizes
) / sizeof(sizes
[0]) &&
620 off
>= sizes
[j
+ 1]) continue;
622 /* This loop determines the length of the hole. */
623 for (l
= 0; l
< sizeof(sizes
) / sizeof(sizes
[0]); l
++) {
624 if (sizes
[l
] == 0 || off
+ sizes
[l
] > sizes
[i
])
627 /* This could have been a loop, too! */
628 sub50i(sizes
[i
], off
, sizes
[l
], 0);
629 sub50i(sizes
[i
], off
, sizes
[l
], 1);
630 sub50i(sizes
[i
], off
, sizes
[l
], 2);