diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 02313ae0fb77..2cfc985a3b1c 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -2127,11 +2127,27 @@ freebsd11_cvtstat32(struct stat *in, struct freebsd11_stat32 *out) break; } } - CP(*in, *out, st_dev); + out->st_dev = in->st_dev; + if (out->st_dev != in->st_dev) { + switch (ino64_trunc_error) { + default: + break; + case 1: + return (EOVERFLOW); + } + } CP(*in, *out, st_mode); CP(*in, *out, st_uid); CP(*in, *out, st_gid); - CP(*in, *out, st_rdev); + out->st_rdev = in->st_rdev; + if (out->st_rdev != in->st_rdev) { + switch (ino64_trunc_error) { + default: + break; + case 1: + return (EOVERFLOW); + } + } TS_CP(*in, *out, st_atim); TS_CP(*in, *out, st_mtim); TS_CP(*in, *out, st_ctim); diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c index 92b23c7dcc2d..0a8ee6e612b7 100644 --- a/sys/compat/linux/linux_stats.c +++ b/sys/compat/linux/linux_stats.c @@ -128,13 +128,30 @@ translate_fd_major_minor(struct thread *td, int fd, struct stat *buf) fdrop(fp, td); } +/* + * l_dev_t has the same encoding as dev_t in the latter's low 16 bits, so + * don't bother going through major() and minor(). Keep doing blind + * truncation, as for other fields. The previous version didn't even do + * blind truncation after dev_t was expanded to 64 bits. It failed to + * mask out bits 8-15 in minor(). These bits can only be nonzero in th + * 64-bit version. + * + * This is only used for st_dev. st_dev is for the mounted-on device so + * it can't be a device that needs very special translation. The translation + * of blind truncation is done here. st_rdev is supposed to be specially + * translated in callers, with the blind truncation done there too and + * st_rdev in the native struct state abused to hold the linux st_rdev. + * Callers do the last step using an open-coded Linux makedev(). + */ +#define dev_to_ldev(d) ((uint16_t)(d)) + static int newstat_copyout(struct stat *buf, void *ubuf) { struct l_newstat tbuf; bzero(&tbuf, sizeof(tbuf)); - tbuf.st_dev = minor(buf->st_dev) | (major(buf->st_dev) << 8); + tbuf.st_dev = dev_to_ldev(buf->st_dev); tbuf.st_ino = buf->st_ino; tbuf.st_mode = buf->st_mode; tbuf.st_nlink = buf->st_nlink; @@ -222,7 +239,7 @@ stat_copyout(struct stat *buf, void *ubuf) struct l_stat lbuf; bzero(&lbuf, sizeof(lbuf)); - lbuf.st_dev = buf->st_dev; + lbuf.st_dev = dev_to_ldev(buf->st_dev); lbuf.st_ino = buf->st_ino; lbuf.st_mode = buf->st_mode; lbuf.st_nlink = buf->st_nlink; @@ -524,7 +541,7 @@ stat64_copyout(struct stat *buf, void *ubuf) struct l_stat64 lbuf; bzero(&lbuf, sizeof(lbuf)); - lbuf.st_dev = minor(buf->st_dev) | (major(buf->st_dev) << 8); + lbuf.st_dev = dev_to_ldev(buf->st_dev); lbuf.st_ino = buf->st_ino; lbuf.st_mode = buf->st_mode; lbuf.st_nlink = buf->st_nlink; diff --git a/sys/fs/nfsclient/nfs_clcomsubs.c b/sys/fs/nfsclient/nfs_clcomsubs.c index 0f895d8a5bac..742a3622d6b0 100644 --- a/sys/fs/nfsclient/nfs_clcomsubs.c +++ b/sys/fs/nfsclient/nfs_clcomsubs.c @@ -430,8 +430,9 @@ nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap) NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR); nap->na_type = nfsv34tov_type(fp->fa_type); nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode); - nap->na_rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1), - fxdr_unsigned(u_char, fp->fa3_rdev.specdata2)); + nap->na_rdev = NFSMAKEDEV( + fxdr_unsigned(int, fp->fa3_rdev.specdata1), + fxdr_unsigned(int, fp->fa3_rdev.specdata2)); nap->na_nlink = fxdr_unsigned(uint32_t, fp->fa_nlink); nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid); nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index b661b8f34cb3..91d93bb89207 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -2094,12 +2094,25 @@ cvtstat(struct stat *st, struct ostat *ost) 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"); + "Error on truncation of device, file or inode number, or link count"); + int freebsd11_cvtstat(struct stat *st, struct freebsd11_stat *ost) { ost->st_dev = st->st_dev; + if (ost->st_dev != st->st_dev) { + switch (ino64_trunc_error) { + default: + /* + * Since dev_t is almost raw, don't clamp to the + * maximum for case 2, but ignore the error. + */ + break; + case 1: + return (EOVERFLOW); + } + } ost->st_ino = st->st_ino; if (ost->st_ino != st->st_ino) { switch (ino64_trunc_error) { @@ -2130,6 +2143,14 @@ freebsd11_cvtstat(struct stat *st, struct freebsd11_stat *ost) ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; + if (ost->st_rdev != st->st_rdev) { + switch (ino64_trunc_error) { + default: + break; + case 1: + return (EOVERFLOW); + } + } ost->st_atim = st->st_atim; ost->st_mtim = st->st_mtim; ost->st_ctim = st->st_ctim; diff --git a/sys/sys/types.h b/sys/sys/types.h index 9009cc9dfd2f..b90f88e292d5 100644 --- a/sys/sys/types.h +++ b/sys/sys/types.h @@ -366,9 +366,36 @@ __bitcount64(__uint64_t _x) #include -#define major(x) ((int)((dev_t)(x) >> 32)) -#define minor(x) ((int)(x)) -#define makedev(x, y) (((dev_t)(x) << 32) | (unsigned)(y)) +/* + * The major and minor numbers are encoded in dev_t as MMMmmmMm (where + * letters correspond to bytes). The encoding of the lower 4 bytes is + * constrained by compatibility with 16-bit and 32-bit dev_t's. The + * encoding of of the upper 4 bytes is the least unnatural one consistent + * with this and other constraints. Also, the decoding of the m bytes by + * minor() is unnatural to maximize compatibility subject to not discarding + * bits. The upper m byte is shifted into the position of the lower M byte + * instead of shifting 3 upper m bytes to close the gap. Compatibility for + * minor() is achieved iff the upper m byte is 0. + */ +#define major(d) __major(d) +static __inline int +__major(dev_t _d) +{ + return (((_d >> 32) & 0xffffff00) | ((_d >> 8) & 0xff)); +} +#define minor(d) __minor(d) +static __inline int +__minor(dev_t _d) +{ + return (((_d >> 24) & 0xff00) | (_d & 0xffff00ff)); +} +#define makedev(M, m) __makedev((M), (m)) +static __inline dev_t +__makedev(int _M, int _m) +{ + return (((dev_t)(_M & 0xffffff00) << 32) | ((_M & 0xff) << 8) | + ((dev_t)(_m & 0xff00) << 24) | (_m & 0xffff00ff)); +} /* * These declarations belong elsewhere, but are repeated here and in