1 /* Tests for truncate(2) call family - by D.C. van Moolenbroek */
2 #include <sys/stat.h>
3 #include <sys/param.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <assert.h>
7
8 #define ITERATIONS 1
9 int max_error = 4;
10 #include "common.h"
11
12
13 #define TESTFILE "testfile"
14 #define TESTSIZE 4096
15 #define THRESHOLD 1048576
16
17
18 int main(int argc, char *argv[]);
19 void prepare(void);
20 int make_file(off_t size);
21 void check_file(int fd, off_t size, off_t hole_start, off_t hole_end);
22 void all_sizes(void (*call) (off_t osize, off_t nsize));
23 void test50a(void);
24 void test50b(void);
25 void test50c(void);
26 void test50d(void);
27 void sub50e(off_t osize, off_t nsize);
28 void test50e(void);
29 void sub50f(off_t osize, off_t nsize);
30 void test50f(void);
31 void sub50g(off_t osize, off_t nsize);
32 void test50g(void);
33 void sub50h(off_t osize, off_t nsize);
34 void test50h(void);
35 void sub50i(off_t size, off_t off, size_t len, int type);
36 void test50i(void);
37
38 /* Some of the sizes have been chosen in such a way that they should be on the
39 * edge of direct/single indirect/double indirect switchovers for a MINIX
40 * file system with 4K block size.
41 */
42 static off_t sizes[] = {
43 0L, 1L, 511L, 512L, 513L, 1023L, 1024L, 1025L, 2047L, 2048L, 2049L, 3071L,
44 3072L, 3073L, 4095L, 4096L, 4097L, 16383L, 16384L, 16385L, 28671L, 28672L,
45 28673L, 65535L, 65536L, 65537L, 4222975L, 4222976L, 4222977L
46 };
47
48 static unsigned char *data;
49
main(argc,argv)50 int main(argc, argv)
51 int argc;
52 char *argv[];
53 {
54 int j, m = 0xFFFF;
55
56 start(50);
57 prepare();
58 if (argc == 2) m = atoi(argv[1]);
59 for (j = 0; j < ITERATIONS; j++) {
60 if (m & 00001) test50a();
61 if (m & 00002) test50b();
62 if (m & 00004) test50c();
63 if (m & 00010) test50d();
64 if (m & 00020) test50e();
65 if (m & 00040) test50f();
66 if (m & 00100) test50g();
67 if (m & 00200) test50h();
68 if (m & 00400) test50i();
69 }
70
71 quit();
72 return(-1); /* impossible */
73 }
74
prepare()75 void prepare()
76 {
77 size_t largest;
78 int i;
79
80 largest = 0;
81 for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
82 if (largest < sizes[i]) largest = sizes[i];
83
84 /* internal integrity check: this is needed for early tests */
85 assert(largest >= TESTSIZE);
86
87 data = malloc(largest);
88 if (data == NULL) e(1000);
89
90 srand(1);
91
92 for (i = 0; i < largest; i++)
93 data[i] = (unsigned char) (rand() % 255 + 1);
94 }
95
96 void all_sizes(call)
97 void(*call) (off_t osize, off_t nsize);
98 {
99 int i, j;
100
101 for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
102 for (j = 0; j < sizeof(sizes) / sizeof(sizes[0]); j++)
103 call(sizes[i], sizes[j]);
104 }
105
make_file(size)106 int make_file(size)
107 off_t size;
108 {
109 off_t off;
110 int fd, r;
111
112 if ((fd = open(TESTFILE, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0) e(1001);
113
114 off = 0;
115 while (off < size) {
116 r = write(fd, data + off, size - off);
117
118 if (r != size - off) e(1002);
119
120 off += r;
121 }
122
123 return fd;
124 }
125
check_file(fd,hole_start,hole_end,size)126 void check_file(fd, hole_start, hole_end, size)
127 int fd;
128 off_t hole_start;
129 off_t hole_end;
130 off_t size;
131 {
132 static unsigned char buf[16384];
133 struct stat statbuf;
134 off_t off;
135 int i, chunk;
136
137 /* The size must match. */
138 if (fstat(fd, &statbuf) != 0) e(1003);
139 if (statbuf.st_size != size) e(1004);
140
141 if (lseek(fd, 0L, SEEK_SET) != 0L) e(1005);
142
143 /* All bytes in the file must be equal to what we wrote, except for the bytes
144 * in the hole, which must be zero.
145 */
146 for (off = 0; off < size; off += chunk) {
147 chunk = MIN(sizeof(buf), size - off);
148
149 if (read(fd, buf, chunk) != chunk) e(1006);
150
151 for (i = 0; i < chunk; i++) {
152 if (off + i >= hole_start && off + i < hole_end) {
153 if (buf[i] != 0) e(1007);
154 }
155 else {
156 if (buf[i] != data[off+i]) e(1008);
157 }
158 }
159 }
160
161 /* We must get back EOF at the end. */
162 if (read(fd, buf, sizeof(buf)) != 0) e(1009);
163 }
164
test50a()165 void test50a()
166 {
167 struct stat statbuf;
168 int fd;
169
170 subtest = 1;
171
172 if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1);
173
174 if (write(fd, data, TESTSIZE) != TESTSIZE) e(2);
175
176 /* Negative sizes should result in EINVAL. */
177 if (truncate(TESTFILE, -1) != -1) e(3);
178 if (errno != EINVAL) e(4);
179
180 /* Make sure the file size did not change. */
181 if (fstat(fd, &statbuf) != 0) e(5);
182 if (statbuf.st_size != TESTSIZE) e(6);
183
184 close(fd);
185 if (unlink(TESTFILE) != 0) e(7);
186
187 /* An empty path should result in ENOENT. */
188 if (truncate("", 0) != -1) e(8);
189 if (errno != ENOENT) e(9);
190
191 /* A non-existing file name should result in ENOENT. */
192 if (truncate(TESTFILE"2", 0) != -1) e(10);
193 if (errno != ENOENT) e(11);
194 }
195
test50b()196 void test50b()
197 {
198 struct stat statbuf;
199 int fd;
200
201 subtest = 2;
202
203 if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1);
204
205 if (write(fd, data, TESTSIZE) != TESTSIZE) e(2);
206
207 /* Negative sizes should result in EINVAL. */
208 if (ftruncate(fd, -1) != -1) e(3);
209 if (errno != EINVAL) e(4);
210
211 /* Make sure the file size did not change. */
212 if (fstat(fd, &statbuf) != 0) e(5);
213 if (statbuf.st_size != TESTSIZE) e(6);
214
215 close(fd);
216
217 /* Calls on an invalid file descriptor should return EBADF or EINVAL. */
218 if (ftruncate(fd, 0) != -1) e(7);
219 if (errno != EBADF && errno != EINVAL) e(8);
220
221 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(9);
222
223 /* Calls on a file opened read-only should return EBADF or EINVAL. */
224 if (ftruncate(fd, 0) != -1) e(10);
225 if (errno != EBADF && errno != EINVAL) e(11);
226
227 close(fd);
228
229 if (unlink(TESTFILE) != 0) e(12);
230 }
231
test50c()232 void test50c()
233 {
234 struct stat statbuf;
235 struct flock flock;
236 off_t off;
237 int fd;
238
239 subtest = 3;
240
241 if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1);
242
243 if (write(fd, data, TESTSIZE) != TESTSIZE) e(2);
244
245 off = TESTSIZE / 2;
246 if (lseek(fd, off, SEEK_SET) != off) e(3);
247
248 flock.l_len = 0;
249
250 /* Negative sizes should result in EINVAL. */
251 flock.l_whence = SEEK_SET;
252 flock.l_start = -1;
253 if (fcntl(fd, F_FREESP, &flock) != -1) e(4);
254 if (errno != EINVAL) e(5);
255
256 flock.l_whence = SEEK_CUR;
257 flock.l_start = -off - 1;
258 if (fcntl(fd, F_FREESP, &flock) != -1) e(6);
259 if (errno != EINVAL) e(7);
260
261 flock.l_whence = SEEK_END;
262 flock.l_start = -TESTSIZE - 1;
263 if (fcntl(fd, F_FREESP, &flock) != -1) e(8);
264 if (errno != EINVAL) e(9);
265
266 /* Make sure the file size did not change. */
267 if (fstat(fd, &statbuf) != 0) e(10);
268 if (statbuf.st_size != TESTSIZE) e(11);
269
270 /* Proper negative values should work, however. */
271 flock.l_whence = SEEK_CUR;
272 flock.l_start = -1;
273 if (fcntl(fd, F_FREESP, &flock) != 0) e(12);
274
275 if (fstat(fd, &statbuf) != 0) e(13);
276 if (statbuf.st_size != off - 1) e(14);
277
278 flock.l_whence = SEEK_END;
279 flock.l_start = -off + 1;
280 if (fcntl(fd, F_FREESP, &flock) != 0) e(15);
281
282 if (fstat(fd, &statbuf) != 0) e(16);
283 if (statbuf.st_size != 0L) e(17);
284
285 close(fd);
286
287 /* Calls on an invalid file descriptor should return EBADF or EINVAL. */
288 flock.l_whence = SEEK_SET;
289 flock.l_start = 0;
290 if (fcntl(fd, F_FREESP, &flock) != -1) e(18);
291 if (errno != EBADF && errno != EINVAL) e(19);
292
293 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(20);
294
295 /* Calls on a file opened read-only should return EBADF or EINVAL. */
296 if (fcntl(fd, F_FREESP, &flock) != -1) e(21);
297 if (errno != EBADF && errno != EINVAL) e(22);
298
299 close(fd);
300
301 if (unlink(TESTFILE) != 0) e(23);
302 }
303
test50d()304 void test50d()
305 {
306 struct stat statbuf;
307 struct flock flock;
308 off_t off;
309 int fd;
310
311 subtest = 4;
312
313 if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1);
314
315 if (write(fd, data, TESTSIZE) != TESTSIZE) e(2);
316
317 off = TESTSIZE / 2;
318 if (lseek(fd, off, SEEK_SET) != off) e(3);
319
320 /* The given length must be positive. */
321 flock.l_whence = SEEK_CUR;
322 flock.l_start = 0;
323 flock.l_len = -1;
324 if (fcntl(fd, F_FREESP, &flock) != -1) e(4);
325 if (errno != EINVAL) e(5);
326
327 /* Negative start positions are not allowed. */
328 flock.l_whence = SEEK_SET;
329 flock.l_start = -1;
330 flock.l_len = 1;
331 if (fcntl(fd, F_FREESP, &flock) != -1) e(6);
332 if (errno != EINVAL) e(7);
333
334 flock.l_whence = SEEK_CUR;
335 flock.l_start = -off - 1;
336 if (fcntl(fd, F_FREESP, &flock) != -1) e(8);
337 if (errno != EINVAL) e(9);
338
339 flock.l_whence = SEEK_END;
340 flock.l_start = -TESTSIZE - 1;
341 if (fcntl(fd, F_FREESP, &flock) != -1) e(10);
342 if (errno != EINVAL) e(11);
343
344 /* Start positions at or beyond the end of the file are no good, either. */
345 flock.l_whence = SEEK_SET;
346 flock.l_start = TESTSIZE;
347 if (fcntl(fd, F_FREESP, &flock) != -1) e(12);
348 if (errno != EINVAL) e(13);
349
350 flock.l_start = TESTSIZE + 1;
351 if (fcntl(fd, F_FREESP, &flock) != -1) e(13);
352 if (errno != EINVAL) e(14);
353
354 flock.l_whence = SEEK_CUR;
355 flock.l_start = TESTSIZE - off;
356 if (fcntl(fd, F_FREESP, &flock) != -1) e(15);
357 if (errno != EINVAL) e(16);
358
359 flock.l_whence = SEEK_END;
360 flock.l_start = 1;
361 if (fcntl(fd, F_FREESP, &flock) != -1) e(17);
362 if (errno != EINVAL) e(18);
363
364 /* End positions beyond the end of the file may be silently bounded. */
365 flock.l_whence = SEEK_SET;
366 flock.l_start = 0;
367 flock.l_len = TESTSIZE + 1;
368 if (fcntl(fd, F_FREESP, &flock) != 0) e(19);
369
370 flock.l_whence = SEEK_CUR;
371 flock.l_len = TESTSIZE - off + 1;
372 if (fcntl(fd, F_FREESP, &flock) != 0) e(20);
373
374 flock.l_whence = SEEK_END;
375 flock.l_start = -1;
376 flock.l_len = 2;
377 if (fcntl(fd, F_FREESP, &flock) != 0) e(21);
378
379 /* However, this must never cause the file size to change. */
380 if (fstat(fd, &statbuf) != 0) e(22);
381 if (statbuf.st_size != TESTSIZE) e(23);
382
383 close(fd);
384
385 /* Calls on an invalid file descriptor should return EBADF or EINVAL. */
386 flock.l_whence = SEEK_SET;
387 flock.l_start = 0;
388 if (fcntl(fd, F_FREESP, &flock) != -1) e(24);
389 if (errno != EBADF && errno != EINVAL) e(25);
390
391 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(26);
392
393 /* Calls on a file opened read-only should return EBADF or EINVAL. */
394 if (fcntl(fd, F_FREESP, &flock) != -1) e(27);
395 if (errno != EBADF && errno != EINVAL) e(28);
396
397 close(fd);
398
399 if (unlink(TESTFILE) != 0) e(29);
400 }
401
sub50e(osize,nsize)402 void sub50e(osize, nsize)
403 off_t osize;
404 off_t nsize;
405 {
406 int fd;
407
408 fd = make_file(osize);
409
410 if (truncate(TESTFILE, nsize) != 0) e(1);
411
412 check_file(fd, osize, nsize, nsize);
413
414 if (nsize < osize) {
415 if (truncate(TESTFILE, osize) != 0) e(2);
416
417 check_file(fd, nsize, osize, osize);
418 }
419
420 close(fd);
421
422 if (unlink(TESTFILE) != 0) e(3);
423
424 }
425
test50e()426 void test50e()
427 {
428 subtest = 5;
429
430 /* truncate(2) on a file that is open. */
431 all_sizes(sub50e);
432 }
433
sub50f(osize,nsize)434 void sub50f(osize, nsize)
435 off_t osize;
436 off_t nsize;
437 {
438 int fd;
439
440 fd = make_file(osize);
441
442 close(fd);
443
444 if (truncate(TESTFILE, nsize) != 0) e(1);
445
446 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(2);
447
448 check_file(fd, osize, nsize, nsize);
449
450 if (nsize < osize) {
451 close(fd);
452
453 if (truncate(TESTFILE, osize) != 0) e(3);
454
455 if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(4);
456
457 check_file(fd, nsize, osize, osize);
458 }
459
460 close(fd);
461
462 if (unlink(TESTFILE) != 0) e(5);
463 }
464
test50f()465 void test50f()
466 {
467 subtest = 6;
468
469 /* truncate(2) on a file that is not open. */
470 all_sizes(sub50f);
471 }
472
sub50g(osize,nsize)473 void sub50g(osize, nsize)
474 off_t osize;
475 off_t nsize;
476 {
477 int fd;
478
479 fd = make_file(osize);
480
481 if (ftruncate(fd, nsize) != 0) e(1);
482
483 check_file(fd, osize, nsize, nsize);
484
485 if (nsize < osize) {
486 if (ftruncate(fd, osize) != 0) e(2);
487
488 check_file(fd, nsize, osize, osize);
489 }
490
491 close(fd);
492
493 if (unlink(TESTFILE) != 0) e(3);
494 }
495
test50g()496 void test50g()
497 {
498 subtest = 7;
499
500 /* ftruncate(2) on an open file. */
501 all_sizes(sub50g);
502 }
503
sub50h(osize,nsize)504 void sub50h(osize, nsize)
505 off_t osize;
506 off_t nsize;
507 {
508 struct flock flock;
509 int fd;
510
511 fd = make_file(osize);
512
513 flock.l_whence = SEEK_SET;
514 flock.l_start = nsize;
515 flock.l_len = 0;
516 if (fcntl(fd, F_FREESP, &flock) != 0) e(1);
517
518 check_file(fd, osize, nsize, nsize);
519
520 if (nsize < osize) {
521 flock.l_whence = SEEK_SET;
522 flock.l_start = osize;
523 flock.l_len = 0;
524 if (fcntl(fd, F_FREESP, &flock) != 0) e(2);
525
526 check_file(fd, nsize, osize, osize);
527 }
528
529 close(fd);
530
531 if (unlink(TESTFILE) != 0) e(3);
532 }
533
test50h()534 void test50h()
535 {
536 subtest = 8;
537
538 /* fcntl(2) with F_FREESP and l_len=0. */
539 all_sizes(sub50h);
540 }
541
sub50i(size,off,len,type)542 void sub50i(size, off, len, type)
543 off_t size;
544 off_t off;
545 size_t len;
546 int type;
547 {
548 struct flock flock;
549 int fd;
550
551 fd = make_file(size);
552
553 switch (type) {
554 case 0:
555 flock.l_whence = SEEK_SET;
556 flock.l_start = off;
557 break;
558 case 1:
559 if (lseek(fd, off, SEEK_SET) != off) e(1);
560 flock.l_whence = SEEK_CUR;
561 flock.l_start = 0;
562 break;
563 case 2:
564 flock.l_whence = SEEK_END;
565 flock.l_start = off - size;
566 break;
567 default:
568 e(1);
569 }
570
571 flock.l_len = len;
572 if (fcntl(fd, F_FREESP, &flock) != 0) e(2);
573
574 check_file(fd, off, off + len, size);
575
576 /* Repeat the call in order to see whether the file system can handle holes
577 * while freeing up. If not, the server would typically crash; we need not
578 * check the results again.
579 */
580 flock.l_whence = SEEK_SET;
581 flock.l_start = off;
582 if (fcntl(fd, F_FREESP, &flock) != 0) e(3);
583
584 close(fd);
585
586 if (unlink(TESTFILE) != 0) e(4);
587 }
588
test50i()589 void test50i()
590 {
591 off_t off;
592 int i, j, k, l;
593
594 subtest = 9;
595
596 /* fcntl(2) with F_FREESP and l_len>0. */
597
598 /* This loop determines the size of the test file. */
599 for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) {
600 /* Big files simply take too long. We have to compromise here. */
601 if (sizes[i] >= THRESHOLD) continue;
602
603 /* This loop determines one of the two values for the offset. */
604 for (j = 0; j < sizeof(sizes) / sizeof(sizes[0]); j++) {
605 if (sizes[j] >= sizes[i]) continue;
606
607 /* This loop determines the other. */
608 for (k = 0; k < sizeof(sizes) / sizeof(sizes[0]); k++) {
609 if (sizes[k] > sizes[j]) continue;
610
611 /* Construct an offset by adding the two sizes. */
612 off = sizes[j] + sizes[k];
613
614 if (j + 1 < sizeof(sizes) / sizeof(sizes[0]) &&
615 off >= sizes[j + 1]) continue;
616
617 /* This loop determines the length of the hole. */
618 for (l = 0; l < sizeof(sizes) / sizeof(sizes[0]); l++) {
619 if (sizes[l] == 0 || off + sizes[l] > sizes[i])
620 continue;
621
622 /* This could have been a loop, too! */
623 sub50i(sizes[i], off, sizes[l], 0);
624 sub50i(sizes[i], off, sizes[l], 1);
625 sub50i(sizes[i], off, sizes[l], 2);
626 }
627 }
628 }
629 }
630 }
631