I'm trying to write a portable program that deals with ustar archives. For device files, these archives store the major and minor device numbers. However, the struct stat as laid out in POSIX only contains a single st_rdev member of type dev_t described with “Device ID (if file is character or block special).”
How can I convert between a pair of major and minor device numbers and a single st_rdev member as returned by stat() in a portable manner?
While all POSIX programming interfaces use the device number (of type
dev_t) as is, FUZxxl pointed out in a comment to this answer that the common UStar file format -- most common tar archive format -- does split the device number into major and minor. (They are typically encoded as seven octal digits each, so for compatibility reasons one should limit to 21-bit unsigned major and 21-bit unsigned minor. This also means that mapping the device number to just the major or just the minor is not a reliable approach.)The following include file expanding on Jonathon Reinhart's answer, after digging on the web for the various systems man pages and documentation (for
makedev(),major(), andminor()), plus comments to this question.One could glean additional definitions from existing UStar-format -capable archivers. Compatibility with existing implementations on each OS/architecture is, in my opinion, the most important thing here.
The above should cover all systems using GNU C library, Linux (including Android), FreeBSD, OpenBSD, NetBSD, DragonFlyBSD, Mac OS X, AIX, Tru64, HP-UX, and Solaris, plus any that define the macros when
<sys/types.h>is included. Of the Windows part, I'm not sure.As I understand it, Windows uses device 0 for all normal files, and a
HANDLE(a void pointer type) for devices. I am not at all sure whether the above logic is sane on Windows, but many older systems put the 8 least significant bits of the device number into minor, and the next 8 bits into major, and the convention seems to be that any leftover bits would be put (without shifting) into minor, too. Examining existing UStar-format tar archives with references to devices would be useful, but I personally do not use Windows at all.If a system is not detected, and the system does not use the BSD-style inclusion for defining the macros, the above will error out stopping the compilation. (I would personally add compile-time machinery that could help finding the correct header definitions, using e.g.
find,xargs, andgrep, in case that happens, with a suggestion to send the addition upstream, too.touch empty.h ; cpp -dM empty.h ; rm -f empty.hshould show all predefined macros, to help with identifying the OS and/or C library.)Originally, POSIX stated that
dev_tmust be an arithmetic type (thus, theoretically, it might have been some variant offloatordoubleon some systems), but IEEE Std 1003.1, 2013 Edition says it must be an integer type. I would wager that means no known POSIX-y system ever used a floating-pointdev_ttype. It would seem that Windows uses a void pointer, orHANDLEtype, but Windows is not POSIX-compliant anyway.