如何在dev_t和主要/次要设备号之间进行转换?

时间:2016-02-14 13:23:20

标签: c posix tar portability device-node

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

如何以便携方式在st_rdev返回的一对主设备号和次设备号以及一个stat()成员之间进行转换?

3 个答案:

答案 0 :(得分:4)

虽然所有POSIX编程接口都使用设备号(类型为dev_t),但FUZxxl在对此答案的评论中指出了常见的UStar文件格式 - 最常见的tar归档格式 - 将设备编号分为主要和次要。 (它们通常编码为每个七个八进制数字,因此出于兼容性原因,应限制为21位无符号主要和21位无符号次要。这也意味着将设备编号映射到主要或仅次要不是可靠的方法。)

以下内容包括在Jonathon Reinhart's answer上展开文件,在网上挖掘各种系统手册页和文档(适用于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格式功能的归档器中收集其他定义。在我看来,与每个OS /架构上的现有实现的兼容性是最重要的。

以上内容应涵盖使用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的某种变体),但是{{3说它必须是整数类型。我敢打赌这意味着没有已知的POSIX-y系统曾经使用过浮点dev_t类型。看起来Windows使用void指针或HANDLE类型,但Windows无论如何都不符合POSIX。

答案 1 :(得分:3)

在定义major()后使用minor()BSD_SOURCE宏。

  

未指定makedev(),major()和minor()函数          POSIX.1,但存在于许多其他系统上。

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

答案 2 :(得分:2)

我有一个基于古典版ls的Minix程序,但是从那以后我修改了很多 mangled 。它具有以下代码来检测主要和次要宏 - 以及一些关于(现在)古老系统的评论。它假定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文件,则会很有用。

POSIX

请注意,POSIX pax命令定义ustar格式,并指定它在信息中包含 devmajor devminor ,但添加:

  

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

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