git: 5e424fec72aa - stable/13 - linux(4): Uniformly dev_t arguments translation
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 29 Jun 2023 08:20:28 UTC
The branch stable/13 has been updated by dchagin: URL: https://cgit.FreeBSD.org/src/commit/?id=5e424fec72aafe1154ae23cd0674193e757a4d2a commit 5e424fec72aafe1154ae23cd0674193e757a4d2a Author: Dmitry Chagin <dchagin@FreeBSD.org> AuthorDate: 2023-04-28 08:55:05 +0000 Commit: Dmitry Chagin <dchagin@FreeBSD.org> CommitDate: 2023-06-29 08:15:49 +0000 linux(4): Uniformly dev_t arguments translation The two main uses of dev_t are in struct stat and as a parameter of the mknod system calls. As of version 2.6.0 of the Linux kernel, dev_t is a 32-bit quantity with 12 bits set asaid for the major number and 20 for the minor number. The in-kernel dev_t encoded as MMMmmmmm, where M is a hex digit of the major number and m is a hex digit of the minor number. The user-space dev_t encoded as mmmM MMmm, where M and m is the major and minor numbers accordingly. This is downward compatible with legacy systems where dev_t is 16 bits wide, encoded as MMmm. In glibc dev_t is a 64-bit quantity, with 32-bit major and minor numbers, encoded as MMMM Mmmm mmmM MMmm. This is downward compatible with the Linux kernel and with legacy systems where dev_t is 16 bits wide. In the FreeBSD dev_t is a 64-bit quantity. The major and minor numbers are encoded as MMMmmmMm, therefore conversion of the device numbers between Linux user-space and FreeBSD kernel required. (cherry picked from commit 166e2e5a9e87d32dbfc7838903904673873f1e71) --- sys/compat/linux/linux.h | 68 ++++++++++++++++++++++++++++++++++++++++++ sys/compat/linux/linux_misc.c | 4 +-- sys/compat/linux/linux_stats.c | 49 ++++++++++++------------------ sys/compat/linux/linux_util.c | 3 +- 4 files changed, 91 insertions(+), 33 deletions(-) diff --git a/sys/compat/linux/linux.h b/sys/compat/linux/linux.h index 9061548399e4..bb70007cfc5b 100644 --- a/sys/compat/linux/linux.h +++ b/sys/compat/linux/linux.h @@ -33,6 +33,74 @@ */ typedef uint32_t l_dev_t; +/* + * Linux dev_t conversion routines. + * + * As of version 2.6.0 of the Linux kernel, dev_t is a 32-bit quantity + * with 12 bits set asaid for the major number and 20 for the minor number. + * The in-kernel dev_t encoded as MMMmmmmm, where M is a hex digit of the + * major number and m is a hex digit of the minor number. + * The user-space dev_t encoded as mmmM MMmm, where M and m is the major + * and minor numbers accordingly. This is downward compatible with legacy + * systems where dev_t is 16 bits wide, encoded as MMmm. + * In glibc dev_t is a 64-bit quantity, with 32-bit major and minor numbers, + * encoded as MMMM Mmmm mmmM MMmm. This is downward compatible with the Linux + * kernel and with legacy systems where dev_t is 16 bits wide. + * + * In the FreeBSD dev_t is a 64-bit quantity. The major and minor numbers + * are encoded as MMMmmmMm, therefore conversion of the device numbers between + * Linux user-space and FreeBSD kernel required. + */ +static __inline l_dev_t +linux_encode_dev(int _major, int _minor) +{ + + return ((_minor & 0xff) | ((_major & 0xfff) << 8) | + (((_minor & ~0xff) << 12) & 0xfff00000)); +} + +static __inline l_dev_t +linux_new_encode_dev(dev_t _dev) +{ + + return (_dev == NODEV ? 0 : linux_encode_dev(major(_dev), minor(_dev))); +} + +static __inline int +linux_encode_major(dev_t _dev) +{ + + return (_dev == NODEV ? 0 : major(_dev) & 0xfff); +} + +static __inline int +linux_encode_minor(dev_t _dev) +{ + + return (_dev == NODEV ? 0 : minor(_dev) & 0xfffff); +} + +static __inline int +linux_decode_major(l_dev_t _dev) +{ + + return ((_dev & 0xfff00) >> 8); +} + +static __inline int +linux_decode_minor(l_dev_t _dev) +{ + + return ((_dev & 0xff) | ((_dev & 0xfff00000) >> 12)); +} + +static __inline dev_t +linux_decode_dev(l_dev_t _dev) +{ + + return (makedev(linux_decode_major(_dev), linux_decode_minor(_dev))); +} + /* * Private Brandinfo flags */ diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c index ba0ac190a946..bc6ff9559493 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -901,7 +901,7 @@ linux_mknod(struct thread *td, struct linux_mknod_args *args) case S_IFCHR: case S_IFBLK: error = kern_mknodat(td, AT_FDCWD, path, seg, - args->mode, args->dev); + args->mode, linux_decode_dev(args->dev)); break; case S_IFDIR: @@ -956,7 +956,7 @@ linux_mknodat(struct thread *td, struct linux_mknodat_args *args) case S_IFCHR: case S_IFBLK: error = kern_mknodat(td, dfd, path, seg, args->mode, - args->dev); + linux_decode_dev(args->dev)); break; case S_IFDIR: diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c index 07ef72706d75..a5c1949a4d28 100644 --- a/sys/compat/linux/linux_stats.c +++ b/sys/compat/linux/linux_stats.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); #include <machine/../linux/linux_proto.h> #endif +#include <compat/linux/linux.h> #include <compat/linux/linux_file.h> #include <compat/linux/linux_util.h> @@ -139,38 +140,19 @@ linux_kern_lstat(struct thread *td, const char *path, enum uio_seg pathseg, } #endif -/* - * l_dev_t has the same encoding as dev_t in the latter's low 16 bits, so - * truncation of a dev_t to 16 bits gives the same result as unpacking - * using major() and minor() and repacking in the l_dev_t format. This - * detail is hidden in dev_to_ldev(). Overflow in conversions of dev_t's - * are not checked for, as for other fields. - * - * dev_to_ldev() is only used for translating st_dev. When we convert - * st_rdev for copying it out, it isn't really a dev_t, but has already - * been translated to an l_dev_t in a nontrivial way. Translating it - * again would be illogical but would have no effect since the low 16 - * bits have the same encoding. - * - * The nontrivial translation for st_rdev renumbers some devices, but not - * ones that can be mounted on, so it is consistent with the translation - * for st_dev except when the renumbering or truncation causes conflicts. - */ -#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 = dev_to_ldev(buf->st_dev); + tbuf.st_dev = linux_new_encode_dev(buf->st_dev); tbuf.st_ino = buf->st_ino; tbuf.st_mode = buf->st_mode; tbuf.st_nlink = buf->st_nlink; tbuf.st_uid = buf->st_uid; tbuf.st_gid = buf->st_gid; - tbuf.st_rdev = buf->st_rdev; + tbuf.st_rdev = linux_new_encode_dev(buf->st_rdev); tbuf.st_size = buf->st_size; tbuf.st_atim.tv_sec = buf->st_atim.tv_sec; tbuf.st_atim.tv_nsec = buf->st_atim.tv_nsec; @@ -239,19 +221,27 @@ linux_newfstat(struct thread *td, struct linux_newfstat_args *args) } #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32)) + +static __inline uint16_t +linux_old_encode_dev(dev_t _dev) +{ + + return (_dev == NODEV ? 0 : linux_encode_dev(major(_dev), minor(_dev))); +} + static int old_stat_copyout(struct stat *buf, void *ubuf) { struct l_old_stat lbuf; bzero(&lbuf, sizeof(lbuf)); - lbuf.st_dev = dev_to_ldev(buf->st_dev); + lbuf.st_dev = linux_old_encode_dev(buf->st_dev); lbuf.st_ino = buf->st_ino; lbuf.st_mode = buf->st_mode; lbuf.st_nlink = buf->st_nlink; lbuf.st_uid = buf->st_uid; lbuf.st_gid = buf->st_gid; - lbuf.st_rdev = buf->st_rdev; + lbuf.st_rdev = linux_old_encode_dev(buf->st_rdev); lbuf.st_size = MIN(buf->st_size, INT32_MAX); lbuf.st_atim.tv_sec = buf->st_atim.tv_sec; lbuf.st_atim.tv_nsec = buf->st_atim.tv_nsec; @@ -550,13 +540,13 @@ stat64_copyout(struct stat *buf, void *ubuf) struct l_stat64 lbuf; bzero(&lbuf, sizeof(lbuf)); - lbuf.st_dev = dev_to_ldev(buf->st_dev); + lbuf.st_dev = linux_new_encode_dev(buf->st_dev); lbuf.st_ino = buf->st_ino; lbuf.st_mode = buf->st_mode; lbuf.st_nlink = buf->st_nlink; lbuf.st_uid = buf->st_uid; lbuf.st_gid = buf->st_gid; - lbuf.st_rdev = buf->st_rdev; + lbuf.st_rdev = linux_new_encode_dev(buf->st_rdev); lbuf.st_size = buf->st_size; lbuf.st_atim.tv_sec = buf->st_atim.tv_sec; lbuf.st_atim.tv_nsec = buf->st_atim.tv_nsec; @@ -762,11 +752,10 @@ statx_copyout(struct stat *buf, void *ubuf) tbuf.stx_ctime.tv_nsec = buf->st_ctim.tv_nsec; tbuf.stx_mtime.tv_sec = buf->st_mtim.tv_sec; tbuf.stx_mtime.tv_nsec = buf->st_mtim.tv_nsec; - - tbuf.stx_rdev_major = buf->st_rdev >> 8; - tbuf.stx_rdev_minor = buf->st_rdev & 0xff; - tbuf.stx_dev_major = buf->st_dev >> 8; - tbuf.stx_dev_minor = buf->st_dev & 0xff; + tbuf.stx_rdev_major = linux_encode_major(buf->st_rdev); + tbuf.stx_rdev_minor = linux_encode_minor(buf->st_rdev); + tbuf.stx_dev_major = linux_encode_major(buf->st_dev); + tbuf.stx_dev_minor = linux_encode_minor(buf->st_dev); return (copyout(&tbuf, ubuf, sizeof(tbuf))); } diff --git a/sys/compat/linux/linux_util.c b/sys/compat/linux/linux_util.c index 5995ac5e18af..498320937fd3 100644 --- a/sys/compat/linux/linux_util.c +++ b/sys/compat/linux/linux_util.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/types.h> #include <sys/bus.h> #include <sys/conf.h> #include <sys/fcntl.h> @@ -248,7 +249,7 @@ translate_vnhook_major_minor(struct vnode *vp, struct stat *sb) sb->st_dev = rootdevmp->mnt_stat.f_fsid.val[0]; if (linux_vn_get_major_minor(vp, &major, &minor) == 0) - sb->st_rdev = (major << 8 | minor); + sb->st_rdev = makedev(major, minor); } char *