Add sysctl vfs.ino64_trunc_error controlling action on truncating

inode number or link count for the ABI compat binaries.

Right now, and by default after the change, too large 64bit values are
silently truncated to 32 bits.  Enabling the knob causes the system to
return EOVERFLOW for stat(2) family of compat syscalls when some
values cannot be completely represented by the old structures.  For
getdirentries(2), knob skips the dirents which would cause non-trivial
truncation of d_ino.

EOVERFLOW error is specified by the X/Open 1996 LFS document
('Adding Support for Arbitrary File Sizes to the Single UNIX
Specification').

Based on the discussion with:	bde
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Konstantin Belousov 2017-06-05 11:40:30 +00:00
parent 77c97f893a
commit 3df7ebc4ed
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=319600
4 changed files with 54 additions and 26 deletions

View File

@ -1904,12 +1904,18 @@ freebsd32_fhstat(struct thread *td, struct freebsd32_fhstat_args *uap)
}
#if defined(COMPAT_FREEBSD11)
static void
extern int ino64_trunc_error;
static int
freebsd11_cvtstat32(struct stat *in, struct freebsd11_stat32 *out)
{
CP(*in, *out, st_ino);
if (in->st_ino != out->st_ino && ino64_trunc_error)
return (EOVERFLOW);
CP(*in, *out, st_nlink);
if (in->st_nlink != out->st_nlink && ino64_trunc_error)
return (EOVERFLOW);
CP(*in, *out, st_dev);
CP(*in, *out, st_mode);
CP(*in, *out, st_uid);
@ -1928,6 +1934,7 @@ freebsd11_cvtstat32(struct stat *in, struct freebsd11_stat32 *out)
bzero((char *)&out->st_birthtim + sizeof(out->st_birthtim),
sizeof(*out) - offsetof(struct freebsd11_stat32,
st_birthtim) - sizeof(out->st_birthtim));
return (0);
}
int
@ -1942,8 +1949,9 @@ freebsd11_freebsd32_stat(struct thread *td,
&sb, NULL);
if (error != 0)
return (error);
freebsd11_cvtstat32(&sb, &sb32);
error = copyout(&sb32, uap->ub, sizeof (sb32));
error = freebsd11_cvtstat32(&sb, &sb32);
if (error == 0)
error = copyout(&sb32, uap->ub, sizeof (sb32));
return (error);
}
@ -1958,8 +1966,9 @@ freebsd11_freebsd32_fstat(struct thread *td,
error = kern_fstat(td, uap->fd, &sb);
if (error != 0)
return (error);
freebsd11_cvtstat32(&sb, &sb32);
error = copyout(&sb32, uap->ub, sizeof (sb32));
error = freebsd11_cvtstat32(&sb, &sb32);
if (error == 0)
error = copyout(&sb32, uap->ub, sizeof (sb32));
return (error);
}
@ -1975,8 +1984,9 @@ freebsd11_freebsd32_fstatat(struct thread *td,
&sb, NULL);
if (error != 0)
return (error);
freebsd11_cvtstat32(&sb, &sb32);
error = copyout(&sb32, uap->buf, sizeof (sb32));
error = freebsd11_cvtstat32(&sb, &sb32);
if (error == 0)
error = copyout(&sb32, uap->buf, sizeof (sb32));
return (error);
}
@ -1990,10 +2000,11 @@ freebsd11_freebsd32_lstat(struct thread *td,
error = kern_statat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->path,
UIO_USERSPACE, &sb, NULL);
if (error)
if (error != 0)
return (error);
freebsd11_cvtstat32(&sb, &sb32);
error = copyout(&sb32, uap->ub, sizeof (sb32));
error = freebsd11_cvtstat32(&sb, &sb32);
if (error == 0)
error = copyout(&sb32, uap->ub, sizeof (sb32));
return (error);
}
@ -2012,8 +2023,9 @@ freebsd11_freebsd32_fhstat(struct thread *td,
error = kern_fhstat(td, fh, &sb);
if (error != 0)
return (error);
freebsd11_cvtstat32(&sb, &sb32);
error = copyout(&sb32, uap->sb, sizeof (sb32));
error = freebsd11_cvtstat32(&sb, &sb32);
if (error == 0)
error = copyout(&sb32, uap->sb, sizeof (sb32));
return (error);
}
#endif

View File

@ -1315,8 +1315,9 @@ freebsd11_fstat(struct thread *td, struct freebsd11_fstat_args *uap)
error = kern_fstat(td, uap->fd, &sb);
if (error != 0)
return (error);
freebsd11_cvtstat(&sb, &osb);
error = copyout(&osb, uap->sb, sizeof(osb));
error = freebsd11_cvtstat(&sb, &osb);
if (error == 0)
error = copyout(&osb, uap->sb, sizeof(osb));
return (error);
}
#endif /* COMPAT_FREEBSD11 */

