1/*
2 * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@altlinux.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 *    derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#if defined HAVE_FTRUNCATE && defined HAVE_FUTIMENS
29
30# ifndef TEST_SYSCALL_STR
31#  error TEST_SYSCALL_STR must be defined
32# endif
33# ifndef TEST_SYSCALL_INVOKE
34#  error TEST_SYSCALL_INVOKE must be defined
35# endif
36# ifndef PRINT_SYSCALL_HEADER
37#  error PRINT_SYSCALL_HEADER must be defined
38# endif
39# ifndef PRINT_SYSCALL_FOOTER
40#  error PRINT_SYSCALL_FOOTER must be defined
41# endif
42
43# include <errno.h>
44# include <stdio.h>
45# include <stddef.h>
46# include <time.h>
47# include <unistd.h>
48# include <sys/sysmacros.h>
49
50static void
51print_time(const time_t t)
52{
53	if (!t) {
54		printf("0");
55		return;
56	}
57
58	struct tm *p = localtime(&t);
59
60	if (p) {
61		char buf[256];
62
63		strftime(buf, sizeof(buf), "%FT%T%z", p);
64
65		printf("%s", buf);
66	} else {
67		printf("%llu", zero_extend_signed_to_ull(t));
68	}
69}
70
71# ifndef STRUCT_STAT
72#  define STRUCT_STAT struct stat
73#  define STRUCT_STAT_STR "struct stat"
74#  define STRUCT_STAT_IS_STAT64 0
75# endif
76# ifndef SAMPLE_SIZE
77#  define SAMPLE_SIZE ((libc_off_t) 43147718418ULL)
78# endif
79
80typedef off_t libc_off_t;
81
82# define stat libc_stat
83# define stat64 libc_stat64
84# include <fcntl.h>
85# include <sys/stat.h>
86# undef stat
87# undef stat64
88
89# undef st_atime
90# undef st_mtime
91# undef st_ctime
92# include "asm_stat.h"
93
94# if STRUCT_STAT_IS_STAT64
95#  undef HAVE_STRUCT_STAT_ST_MTIME_NSEC
96#  if defined MPERS_IS_m32
97#   ifdef HAVE_M32_STRUCT_STAT64_ST_MTIME_NSEC
98#    define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
99#   endif
100#  elif defined MPERS_IS_mx32
101#   ifdef HAVE_MX32_STRUCT_STAT64_ST_MTIME_NSEC
102#    define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
103#   endif
104#  elif defined HAVE_STRUCT_STAT64_ST_MTIME_NSEC
105#   define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
106#  endif /* MPERS_IS_m32 || MPERS_IS_mx32 || HAVE_STRUCT_STAT64_ST_MTIME_NSEC */
107# else /* !STRUCT_STAT_IS_STAT64 */
108#  if defined MPERS_IS_m32
109#   undef HAVE_STRUCT_STAT_ST_MTIME_NSEC
110#   ifdef HAVE_M32_STRUCT_STAT_ST_MTIME_NSEC
111#    define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
112#   endif
113#  elif defined MPERS_IS_mx32
114#   undef HAVE_STRUCT_STAT_ST_MTIME_NSEC
115#   ifdef HAVE_MX32_STRUCT_STAT_ST_MTIME_NSEC
116#    define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
117#   endif
118#  endif /*  MPERS_IS_m32 || MPERS_IS_mx32 */
119# endif /* STRUCT_STAT_IS_STAT64 */
120
121# ifndef TEST_BOGUS_STRUCT_STAT
122#  define TEST_BOGUS_STRUCT_STAT 1
123# endif
124
125# ifndef IS_FSTAT
126#  define IS_FSTAT 0
127# endif
128
129# ifndef OLD_STAT
130#  define OLD_STAT 0
131# endif
132
133static void
134print_ftype(const unsigned int mode)
135{
136	if (S_ISREG(mode))
137		printf("S_IFREG");
138	else if (S_ISDIR(mode))
139		printf("S_IFDIR");
140	else if (S_ISCHR(mode))
141		printf("S_IFCHR");
142	else if (S_ISBLK(mode))
143		printf("S_IFBLK");
144	else
145		printf("%#o", mode & S_IFMT);
146}
147
148static void
149print_perms(const unsigned int mode)
150{
151	printf("%#o", mode & ~S_IFMT);
152}
153
154static void
155print_stat(const STRUCT_STAT *st)
156{
157	printf("{st_dev=makedev(%u, %u)",
158	       (unsigned int) major(zero_extend_signed_to_ull(st->st_dev)),
159	       (unsigned int) minor(zero_extend_signed_to_ull(st->st_dev)));
160	printf(", st_ino=%llu", zero_extend_signed_to_ull(st->st_ino));
161	printf(", st_mode=");
162	print_ftype(st->st_mode);
163	printf("|");
164	print_perms(st->st_mode);
165	printf(", st_nlink=%llu", zero_extend_signed_to_ull(st->st_nlink));
166	printf(", st_uid=%llu", zero_extend_signed_to_ull(st->st_uid));
167	printf(", st_gid=%llu", zero_extend_signed_to_ull(st->st_gid));
168# if OLD_STAT
169	printf(", st_blksize=0, st_blocks=0");
170# else /* !OLD_STAT */
171	printf(", st_blksize=%llu", zero_extend_signed_to_ull(st->st_blksize));
172	printf(", st_blocks=%llu", zero_extend_signed_to_ull(st->st_blocks));
173# endif /* OLD_STAT */
174
175	switch (st->st_mode & S_IFMT) {
176	case S_IFCHR: case S_IFBLK:
177		printf(", st_rdev=makedev(%u, %u)",
178		       (unsigned int) major(zero_extend_signed_to_ull(st->st_rdev)),
179		       (unsigned int) minor(zero_extend_signed_to_ull(st->st_rdev)));
180		break;
181	default:
182		printf(", st_size=%llu", zero_extend_signed_to_ull(st->st_size));
183	}
184
185	printf(", st_atime=");
186	print_time(sign_extend_unsigned_to_ll(st->st_atime));
187# if defined(HAVE_STRUCT_STAT_ST_MTIME_NSEC) && !OLD_STAT
188	if (st->st_atime_nsec)
189		printf(".%09llu", zero_extend_signed_to_ull(st->st_atime_nsec));
190# endif
191	printf(", st_mtime=");
192	print_time(sign_extend_unsigned_to_ll(st->st_mtime));
193# if defined(HAVE_STRUCT_STAT_ST_MTIME_NSEC) && !OLD_STAT
194	if (st->st_mtime_nsec)
195		printf(".%09llu", zero_extend_signed_to_ull(st->st_mtime_nsec));
196# endif
197	printf(", st_ctime=");
198	print_time(sign_extend_unsigned_to_ll(st->st_ctime));
199# if defined(HAVE_STRUCT_STAT_ST_MTIME_NSEC) && !OLD_STAT
200	if (st->st_ctime_nsec)
201		printf(".%09llu", zero_extend_signed_to_ull(st->st_ctime_nsec));
202# endif
203	printf("}");
204}
205
206static int
207create_sample(const char *fname, const libc_off_t size)
208{
209	static const struct timespec ts[] = {
210		{-10843, 135}, {-10841, 246}
211	};
212
213	(void) close(0);
214	if (open(fname, O_RDWR | O_CREAT | O_TRUNC, 0640)) {
215		perror(fname);
216		return 77;
217	}
218	if (ftruncate(0, size)) {
219		perror("ftruncate");
220		return 77;
221	}
222	if (futimens(0, ts)) {
223		perror("futimens");
224		return 77;
225	}
226	return 0;
227}
228
229int
230main(void)
231{
232# if !IS_FSTAT
233	static const char full[] = "/dev/full";
234# endif
235	static const char sample[] = TEST_SYSCALL_STR ".sample";
236	STRUCT_STAT st[2];
237
238	int rc;
239
240	rc = create_sample(sample, SAMPLE_SIZE);
241	if (rc) {
242		(void) unlink(sample);
243		return rc;
244	}
245
246# if TEST_BOGUS_STRUCT_STAT
247	STRUCT_STAT *st_cut = tail_alloc(sizeof(long) * 4);
248	rc = TEST_SYSCALL_INVOKE(sample, st_cut);
249	PRINT_SYSCALL_HEADER(sample);
250	printf("%p", st_cut);
251	PRINT_SYSCALL_FOOTER(rc);
252# endif
253
254# if !IS_FSTAT
255	rc = TEST_SYSCALL_INVOKE(full, st);
256	PRINT_SYSCALL_HEADER(full);
257	if (rc)
258		printf("%p", st);
259	else
260		print_stat(st);
261	PRINT_SYSCALL_FOOTER(rc);
262# endif
263
264	if ((rc = TEST_SYSCALL_INVOKE(sample, st))) {
265# if OLD_STAT
266		if (errno != EOVERFLOW)
267# endif
268		{
269			perror(TEST_SYSCALL_STR);
270			(void) unlink(sample);
271			return 77;
272		}
273	}
274	(void) unlink(sample);
275	if (!rc && zero_extend_signed_to_ull(SAMPLE_SIZE) !=
276	    zero_extend_signed_to_ull(st[0].st_size)) {
277		fprintf(stderr, "Size mismatch: "
278				"requested size(%llu) != st_size(%llu)\n",
279			zero_extend_signed_to_ull(SAMPLE_SIZE),
280			zero_extend_signed_to_ull(st[0].st_size));
281		fprintf(stderr, "The most likely reason for this is incorrect"
282				" definition of %s.\n"
283				"Here is some diagnostics that might help:\n",
284			STRUCT_STAT_STR);
285
286#define LOG_STAT_OFFSETOF_SIZEOF(object, member)			\
287		fprintf(stderr, "offsetof(%s, %s) = %zu"		\
288				", sizeof(%s) = %zu\n",			\
289				STRUCT_STAT_STR, #member,		\
290				offsetof(STRUCT_STAT, member),		\
291				#member, sizeof((object).member))
292
293		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_dev);
294		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_ino);
295		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_mode);
296		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_nlink);
297		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_uid);
298		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_gid);
299		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_rdev);
300		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_size);
301# if !OLD_STAT
302		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_blksize);
303		LOG_STAT_OFFSETOF_SIZEOF(st[0], st_blocks);
304# endif /* !OLD_STAT */
305
306		return 1;
307	}
308
309	PRINT_SYSCALL_HEADER(sample);
310	if (rc)
311		printf("%p", st);
312	else
313		print_stat(st);
314	PRINT_SYSCALL_FOOTER(rc);
315
316	puts("+++ exited with 0 +++");
317	return 0;
318}
319
320#else
321
322SKIP_MAIN_UNDEFINED("HAVE_FTRUNCATE && HAVE_FUTIMENS")
323
324#endif
325