1/*
2 * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@altlinux.org>
3 * Copyright (c) 2015-2017 The strace developers.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#if defined HAVE_FTRUNCATE && defined HAVE_FUTIMENS
30
31# ifndef TEST_SYSCALL_STR
32#  error TEST_SYSCALL_STR must be defined
33# endif
34# ifndef TEST_SYSCALL_INVOKE
35#  error TEST_SYSCALL_INVOKE must be defined
36# endif
37# ifndef PRINT_SYSCALL_HEADER
38#  error PRINT_SYSCALL_HEADER must be defined
39# endif
40# ifndef PRINT_SYSCALL_FOOTER
41#  error PRINT_SYSCALL_FOOTER must be defined
42# endif
43
44# include <errno.h>
45# include <stdio.h>
46# include <stddef.h>
47# include <time.h>
48# include <unistd.h>
49# include <sys/sysmacros.h>
50
51# include "print_fields.h"
52# include "statx.h"
53
54# ifndef STRUCT_STAT
55#  define STRUCT_STAT struct stat
56#  define STRUCT_STAT_STR "struct stat"
57#  define STRUCT_STAT_IS_STAT64 0
58# endif
59# ifndef SAMPLE_SIZE
60#  define SAMPLE_SIZE ((libc_off_t) 43147718418ULL)
61# endif
62
63typedef off_t libc_off_t;
64
65# define stat libc_stat
66# define stat64 libc_stat64
67# include <fcntl.h>
68# include <sys/stat.h>
69# undef stat
70# undef stat64
71
72# undef st_atime
73# undef st_mtime
74# undef st_ctime
75# include "asm_stat.h"
76
77# if STRUCT_STAT_IS_STAT64
78#  undef HAVE_STRUCT_STAT_ST_MTIME_NSEC
79#  if defined MPERS_IS_m32
80#   ifdef HAVE_M32_STRUCT_STAT64_ST_MTIME_NSEC
81#    define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
82#   endif
83#  elif defined MPERS_IS_mx32
84#   ifdef HAVE_MX32_STRUCT_STAT64_ST_MTIME_NSEC
85#    define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
86#   endif
87#  elif defined HAVE_STRUCT_STAT64_ST_MTIME_NSEC
88#   define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
89#  endif /* MPERS_IS_m32 || MPERS_IS_mx32 || HAVE_STRUCT_STAT64_ST_MTIME_NSEC */
90# else /* !STRUCT_STAT_IS_STAT64 */
91#  if defined MPERS_IS_m32
92#   undef HAVE_STRUCT_STAT_ST_MTIME_NSEC
93#   ifdef HAVE_M32_STRUCT_STAT_ST_MTIME_NSEC
94#    define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
95#   endif
96#  elif defined MPERS_IS_mx32
97#   undef HAVE_STRUCT_STAT_ST_MTIME_NSEC
98#   ifdef HAVE_MX32_STRUCT_STAT_ST_MTIME_NSEC
99#    define HAVE_STRUCT_STAT_ST_MTIME_NSEC 1
100#   endif
101#  endif /*  MPERS_IS_m32 || MPERS_IS_mx32 */
102# endif /* STRUCT_STAT_IS_STAT64 */
103
104# ifndef TEST_BOGUS_STRUCT_STAT
105#  define TEST_BOGUS_STRUCT_STAT 1
106# endif
107
108# ifndef IS_FSTAT
109#  define IS_FSTAT 0
110# endif
111
112# ifndef OLD_STAT
113#  define OLD_STAT 0
114# endif
115
116# ifndef IS_STATX
117#  define IS_STATX 0
118# endif
119
120static void
121print_ftype(const unsigned int mode)
122{
123	if (S_ISREG(mode))
124		printf("S_IFREG");
125	else if (S_ISDIR(mode))
126		printf("S_IFDIR");
127	else if (S_ISCHR(mode))
128		printf("S_IFCHR");
129	else if (S_ISBLK(mode))
130		printf("S_IFBLK");
131	else
132		printf("%#o", mode & S_IFMT);
133}
134
135static void
136print_perms(const unsigned int mode)
137{
138	printf("%#o", mode & ~S_IFMT);
139}
140
141# if !IS_STATX
142
143static void
144print_stat(const STRUCT_STAT *st)
145{
146	printf("{st_dev=makedev(%u, %u)",
147	       (unsigned int) major(zero_extend_signed_to_ull(st->st_dev)),
148	       (unsigned int) minor(zero_extend_signed_to_ull(st->st_dev)));
149	printf(", st_ino=%llu", zero_extend_signed_to_ull(st->st_ino));
150	printf(", st_mode=");
151	print_ftype(st->st_mode);
152	printf("|");
153	print_perms(st->st_mode);
154	printf(", st_nlink=%llu", zero_extend_signed_to_ull(st->st_nlink));
155	printf(", st_uid=%llu", zero_extend_signed_to_ull(st->st_uid));
156	printf(", st_gid=%llu", zero_extend_signed_to_ull(st->st_gid));
157#  if OLD_STAT
158	printf(", st_blksize=0, st_blocks=0");
159#  else /* !OLD_STAT */
160	printf(", st_blksize=%llu", zero_extend_signed_to_ull(st->st_blksize));
161	printf(", st_blocks=%llu", zero_extend_signed_to_ull(st->st_blocks));
162#  endif /* OLD_STAT */
163
164	switch (st->st_mode & S_IFMT) {
165	case S_IFCHR: case S_IFBLK:
166		printf(", st_rdev=makedev(%u, %u)",
167		       (unsigned int) major(zero_extend_signed_to_ull(st->st_rdev)),
168		       (unsigned int) minor(zero_extend_signed_to_ull(st->st_rdev)));
169		break;
170	default:
171		printf(", st_size=%llu", zero_extend_signed_to_ull(st->st_size));
172	}
173
174#  if defined(HAVE_STRUCT_STAT_ST_MTIME_NSEC) && !OLD_STAT
175#   define TIME_NSEC(val)	zero_extend_signed_to_ull(val)
176#   define HAVE_NSEC		1
177#  else
178#   define TIME_NSEC(val)	0ULL
179#   define HAVE_NSEC		0
180#  endif
181
182#define PRINT_ST_TIME(field)							\
183	do {									\
184		printf(", st_" #field "=%lld",					\
185		       sign_extend_unsigned_to_ll(st->st_ ## field));		\
186		print_time_t_nsec(sign_extend_unsigned_to_ll(st->st_ ## field),	\
187				  TIME_NSEC(st->st_ ## field ## _nsec), 1);	\
188		if (HAVE_NSEC)							\
189			printf(", st_" #field "_nsec=%llu",			\
190			       TIME_NSEC(st->st_ ## field ## _nsec));		\
191	} while (0)
192
193	PRINT_ST_TIME(atime);
194	PRINT_ST_TIME(mtime);
195	PRINT_ST_TIME(ctime);
196	printf("}");
197}
198
199# else /* !IS_STATX */
200
201static void
202print_stat(const STRUCT_STAT *st)
203{
204#  define PRINT_FIELD_U32_UID(field)					\
205	do {								\
206		if (st->field == (uint32_t) -1)				\
207			printf(", %s=-1", #field);			\
208		else							\
209			printf(", %s=%llu", #field,			\
210			       (unsigned long long) st->field);		\
211	} while (0)
212
213#  define PRINT_FIELD_TIME(field)						\
214	do {									\
215		printf(", %s={tv_sec=%lld, tv_nsec=%u}",			\
216		       #field, (long long) st->field.tv_sec,			\
217		       (unsigned) st->field.tv_nsec);				\
218		print_time_t_nsec(st->field.tv_sec,				\
219				  zero_extend_signed_to_ull(st->field.tv_nsec),	\
220				  1);						\
221	} while (0)
222
223	printf("{stx_mask=");
224	printflags(statx_masks, st->stx_mask, "STATX_???");
225
226	PRINT_FIELD_U(", ", *st, stx_blksize);
227
228	printf(", stx_attributes=");
229	printflags(statx_attrs, st->stx_attributes, "STATX_ATTR_???");
230
231	PRINT_FIELD_U(", ", *st, stx_nlink);
232	PRINT_FIELD_U32_UID(stx_uid);
233	PRINT_FIELD_U32_UID(stx_gid);
234
235	printf(", stx_mode=");
236	print_ftype(st->stx_mode);
237	printf("|");
238	print_perms(st->stx_mode);
239
240	PRINT_FIELD_U(", ", *st, stx_ino);
241	PRINT_FIELD_U(", ", *st, stx_size);
242	PRINT_FIELD_U(", ", *st, stx_blocks);
243
244	printf(", stx_attributes_mask=");
245	printflags(statx_attrs, st->stx_attributes_mask, "STATX_ATTR_???");
246
247	PRINT_FIELD_TIME(stx_atime);
248	PRINT_FIELD_TIME(stx_btime);
249	PRINT_FIELD_TIME(stx_ctime);
250	PRINT_FIELD_TIME(stx_mtime);
251	PRINT_FIELD_U(", ", *st, stx_rdev_major);
252	PRINT_FIELD_U(", ", *st, stx_rdev_minor);
253	PRINT_FIELD_U(", ", *st, stx_dev_major);
254	PRINT_FIELD_U(", ", *st, stx_dev_minor);
255	printf("}");
256}
257
258# endif /* !IS_STATX */
259
260static int
261create_sample(const char *fname, const libc_off_t size)
262{
263	static const struct timespec ts[] = {
264		{-10843, 135}, {-10841, 246}
265	};
266
267	(void) close(0);
268	if (open(fname, O_RDWR | O_CREAT | O_TRUNC, 0640)) {
269		perror(fname);
270		return 77;
271	}
272	if (ftruncate(0, size)) {
273		perror("ftruncate");
274		return 77;
275	}
276	if (futimens(0, ts)) {
277		perror("futimens");
278		return 77;
279	}
280	return 0;
281}
282
283int
284main(void)
285{
286# if IS_FSTAT
287	skip_if_unavailable("/proc/self/fd/");
288# else
289	static const char full[] = "/dev/full";
290# endif
291	static const char sample[] = "stat.sample";
292	TAIL_ALLOC_OBJECT_CONST_PTR(STRUCT_STAT, st);
293
294	int rc;
295
296	rc = create_sample(sample, SAMPLE_SIZE);
297	if (rc)
298		return rc;
299
300# if TEST_BOGUS_STRUCT_STAT
301	STRUCT_STAT *st_cut = tail_alloc(sizeof(long) * 4);
302	rc = TEST_SYSCALL_INVOKE(sample, st_cut);
303	PRINT_SYSCALL_HEADER(sample);
304	printf("%p", st_cut);
305	PRINT_SYSCALL_FOOTER(rc);
306# endif
307
308# if !IS_FSTAT
309	rc = TEST_SYSCALL_INVOKE(full, st);
310	PRINT_SYSCALL_HEADER(full);
311	if (rc)
312		printf("%p", st);
313	else
314		print_stat(st);
315	PRINT_SYSCALL_FOOTER(rc);
316# endif
317
318	if ((rc = TEST_SYSCALL_INVOKE(sample, st))) {
319		if (errno != EOVERFLOW) {
320			rc = (errno == ENOSYS) ? 77 : 1;
321			perror(TEST_SYSCALL_STR);
322			return rc;
323		}
324	}
325
326# if IS_STATX
327#  define ST_SIZE_FIELD stx_size
328# else
329#  define ST_SIZE_FIELD st_size
330# endif
331	if (!rc && zero_extend_signed_to_ull(SAMPLE_SIZE) !=
332	    zero_extend_signed_to_ull(st->ST_SIZE_FIELD)) {
333		fprintf(stderr, "Size mismatch: "
334				"requested size(%llu) != st_size(%llu)\n",
335			zero_extend_signed_to_ull(SAMPLE_SIZE),
336			zero_extend_signed_to_ull(st->ST_SIZE_FIELD));
337		fprintf(stderr, "The most likely reason for this is incorrect"
338				" definition of %s.\n"
339				"Here is some diagnostics that might help:\n",
340			STRUCT_STAT_STR);
341
342# define LOG_STAT_OFFSETOF_SIZEOF(object, member)			\
343		fprintf(stderr, "offsetof(%s, %s) = %zu"		\
344				", sizeof(%s) = %zu\n",			\
345				STRUCT_STAT_STR, #member,		\
346				offsetof(STRUCT_STAT, member),		\
347				#member, sizeof((object).member))
348
349# if IS_STATX
350		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_mask);
351		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_blksize);
352		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_attributes);
353		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_nlink);
354		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_uid);
355		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_gid);
356		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_mode);
357		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_ino);
358		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_size);
359		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_blocks);
360		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_attributes_mask);
361		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_atime);
362		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_btime);
363		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_ctime);
364		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_mtime);
365		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_rdev_major);
366		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_rdev_minor);
367		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_dev_major);
368		LOG_STAT_OFFSETOF_SIZEOF(*st, stx_dev_minor);
369# else
370		LOG_STAT_OFFSETOF_SIZEOF(*st, st_dev);
371		LOG_STAT_OFFSETOF_SIZEOF(*st, st_ino);
372		LOG_STAT_OFFSETOF_SIZEOF(*st, st_mode);
373		LOG_STAT_OFFSETOF_SIZEOF(*st, st_nlink);
374		LOG_STAT_OFFSETOF_SIZEOF(*st, st_uid);
375		LOG_STAT_OFFSETOF_SIZEOF(*st, st_gid);
376		LOG_STAT_OFFSETOF_SIZEOF(*st, st_rdev);
377		LOG_STAT_OFFSETOF_SIZEOF(*st, st_size);
378#  if !OLD_STAT
379		LOG_STAT_OFFSETOF_SIZEOF(*st, st_blksize);
380		LOG_STAT_OFFSETOF_SIZEOF(*st, st_blocks);
381#  endif /* !OLD_STAT */
382
383# endif /* IS_STATX */
384
385		return 1;
386	}
387
388	PRINT_SYSCALL_HEADER(sample);
389	if (rc)
390		printf("%p", st);
391	else
392		print_stat(st);
393	PRINT_SYSCALL_FOOTER(rc);
394
395# if IS_STATX
396
397#  define INVOKE()					\
398	do {						\
399		rc = TEST_SYSCALL_INVOKE(sample, st);	\
400		PRINT_SYSCALL_HEADER(sample);		\
401		if (rc)					\
402			printf("%p", st);		\
403		else					\
404			print_stat(st);			\
405		PRINT_SYSCALL_FOOTER(rc);		\
406	} while (0)
407
408#  define SET_FLAGS_INVOKE(flags, flags_str)			\
409	do {							\
410		TEST_SYSCALL_STATX_FLAGS = flags;		\
411		TEST_SYSCALL_STATX_FLAGS_STR = flags_str;	\
412		INVOKE();					\
413	} while (0)
414
415#  define SET_MASK_INVOKE(mask, mask_str)			\
416	do {							\
417		TEST_SYSCALL_STATX_MASK = mask;			\
418		TEST_SYSCALL_STATX_MASK_STR = mask_str;		\
419		INVOKE();					\
420	} while (0)
421
422	unsigned old_flags = TEST_SYSCALL_STATX_FLAGS;
423	const char *old_flags_str = TEST_SYSCALL_STATX_FLAGS_STR;
424	unsigned old_mask = TEST_SYSCALL_STATX_MASK;
425	const char *old_mask_str = TEST_SYSCALL_STATX_MASK_STR;
426
427	SET_FLAGS_INVOKE(AT_SYMLINK_FOLLOW | 0xffff0000U,
428		"AT_STATX_SYNC_AS_STAT|AT_SYMLINK_FOLLOW|0xffff0000");
429
430	SET_FLAGS_INVOKE(AT_STATX_SYNC_TYPE,
431		"AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC");
432
433	SET_FLAGS_INVOKE(0xffffff,
434		"AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC|AT_SYMLINK_NOFOLLOW|"
435		"AT_REMOVEDIR|AT_SYMLINK_FOLLOW|AT_NO_AUTOMOUNT|AT_EMPTY_PATH|"
436		"0xff80ff");
437
438	/* We're done playing with flags. */
439	TEST_SYSCALL_STATX_FLAGS = old_flags;
440	TEST_SYSCALL_STATX_FLAGS_STR = old_flags_str;
441
442	SET_MASK_INVOKE(0, "0");
443	SET_MASK_INVOKE(0xfffff000U, "0xfffff000 /* STATX_??? */");
444
445	SET_MASK_INVOKE(0xfffffffbU,
446		"STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID|STATX_ATIME|"
447		"STATX_MTIME|STATX_CTIME|STATX_INO|STATX_SIZE|STATX_BLOCKS|"
448		"STATX_BTIME|0xfffff000");
449
450	SET_MASK_INVOKE(STATX_UID, "STATX_UID");
451
452	/* ...and with mask. */
453	TEST_SYSCALL_STATX_MASK = old_mask;
454	TEST_SYSCALL_STATX_MASK_STR = old_mask_str;
455
456# endif /* IS_STATX */
457
458	puts("+++ exited with 0 +++");
459	return 0;
460}
461
462#else
463
464SKIP_MAIN_UNDEFINED("HAVE_FTRUNCATE && HAVE_FUTIMENS")
465
466#endif
467