我正在尝试编写一个处理ustar档案的可移植程序。对于设备文件,这些档案存储主要和次要设备号。但是,struct stat
在 POSIX 中列出的 仅包含使用“设备 ID(如果文件是字符或块特殊文件)”描述
st_rdev
的类型的单个成员。</p>dev_t
如何在一对主要设备号和次要设备号与以可移植方式st_rdev
返回的单个成员之间进行转换?stat()
我正在尝试编写一个处理ustar档案的可移植程序。对于设备文件,这些档案存储主要和次要设备号。但是,struct stat
在 POSIX 中列出的 仅包含使用“设备 ID(如果文件是字符或块特殊文件)”描述
st_rdev
的类型的单个成员。</p>dev_t
如何在一对主要设备号和次要设备号与以可移植方式st_rdev
返回的单个成员之间进行转换?stat()
虽然所有 POSIX 编程接口都按原样使用设备号(类型dev_t
),但 FUZxxl 在对此答案的评论中指出,常见的UStar文件格式(最常见的 tar 存档格式)确实将设备号分为主要和次要。(它们通常每个编码为七个八进制数字,因此出于兼容性原因,应该限制为 21 位无符号主要和 21 位无符号次要。这也意味着将设备号映射到仅主要或仅次要不是可靠的方法。)
以下包括扩展Jonathon Reinhart 的答案的文件,在网上挖掘各种系统手册页和文档(for makedev()
、major()
和minor()
)以及对此问题的评论之后。
#if defined(custom_makedev) && defined(custom_major) && defined(custom_minor)
/* Already defined */
#else
#undef custom_makedev
#undef custom_major
#undef custom_minor
#if defined(__linux__) || defined(__GLIBC__)
/* Linux, Android, and other systems using GNU C library */
#ifndef _BSD_SOURCE
#define _BSD_SOURCE 1
#endif
#include <sys/types.h>
#define custom_makedev(dmajor, dminor) makedev(dmajor, dminor)
#define custom_major(devnum) major(devnum)
#define custom_minor(devnum) minor(devnum)
#elif defined(_WIN32)
/* 32- and 64-bit Windows. VERIFY: These are just a guess! */
#define custom_makedev(dmajor, dminor) ((((unsigned int)dmajor << 8) & 0xFF00U) | ((unsigned int)dminor & 0xFFFF00FFU))
#define custom_major(devnum) (((unsigned int)devnum & 0xFF00U) >> 8)
#define custom_minor(devnum) ((unsigned int)devnum & 0xFFFF00FFU)
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
/* FreeBSD, OpenBSD, NetBSD, and DragonFlyBSD */
#include <sys/types.h>
#define custom_makedev(dmajor, dminor) makedev(dmajor, dminor)
#define custom_major(devnum) major(devnum)
#define custom_minor(devnum) minor(devnum)
#elif defined(__APPLE__) && defined(__MACH__)
/* Mac OS X */
#include <sys/types.h>
#define custom_makedev(dmajor, dminor) makedev(dmajor, dminor)
#define custom_major(devnum) major(devnum)
#define custom_minor(devnum) minor(devnum)
#elif defined(_AIX) || defined (__osf__)
/* AIX, OSF/1, Tru64 Unix */
#include <sys/types.h>
#define custom_makedev(dmajor, dminor) makedev(dmajor, dminor)
#define custom_major(devnum) major(devnum)
#define custom_minor(devnum) minor(devnum)
#elif defined(hpux)
/* HP-UX */
#include <sys/sysmacros.h>
#define custom_makedev(dmajor, dminor) makedev(dmajor, dminor)
#define custom_major(devnum) major(devnum)
#define custom_minor(devnum) minor(devnum)
#elif defined(sun)
/* Solaris */
#include <sys/types.h>
#include <sys/mkdev.h>
#define custom_makedev(dmajor, dminor) makedev(dmajor, dminor)
#define custom_major(devnum) major(devnum)
#define custom_minor(devnum) minor(devnum)
#else
/* Unknown OS. Try a the BSD approach. */
#ifndef _BSD_SOURCE
#define _BSD_SOURCE 1
#endif
#include <sys/types.h>
#if defined(makedev) && defined(major) && defined(minor)
#define custom_makedev(dmajor, dminor) makedev(dmajor, dminor)
#define custom_major(devnum) major(devnum)
#define custom_minor(devnum) minor(devnum)
#endif
#endif
#if !defined(custom_makedev) || !defined(custom_major) || !defined(custom_minor)
#error Unknown OS: please add definitions for custom_makedev(), custom_major(), and custom_minor(), for device number major/minor handling.
#endif
#endif
可以从现有的支持 UStar 格式的存档器中收集其他定义。在我看来,与每个操作系统/架构上的现有实现的兼容性是这里最重要的事情。
以上内容应涵盖所有使用 GNU C 库、Linux(包括 Android)、FreeBSD、OpenBSD、NetBSD、DragonFlyBSD、Mac OS X、AIX、Tru64、HP-UX 和 Solaris 的系统,以及在<sys/types.h>
包含时定义宏的任何系统。在 Windows 部分,我不确定。
据我了解,Windows 对所有普通文件使用设备 0,对设备使用HANDLE
(空指针类型)。我完全不确定上述逻辑在 Windows 上是否合理,但许多旧系统将设备编号的 8 个最低有效位放入次要位,将接下来的 8 位放入主要位,约定似乎是任何剩余位也将被放入(不转移)小调。检查现有的 UStar 格式的 tar 档案并参考设备会很有用,但我个人根本不使用 Windows。
如果未检测到系统,并且系统未使用 BSD 样式的包含来定义宏,则上述错误将导致停止编译。(我会亲自添加编译时机制,它可以帮助找到正确的标头定义,使用例如find
、xargs
和grep
,如果发生这种情况,建议也将添加内容发送到上游。touch empty.h ; cpp -dM empty.h ; rm -f empty.h
应该显示所有预定义的宏,以帮助识别操作系统和/或 C 库。)
最初,POSIX 声明它dev_t
必须是算术类型(因此,理论上,它可能是某些系统的某种变体float
)double
,但IEEE Std 1003.1,2013 版说它必须是整数类型。我敢打赌,这意味着没有已知的 POSIX-y 系统曾经使用过浮点dev_t
类型。看起来 Windows 使用 void 指针或HANDLE
类型,但 Windows 无论如何都不符合 POSIX。
定义后使用major()
andminor()
宏BSD_SOURCE
。
makedev()、major() 和 minor() 函数在 POSIX.1 中没有指定,但在许多其他系统上都有。
我有一个基于ls
Minix 的古董版本的程序,但从那时起被我修改了很多。它具有以下代码来检测主要和次要宏 - 以及一些关于它过去工作过的(现在)古董系统的评论。它假定有足够新的 GCC 版本可用于支持等。除非您明确包含它,否则您#pragma GCC diagnostic ignored
必须非常努力(例如clang -Weverything
)才能使该选项生效。-Wunused-macros
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-macros"
/* Defines to ensure major and minor macros are available */
#define _DARWIN_C_SOURCE /* In <sys/types.h> on MacOS X */
#define _BSD_SOURCE /* In <sys/sysmacros.h> via <sys/types.h> on Linux (Ubuntu 12.0.4) */
#define __EXTENSIONS__ /* Maybe beneficial on Solaris */
#pragma GCC diagnostic pop
/* From Solaris 2.6 sys/sysmacros.h
**
** WARNING: The device number macros defined here should not be used by
** device drivers or user software. [...] Application software should make
** use of the library routines available in makedev(3). [...] Macro
** routines bmajor(), major(), minor(), emajor(), eminor(), and makedev()
** will be removed or their definitions changed at the next major release
** following SVR4.
**
** #define O_BITSMAJOR 7 -- # of SVR3 major device bits
** #define O_BITSMINOR 8 -- # of SVR3 minor device bits
** #define O_MAXMAJ 0x7f -- SVR3 max major value
** #define O_MAXMIN 0xff -- SVR3 max major value
**
** #define L_BITSMAJOR 14 -- # of SVR4 major device bits
** #define L_BITSMINOR 18 -- # of SVR4 minor device bits
** #define L_MAXMAJ 0x3fff -- SVR4 max major value
** #define L_MAXMIN 0x3ffff -- MAX minor for 3b2 software drivers.
** -- For 3b2 hardware devices the minor is restricted to 256 (0-255)
*/
/* AC_HEADER_MAJOR:
** - defines MAJOR_IN_MKDEV if found in sys/mkdev.h
** - defines MAJOR_IN_SYSMACROS if found in sys/macros.h
** - otherwise, hope they are in sys/types.h
*/
#if defined MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#elif defined(MAJOR_MINOR_MACROS_IN_SYS_TYPES_H)
/* MacOS X 10.2 - for example */
/* MacOS X 10.5 requires -D_DARWIN_C_SOURCE or -U_POSIX_C_SOURCE - see above */
#elif defined(USE_CLASSIC_MAJOR_MINOR_MACROS)
#define major(x) ((x>>8) & 0x7F)
#define minor(x) (x & 0xFF)
#else
/* Hope the macros are in <sys/types.h> or otherwise magically visible */
#endif
#define MAJOR(x) ((long)major(x))
#define MINOR(x) ((long)minor(x))
您将有理由不那么热衷于代码中的“希望宏......神奇地可见”部分。
对 的引用是对推断此信息AC_HEADER_MAJOR
的宏。autoconf
如果您有一个config.h
由autoconf
.
请注意,POSIXpax
命令定义了ustar
格式并指定它在信息中包含devmajor和devminor,但添加了:
… 分别代表字符特殊文件和块特殊文件。在这种情况下,devmajor和devminor字段应包含定义设备的信息,其格式未在本卷 POSIX.1-2008 中指定。实现可以将设备规范映射到他们自己的本地规范,或者可以忽略该条目。
这意味着没有一种完全可移植的方式来表示数字。这并非完全不合理(但它是令人讨厌的);主要和次要设备编号的含义因平台而异,也未指定。任何通过格式创建块或字符设备的尝试ustar
只有在源机器和目标机器运行相同(相同版本)操作系统的情况下才能合理可靠地工作——尽管它们通常可以跨相同操作系统的版本移植。