7

我正在尝试编写一个处理ustar档案的可移植程序。对于设备文件,这些档案存储主要次要设备号。但是,struct stat在 POSIX 中列出的 仅包含使用“设备 ID(如果文件是字符或块特殊文件)”描述 st_rdev的类型的单个成员。</p>dev_t

如何在一对主要设备号和次要设备号与以可移植方式st_rdev返回的单个成员之间进行转换?stat()

4

3 回答 3

4

虽然所有 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 样式的包含来定义宏,则上述错误将导致停止编译。(我会亲自添加编译时机制,它可以帮助找到正确的标头定义,使用例如findxargsgrep,如果发生这种情况,建议也将添加内容发送到上游。touch empty.h ; cpp -dM empty.h ; rm -f empty.h应该显示所有预定义的宏,以帮助识别操作系统和/或 C 库。)

最初,POSIX 声明它dev_t必须是算术类型(因此,理论上,它可能是某些系统的某种变体floatdouble,但IEEE Std 1003.1,2013 版说它必须是整数类型。我敢打赌,这意味着没有已知的 POSIX-y 系统曾经使用过浮点dev_t类型。看起来 Windows 使用 void 指针或HANDLE类型,但 Windows 无论如何都不符合 POSIX。

于 2016-02-21T03:39:26.550 回答
3

定义后使用major()andminor()BSD_SOURCE

makedev()、major() 和 minor() 函数在 POSIX.1 中没有指定,但在许多其他系统上都有。

http://man7.org/linux/man-pages/man3/major.3.html

于 2016-02-14T13:28:27.437 回答
2

我有一个基于lsMinix 的古董版本的程序,但从那时起被我修改了很多。它具有以下代码来检测主要和次要宏 - 以及一些关于它过去工作过的(现在)古董系统的评论。它假定有足够新的 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.hautoconf.

POSIX

请注意,POSIXpax命令定义了ustar格式并指定它在信息中包含devmajordevminor,但添加了:

… 分别代表字符特殊文件和块特殊文件。在这种情况下,devmajordevminor字段应包含定义设备的信息,其格式未在本卷 POSIX.1-2008 中指定。实现可以将设备规范映射到他们自己的本地规范,或者可以忽略该条目。

这意味着没有一种完全可移植的方式来表示数字。这并非完全不合理(但它是令人讨厌的);主要和次要设备编号的含义因平台而异,也未指定。任何通过格式创建块或字符设备的尝试ustar只有在源机器和目标机器运行相同(相同版本)操作系统的情况下才能合理可靠地工作——尽管它们通常可以跨相同操作系统的版本移植。

于 2016-02-21T08:58:52.913 回答