View File

@ -2107,14 +2107,22 @@ cvtstat(struct stat *st, struct ostat *ost)
#endif /* COMPAT_43 */
#if defined(COMPAT_FREEBSD11)
void
int ino64_trunc_error;
SYSCTL_INT(_vfs, OID_AUTO, ino64_trunc_error, CTLFLAG_RW,
&ino64_trunc_error, 0,
"Error on truncation of inode number, device id or link count");
int
freebsd11_cvtstat(struct stat *st, struct freebsd11_stat *ost)
{
ost->st_dev = st->st_dev;
ost->st_ino = st->st_ino; /* truncate */
ost->st_ino = st->st_ino;
if (ost->st_ino != st->st_ino && ino64_trunc_error)
return (EOVERFLOW);
ost->st_mode = st->st_mode;
ost->st_nlink = st->st_nlink; /* truncate */
ost->st_nlink = st->st_nlink;
if (ost->st_nlink != st->st_nlink && ino64_trunc_error)
return (EOVERFLOW);
ost->st_uid = st->st_uid;
ost->st_gid = st->st_gid;
ost->st_rdev = st->st_rdev;
@ -2131,6 +2139,7 @@ freebsd11_cvtstat(struct stat *st, struct freebsd11_stat *ost)
bzero((char *)&ost->st_birthtim + sizeof(ost->st_birthtim),
sizeof(*ost) - offsetof(struct freebsd11_stat,
st_birthtim) - sizeof(ost->st_birthtim));
return (0);
}
int
@ -2144,8 +2153,9 @@ freebsd11_stat(struct thread *td, struct freebsd11_stat_args* uap)
&sb, NULL);
if (error != 0)
return (error);
freebsd11_cvtstat(&sb, &osb);
error = copyout(&osb, uap->ub, sizeof(osb));
error = freebsd11_cvtstat(&sb, &osb);
if (error == 0)
error = copyout(&osb, uap->ub, sizeof(osb));
return (error);
}
@ -2160,8 +2170,9 @@ freebsd11_lstat(struct thread *td, struct freebsd11_lstat_args* uap)
UIO_USERSPACE, &sb, NULL);
if (error != 0)
return (error);
freebsd11_cvtstat(&sb, &osb);
error = copyout(&osb, uap->ub, sizeof(osb));
error = freebsd11_cvtstat(&sb, &osb);
if (error == 0)
error = copyout(&osb, uap->ub, sizeof(osb));
return (error);
}
@ -2179,8 +2190,9 @@ freebsd11_fhstat(struct thread *td, struct freebsd11_fhstat_args* uap)
error = kern_fhstat(td, fh, &sb);
if (error != 0)
return (error);
freebsd11_cvtstat(&sb, &osb);
error = copyout(&osb, uap->sb, sizeof(osb));
error = freebsd11_cvtstat(&sb, &osb);
if (error == 0)
error = copyout(&osb, uap->sb, sizeof(osb));
return (error);
}
@ -2195,8 +2207,9 @@ freebsd11_fstatat(struct thread *td, struct freebsd11_fstatat_args* uap)
UIO_USERSPACE, &sb, NULL);
if (error != 0)
return (error);
freebsd11_cvtstat(&sb, &osb);
error = copyout(&osb, uap->buf, sizeof(osb));
error = freebsd11_cvtstat(&sb, &osb);
if (error == 0)
error = copyout(&osb, uap->buf, sizeof(osb));
return (error);
}
#endif /* COMPAT_FREEBSD11 */
@ -3738,6 +3751,8 @@ freebsd11_kern_getdirentries(struct thread *td, int fd, char *ubuf, u_int count,
dstdp.d_type = dp->d_type;
dstdp.d_namlen = dp->d_namlen;
dstdp.d_fileno = dp->d_fileno; /* truncate */
if (dstdp.d_fileno != dp->d_fileno && ino64_trunc_error)
continue;
dstdp.d_reclen = sizeof(dstdp) - sizeof(dstdp.d_name) +
((dp->d_namlen + 1 + 3) &~ 3);
bcopy(dp->d_name, dstdp.d_name, dstdp.d_namlen);

View File

@ -610,7 +610,7 @@ void cache_purgevfs(struct mount *mp, bool force);
int change_dir(struct vnode *vp, struct thread *td);
void cvtstat(struct stat *st, struct ostat *ost);
void freebsd11_cvtnstat(struct stat *sb, struct nstat *nsb);
void freebsd11_cvtstat(struct stat *st, struct freebsd11_stat *ost);
int freebsd11_cvtstat(struct stat *st, struct freebsd11_stat *ost);
int getnewvnode(const char *tag, struct mount *mp, struct vop_vector *vops,
struct vnode **vpp);
void getnewvnode_reserve(u_int count);