xref: /illumos-gate/usr/src/uts/common/syscall/stat.c (revision 36b60d35)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * Portions of this source code were derived from Berkeley 4.3 BSD
31  * under license from the Regents of the University of California.
32  */
33 
34 /*
35  * Get file attribute information through a file name or a file descriptor.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/isa_defs.h>
40 #include <sys/types.h>
41 #include <sys/sysmacros.h>
42 #include <sys/cred.h>
43 #include <sys/systm.h>
44 #include <sys/errno.h>
45 #include <sys/fcntl.h>
46 #include <sys/pathname.h>
47 #include <sys/stat.h>
48 #include <sys/vfs.h>
49 #include <sys/vnode.h>
50 #include <sys/mode.h>
51 #include <sys/file.h>
52 #include <sys/proc.h>
53 #include <sys/uio.h>
54 #include <sys/debug.h>
55 #include <sys/cmn_err.h>
56 #include <c2/audit.h>
57 #include <fs/fs_subr.h>
58 
59 /*
60  * =========================================================
61  * "ROUND_TO_USEC" workaround for missing syscall interface.
62  * =========================================================
63  *
64  * Solaris does not provide interface to set the timestamp of a file in
65  * nanosecond granularity. POSIX.1-2008 specifies such syscall interface:
66  * futimens(), utimensat(). The modern filesystems like ZFS support
67  * nanosecond granular timestamps.
68  *
69  * Before the workaround was implemented:
70  *
71  * The timestamps were read with the nanosecond granularity but written
72  * only with the microsecond granularity. If the timestamp was copied by
73  * reading it with nanosecond granularity and by writing with the microsecond
74  * granularity, the nanosecond part of the timestamp was zero.
75  *
76  * Example: such copying of the timestamp is done by touch(1) if the '-r'
77  * option is used. 'touch -r' is used by build procuderes based on make(1S).
78  * The missing nanosecond part was breaking build procuderes and resulted in
79  * hard to diagnose build failures.
80  *
81  * After the workaround is implemented:
82  *
83  * The timestamps are in the kernel still stored with the nanosecond
84  * granularity. However, all the 'stat' syscalls now clear the nanosecond part
85  * in the timestamp values returned by these syscalls.
86  *
87  * The "ROUND_TO_USEC" workaround should be removed when the new syscall
88  * interface is available.
89  */
90 int stat_force_usec_granularity = 1;
91 
92 #define	ROUND_TO_USEC(tms)						\
93 {									\
94 	(tms)->tv_nsec = (long)(((tms)->tv_nsec / 1000) * 1000);	\
95 }
96 
97 /*
98  * Get the vp to be stated and the cred to be used for the call
99  * to VOP_GETATTR
100  */
101 
102 /*
103  * nmflag has the following values
104  *
105  * 1 - Always do lookup.  i.e. stat, lstat.
106  * 2 - Name is optional i.e. fstatat
107  * 0 - Don't lookup name, vp is in file_p. i.e. fstat
108  *
109  */
110 static int
111 cstatat_getvp(int fd, char *name, int nmflag,
112     int follow, vnode_t **vp, cred_t **cred)
113 {
114 	vnode_t *startvp;
115 	file_t *fp;
116 	int error;
117 	cred_t *cr;
118 	int estale_retry = 0;
119 
120 	*vp = NULL;
121 
122 	/*
123 	 * Only return EFAULT for fstatat when fd == AT_FDCWD && name == NULL
124 	 */
125 
126 	if (fd == AT_FDCWD) {
127 		if (name != NULL || nmflag != 2) {
128 			startvp = NULL;
129 			cr = CRED();
130 			crhold(cr);
131 		} else
132 			return (EFAULT);
133 	} else {
134 		char startchar;
135 
136 		if (nmflag == 1 || (nmflag == 2 && name != NULL)) {
137 			if (copyin(name, &startchar, sizeof (char)))
138 				return (EFAULT);
139 		} else {
140 			startchar = '\0';
141 		}
142 		if (startchar != '/' || nmflag == 0) {
143 			if ((fp = getf(fd)) == NULL) {
144 				return (EBADF);
145 			}
146 			startvp = fp->f_vnode;
147 			cr = fp->f_cred;
148 			crhold(cr);
149 			VN_HOLD(startvp);
150 			releasef(fd);
151 		} else {
152 			startvp = NULL;
153 			cr = CRED();
154 			crhold(cr);
155 		}
156 	}
157 	*cred = cr;
158 
159 	if (audit_active)
160 		audit_setfsat_path(1);
161 
162 
163 	if (nmflag == 1 || (nmflag == 2 && name != NULL)) {
164 lookup:
165 		if (error = lookupnameat(name, UIO_USERSPACE, follow, NULLVPP,
166 		    vp, startvp)) {
167 			if ((error == ESTALE) &&
168 			    fs_need_estale_retry(estale_retry++))
169 				goto lookup;
170 			if (startvp != NULL)
171 				VN_RELE(startvp);
172 			crfree(cr);
173 			return (error);
174 		}
175 		if (startvp != NULL)
176 			VN_RELE(startvp);
177 	} else {
178 		*vp = startvp;
179 	}
180 
181 	return (0);
182 }
183 
184 /*
185  * Native syscall interfaces:
186  *
187  * N-bit kernel, N-bit applications, N-bit file offsets
188  */
189 
190 static int cstatat(int, char *, int, struct stat *, int, int);
191 static int cstat(vnode_t *vp, struct stat *, int, cred_t *);
192 
193 int
194 stat(char *fname, struct stat *sb)
195 {
196 	return (cstatat(AT_FDCWD, fname, 1, sb, 0, ATTR_REAL));
197 }
198 
199 int
200 lstat(char *fname, struct stat *sb)
201 {
202 	return (cstatat(AT_FDCWD, fname, 1, sb, AT_SYMLINK_NOFOLLOW, 0));
203 }
204 
205 /*
206  * fstat can and should be fast, do an inline implementation here.
207  */
208 #define	FSTAT_BODY(fd, sb, statfn)				\
209 	{							\
210 		file_t *fp;					\
211 		int error;					\
212 								\
213 		if ((fp = getf(fd)) == NULL)			\
214 			return (set_errno(EBADF));		\
215 		if (audit_active)				\
216 			audit_setfsat_path(1);			\
217 		error = statfn(fp->f_vnode, sb, 0, fp->f_cred);	\
218 		releasef(fd);					\
219 		if (error)					\
220 			return (set_errno(error));		\
221 		return (0);					\
222 	}
223 
224 int
225 fstat(int fd, struct stat *sb)
226 {
227 	FSTAT_BODY(fd, sb, cstat)
228 }
229 
230 int
231 fstatat(int fd, char *name, struct stat *sb, int flags)
232 {
233 	return (cstatat(fd, name, 2, sb,
234 	    flags & AT_SYMLINK_NOFOLLOW ? AT_SYMLINK_NOFOLLOW : 0,
235 	    flags & _AT_TRIGGER ? ATTR_TRIGGER : 0));
236 }
237 
238 #if defined(__i386) || defined(__i386_COMPAT)
239 
240 /*
241  * Handle all the "extended" stat operations in the same way;
242  * validate the version, then call the real handler.
243  */
244 
245 #define	XSTAT_BODY(ver, f, s, fn)			\
246 	return (ver != _STAT_VER ? set_errno(EINVAL) : fn(f, s));
247 
248 #endif	/* __i386 || __i386_COMPAT */
249 
250 #if defined(__i386)
251 
252 /*
253  * Syscalls for i386 applications that issue {,l,f}xstat() directly
254  */
255 int
256 xstat(int version, char *fname, struct stat *sb)
257 {
258 	XSTAT_BODY(version, fname, sb, stat)
259 }
260 
261 int
262 lxstat(int version, char *fname, struct stat *sb)
263 {
264 	XSTAT_BODY(version, fname, sb, lstat)
265 }
266 
267 int
268 fxstat(int version, int fd, struct stat *sb)
269 {
270 	XSTAT_BODY(version, fd, sb, fstat)
271 }
272 
273 #endif	/* __i386 */
274 
275 /*
276  * Common code for stat(), lstat(), and fstat().
277  * (32-bit kernel, 32-bit applications, 32-bit files)
278  * (64-bit kernel, 64-bit applications, 64-bit files)
279  */
280 static int
281 cstat(vnode_t *vp, struct stat *ubp, int flag, cred_t *cr)
282 {
283 	struct vfssw *vswp;
284 	struct stat sb;
285 	vattr_t vattr;
286 	int error;
287 
288 	vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE;
289 	if ((error = VOP_GETATTR(vp, &vattr, flag, cr, NULL)) != 0)
290 		return (error);
291 
292 	/* Workaround - see beginning of the file for the details */
293 	if (stat_force_usec_granularity) {
294 		ROUND_TO_USEC(&(vattr.va_atime));
295 		ROUND_TO_USEC(&(vattr.va_mtime));
296 		ROUND_TO_USEC(&(vattr.va_ctime));
297 	}
298 
299 #ifdef	_ILP32
300 	/*
301 	 * (32-bit kernel, 32-bit applications, 32-bit files)
302 	 * NOTE: 32-bit kernel maintains a 64-bit unsigend va_size.
303 	 *
304 	 * st_size of devices (VBLK and VCHR special files) is a special case.
305 	 * POSIX does not define size behavior for special files, so the
306 	 * following Solaris specific behavior is not a violation. Solaris
307 	 * returns the size of the device.
308 	 *
309 	 * For compatibility with 32-bit programs which happen to do stat() on
310 	 * a (mknod) bigger than 2GB we suppress the large file EOVERFLOW and
311 	 * instead we return the value MAXOFF32_T (LONG_MAX).
312 	 *
313 	 * 32-bit applications that care about the size of devices should be
314 	 * built 64-bit or use a large file interface (lfcompile(5) or lf64(5)).
315 	 */
316 	if ((vattr.va_size > MAXOFF32_T) &&
317 	    ((vp->v_type == VBLK) || (vp->v_type == VCHR))) {
318 		/* OVERFLOW | UNKNOWN_SIZE */
319 		vattr.va_size = MAXOFF32_T;
320 	}
321 #endif	/* _ILP32 */
322 	if (vattr.va_size > MAXOFF_T || vattr.va_nblocks > LONG_MAX ||
323 	    vattr.va_nodeid > ULONG_MAX)
324 		return (EOVERFLOW);
325 
326 	bzero(&sb, sizeof (sb));
327 	sb.st_dev = vattr.va_fsid;
328 	sb.st_ino = (ino_t)vattr.va_nodeid;
329 	sb.st_mode = VTTOIF(vattr.va_type) | vattr.va_mode;
330 	sb.st_nlink = vattr.va_nlink;
331 	sb.st_uid = vattr.va_uid;
332 	sb.st_gid = vattr.va_gid;
333 	sb.st_rdev = vattr.va_rdev;
334 	sb.st_size = (off_t)vattr.va_size;
335 	sb.st_atim = vattr.va_atime;
336 	sb.st_mtim = vattr.va_mtime;
337 	sb.st_ctim = vattr.va_ctime;
338 	sb.st_blksize = vattr.va_blksize;
339 	sb.st_blocks = (blkcnt_t)vattr.va_nblocks;
340 	if (vp->v_vfsp != NULL) {
341 		vswp = &vfssw[vp->v_vfsp->vfs_fstype];
342 		if (vswp->vsw_name && *vswp->vsw_name)
343 			(void) strcpy(sb.st_fstype, vswp->vsw_name);
344 	}
345 	if (copyout(&sb, ubp, sizeof (sb)))
346 		return (EFAULT);
347 	return (0);
348 }
349 
350 static int
351 cstatat(int fd, char *name, int nmflag, struct stat *sb, int follow, int flags)
352 {
353 	vnode_t *vp;
354 	int error;
355 	cred_t *cred;
356 	int link_follow;
357 	int estale_retry = 0;
358 
359 	link_follow = (follow == AT_SYMLINK_NOFOLLOW) ? NO_FOLLOW : FOLLOW;
360 lookup:
361 	if (error = cstatat_getvp(fd, name, nmflag, link_follow, &vp, &cred))
362 		return (set_errno(error));
363 	error = cstat(vp, sb, flags, cred);
364 	crfree(cred);
365 	VN_RELE(vp);
366 out:
367 	if (error != 0) {
368 		if (error == ESTALE &&
369 		    fs_need_estale_retry(estale_retry++) &&
370 		    (nmflag == 1 || (nmflag == 2 && name != NULL)))
371 			goto lookup;
372 		return (set_errno(error));
373 	}
374 	return (0);
375 }
376 
377 #if defined(_SYSCALL32_IMPL)
378 
379 /*
380  * 64-bit kernel, 32-bit applications, 32-bit file offsets
381  */
382 static int cstatat32(int, char *, int, struct stat32 *, int, int);
383 static int cstat32(vnode_t *, struct stat32 *, int, cred_t *);
384 int
385 stat32(char *fname, struct stat32 *sb)
386 {
387 	return (cstatat32(AT_FDCWD, fname, 1, sb, 0, ATTR_REAL));
388 }
389 
390 int
391 lstat32(char *fname, struct stat32 *sb)
392 {
393 	return (cstatat32(AT_FDCWD, fname, 1, sb, AT_SYMLINK_NOFOLLOW, 0));
394 }
395 
396 int
397 fstat32(int fd, struct stat32 *sb)
398 {
399 	FSTAT_BODY(fd, sb, cstat32)
400 }
401 
402 int
403 fstatat32(int fd, char *name, struct stat32 *sb, int flags)
404 {
405 	return (cstatat32(fd, name, 2, sb,
406 	    flags & AT_SYMLINK_NOFOLLOW ? AT_SYMLINK_NOFOLLOW : 0,
407 	    flags & _AT_TRIGGER ? ATTR_TRIGGER : 0));
408 }
409 
410 #if defined(__i386_COMPAT)
411 
412 /*
413  * Syscalls for i386 applications that issue {,l,f}xstat() directly
414  */
415 int
416 xstat32(int version, char *fname, struct stat32 *sb)
417 {
418 	XSTAT_BODY(version, fname, sb, stat32)
419 }
420 
421 int
422 lxstat32(int version, char *fname, struct stat32 *sb)
423 {
424 	XSTAT_BODY(version, fname, sb, lstat32)
425 }
426 
427 int
428 fxstat32(int version, int fd, struct stat32 *sb)
429 {
430 	XSTAT_BODY(version, fd, sb, fstat32)
431 }
432 
433 #endif	/* __i386_COMPAT */
434 
435 static int
436 cstat32(vnode_t *vp, struct stat32 *ubp, int flag, struct cred *cr)
437 {
438 	struct vfssw *vswp;
439 	struct stat32 sb;
440 	vattr_t vattr;
441 	int error;
442 	dev32_t st_dev, st_rdev;
443 
444 	vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE;
445 	if (error = VOP_GETATTR(vp, &vattr, flag, cr, NULL))
446 		return (error);
447 
448 	/* Workaround - see beginning of the file for the details */
449 	if (stat_force_usec_granularity) {
450 		ROUND_TO_USEC(&(vattr.va_atime));
451 		ROUND_TO_USEC(&(vattr.va_mtime));
452 		ROUND_TO_USEC(&(vattr.va_ctime));
453 	}
454 
455 	/* devices are a special case, see comments in cstat */
456 	if ((vattr.va_size > MAXOFF32_T) &&
457 	    ((vp->v_type == VBLK) || (vp->v_type == VCHR))) {
458 		/* OVERFLOW | UNKNOWN_SIZE */
459 		vattr.va_size = MAXOFF32_T;
460 	}
461 
462 	/* check for large values */
463 	if (!cmpldev(&st_dev, vattr.va_fsid) ||
464 	    !cmpldev(&st_rdev, vattr.va_rdev) ||
465 	    vattr.va_size > MAXOFF32_T ||
466 	    vattr.va_nblocks > INT32_MAX ||
467 	    vattr.va_nodeid > UINT32_MAX ||
468 	    TIMESPEC_OVERFLOW(&(vattr.va_atime)) ||
469 	    TIMESPEC_OVERFLOW(&(vattr.va_mtime)) ||
470 	    TIMESPEC_OVERFLOW(&(vattr.va_ctime)))
471 		return (EOVERFLOW);
472 
473 	bzero(&sb, sizeof (sb));
474 	sb.st_dev = st_dev;
475 	sb.st_ino = (ino32_t)vattr.va_nodeid;
476 	sb.st_mode = VTTOIF(vattr.va_type) | vattr.va_mode;
477 	sb.st_nlink = vattr.va_nlink;
478 	sb.st_uid = vattr.va_uid;
479 	sb.st_gid = vattr.va_gid;
480 	sb.st_rdev = st_rdev;
481 	sb.st_size = (off32_t)vattr.va_size;
482 	TIMESPEC_TO_TIMESPEC32(&(sb.st_atim), &(vattr.va_atime));
483 	TIMESPEC_TO_TIMESPEC32(&(sb.st_mtim), &(vattr.va_mtime));
484 	TIMESPEC_TO_TIMESPEC32(&(sb.st_ctim), &(vattr.va_ctime));
485 	sb.st_blksize = vattr.va_blksize;
486 	sb.st_blocks = (blkcnt32_t)vattr.va_nblocks;
487 	if (vp->v_vfsp != NULL) {
488 		vswp = &vfssw[vp->v_vfsp->vfs_fstype];
489 		if (vswp->vsw_name && *vswp->vsw_name)
490 			(void) strcpy(sb.st_fstype, vswp->vsw_name);
491 	}
492 	if (copyout(&sb, ubp, sizeof (sb)))
493 		return (EFAULT);
494 	return (0);
495 }
496 
497 static int
498 cstatat32(int fd, char *name, int nmflag, struct stat32 *sb,
499     int follow, int flags)
500 {
501 	vnode_t *vp;
502 	int error;
503 	cred_t *cred;
504 	int link_follow;
505 	int estale_retry = 0;
506 
507 	link_follow = (follow == AT_SYMLINK_NOFOLLOW) ? NO_FOLLOW : FOLLOW;
508 lookup:
509 	if (error = cstatat_getvp(fd, name, nmflag, link_follow, &vp, &cred))
510 		return (set_errno(error));
511 	error = cstat32(vp, sb, flags, cred);
512 	crfree(cred);
513 	VN_RELE(vp);
514 out:
515 	if (error != 0) {
516 		if (error == ESTALE &&
517 		    fs_need_estale_retry(estale_retry++) &&
518 		    (nmflag == 1 || (nmflag == 2 && name != NULL)))
519 			goto lookup;
520 		return (set_errno(error));
521 	}
522 	return (0);
523 }
524 
525 #endif	/* _SYSCALL32_IMPL */
526 
527 #if defined(_ILP32)
528 
529 /*
530  * 32-bit kernel, 32-bit applications, 64-bit file offsets.
531  *
532  * These routines are implemented differently on 64-bit kernels.
533  */
534 static int cstatat64(int, char *, int, struct stat64 *, int, int);
535 static int cstat64(vnode_t *, struct stat64 *, int, cred_t *);
536 
537 int
538 stat64(char *fname, struct stat64 *sb)
539 {
540 	return (cstatat64(AT_FDCWD, fname, 1, sb, 0, ATTR_REAL));
541 }
542 
543 int
544 lstat64(char *fname, struct stat64 *sb)
545 {
546 	return (cstatat64(AT_FDCWD, fname, 1, sb, AT_SYMLINK_NOFOLLOW, 0));
547 }
548 
549 int
550 fstat64(int fd, struct stat64 *sb)
551 {
552 	FSTAT_BODY(fd, sb, cstat64)
553 }
554 
555 int
556 fstatat64(int fd, char *name, struct stat64 *sb, int flags)
557 {
558 	return (cstatat64(fd, name, 2, sb,
559 	    flags & AT_SYMLINK_NOFOLLOW ? AT_SYMLINK_NOFOLLOW : 0,
560 	    flags & _AT_TRIGGER ? ATTR_TRIGGER : 0));
561 }
562 
563 static int
564 cstat64(vnode_t *vp, struct stat64 *ubp, int flag, cred_t *cr)
565 {
566 	struct vfssw *vswp;
567 	struct stat64 lsb;
568 	vattr_t vattr;
569 	int error;
570 
571 	vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE;
572 	if (error = VOP_GETATTR(vp, &vattr, flag, cr, NULL))
573 		return (error);
574 
575 	/* Workaround - see beginning of the file for the details */
576 	if (stat_force_usec_granularity) {
577 		ROUND_TO_USEC(&(vattr.va_atime));
578 		ROUND_TO_USEC(&(vattr.va_mtime));
579 		ROUND_TO_USEC(&(vattr.va_ctime));
580 	}
581 
582 	bzero(&lsb, sizeof (lsb));
583 	lsb.st_dev = vattr.va_fsid;
584 	lsb.st_ino = vattr.va_nodeid;
585 	lsb.st_mode = VTTOIF(vattr.va_type) | vattr.va_mode;
586 	lsb.st_nlink = vattr.va_nlink;
587 	lsb.st_uid = vattr.va_uid;
588 	lsb.st_gid = vattr.va_gid;
589 	lsb.st_rdev = vattr.va_rdev;
590 	lsb.st_size = vattr.va_size;
591 	lsb.st_atim = vattr.va_atime;
592 	lsb.st_mtim = vattr.va_mtime;
593 	lsb.st_ctim = vattr.va_ctime;
594 	lsb.st_blksize = vattr.va_blksize;
595 	lsb.st_blocks = vattr.va_nblocks;
596 	if (vp->v_vfsp != NULL) {
597 		vswp = &vfssw[vp->v_vfsp->vfs_fstype];
598 		if (vswp->vsw_name && *vswp->vsw_name)
599 			(void) strcpy(lsb.st_fstype, vswp->vsw_name);
600 	}
601 	if (copyout(&lsb, ubp, sizeof (lsb)))
602 		return (EFAULT);
603 	return (0);
604 }
605 
606 static int
607 cstatat64(int fd, char *name, int nmflag, struct stat64 *sb,
608     int follow, int flags)
609 {
610 	vnode_t *vp;
611 	int error;
612 	cred_t *cred;
613 	int link_follow;
614 	int estale_retry = 0;
615 
616 	link_follow = (follow == AT_SYMLINK_NOFOLLOW) ? NO_FOLLOW : FOLLOW;
617 lookup:
618 	if (error = cstatat_getvp(fd, name, nmflag, link_follow, &vp, &cred))
619 		return (set_errno(error));
620 	error = cstat64(vp, sb, flags, cred);
621 	crfree(cred);
622 	VN_RELE(vp);
623 out:
624 	if (error != 0) {
625 		if (error == ESTALE &&
626 		    fs_need_estale_retry(estale_retry++) &&
627 		    (nmflag == 1 || (nmflag == 2 && name != NULL)))
628 			goto lookup;
629 		return (set_errno(error));
630 	}
631 	return (0);
632 }
633 
634 #endif	/* _ILP32 */
635 
636 #if defined(_SYSCALL32_IMPL)
637 
638 /*
639  * 64-bit kernel, 32-bit applications, 64-bit file offsets.
640  *
641  * We'd really like to call the "native" stat calls for these ones,
642  * but the problem is that the 64-bit ABI defines the 'stat64' structure
643  * differently from the way the 32-bit ABI defines it.
644  */
645 
646 static int cstatat64_32(int, char *, int, struct stat64_32 *, int, int);
647 static int cstat64_32(vnode_t *, struct stat64_32 *, int, cred_t *);
648 
649 int
650 stat64_32(char *fname, struct stat64_32 *sb)
651 {
652 	return (cstatat64_32(AT_FDCWD, fname, 1, sb, 0, ATTR_REAL));
653 }
654 
655 int
656 lstat64_32(char *fname, struct stat64_32 *sb)
657 {
658 	return (cstatat64_32(AT_FDCWD, fname, 1, sb, AT_SYMLINK_NOFOLLOW, 0));
659 }
660 
661 int
662 fstat64_32(int fd, struct stat64_32 *sb)
663 {
664 	FSTAT_BODY(fd, sb, cstat64_32)
665 }
666 
667 int
668 fstatat64_32(int fd, char *name, struct stat64_32 *sb, int flags)
669 {
670 	return (cstatat64_32(fd, name, 2, sb,
671 	    flags & AT_SYMLINK_NOFOLLOW ? AT_SYMLINK_NOFOLLOW : 0,
672 	    flags & _AT_TRIGGER ? ATTR_TRIGGER : 0));
673 }
674 
675 static int
676 cstat64_32(vnode_t *vp, struct stat64_32 *ubp, int flag, cred_t *cr)
677 {
678 	struct vfssw *vswp;
679 	struct stat64_32 lsb;
680 	vattr_t vattr;
681 	int error;
682 	dev32_t st_dev, st_rdev;
683 
684 	vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE;
685 	if (error = VOP_GETATTR(vp, &vattr, flag, cr, NULL))
686 		return (error);
687 
688 	/* Workaround - see beginning of the file for the details */
689 	if (stat_force_usec_granularity) {
690 		ROUND_TO_USEC(&(vattr.va_atime));
691 		ROUND_TO_USEC(&(vattr.va_mtime));
692 		ROUND_TO_USEC(&(vattr.va_ctime));
693 	}
694 
695 	if (!cmpldev(&st_dev, vattr.va_fsid) ||
696 	    !cmpldev(&st_rdev, vattr.va_rdev) ||
697 	    TIMESPEC_OVERFLOW(&(vattr.va_atime)) ||
698 	    TIMESPEC_OVERFLOW(&(vattr.va_mtime)) ||
699 	    TIMESPEC_OVERFLOW(&(vattr.va_ctime)))
700 		return (EOVERFLOW);
701 
702 	bzero(&lsb, sizeof (lsb));
703 	lsb.st_dev = st_dev;
704 	lsb.st_ino = vattr.va_nodeid;
705 	lsb.st_mode = VTTOIF(vattr.va_type) | vattr.va_mode;
706 	lsb.st_nlink = vattr.va_nlink;
707 	lsb.st_uid = vattr.va_uid;
708 	lsb.st_gid = vattr.va_gid;
709 	lsb.st_rdev = st_rdev;
710 	lsb.st_size = vattr.va_size;
711 	TIMESPEC_TO_TIMESPEC32(&(lsb.st_atim), &(vattr.va_atime));
712 	TIMESPEC_TO_TIMESPEC32(&(lsb.st_mtim), &(vattr.va_mtime));
713 	TIMESPEC_TO_TIMESPEC32(&(lsb.st_ctim), &(vattr.va_ctime));
714 	lsb.st_blksize = vattr.va_blksize;
715 	lsb.st_blocks = vattr.va_nblocks;
716 	if (vp->v_vfsp != NULL) {
717 		vswp = &vfssw[vp->v_vfsp->vfs_fstype];
718 		if (vswp->vsw_name && *vswp->vsw_name)
719 			(void) strcpy(lsb.st_fstype, vswp->vsw_name);
720 	}
721 	if (copyout(&lsb, ubp, sizeof (lsb)))
722 		return (EFAULT);
723 	return (0);
724 }
725 
726 static int
727 cstatat64_32(int fd, char *name, int nmflag, struct stat64_32 *sb,
728     int follow, int flags)
729 {
730 	vnode_t  *vp;
731 	int error;
732 	cred_t *cred;
733 	int link_follow;
734 	int estale_retry = 0;
735 
736 	link_follow = (follow == AT_SYMLINK_NOFOLLOW) ? NO_FOLLOW : FOLLOW;
737 lookup:
738 	if (error = cstatat_getvp(fd, name, nmflag, link_follow, &vp, &cred))
739 		return (set_errno(error));
740 	error = cstat64_32(vp, sb, flags, cred);
741 	crfree(cred);
742 	VN_RELE(vp);
743 out:
744 	if (error != 0) {
745 		if (error == ESTALE &&
746 		    fs_need_estale_retry(estale_retry++) &&
747 		    (nmflag == 1 || (nmflag == 2 && name != NULL)))
748 			goto lookup;
749 		return (set_errno(error));
750 	}
751 	return (0);
752 }
753 
754 #endif /* _SYSCALL32_IMPL */
755