xref: /minix3/minix/tests/test50.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
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