C/Unix: How to extract the bits from st_mode?

2019-09-12 05:58发布

问题:

I am a beginner to Unix programming and C and I have two questions regarding the struct stat and its field st_mode:

  1. When accessing the st_mode field as below, what type of number is returned( octal, decimal, etc.) ?

    struct stat file;
    stat( someFilePath, &file);
    printf("%d", file.st_mode );
    

I thought the number is in octal but when I ran this code, and I got the value 33188. What is the base ?

  1. I found out that the st_mode encodes a 16 bit binary number that represents the file type and file permissions. How do I get the 16-bit number from the above output(esp. when it doesn't seem to be in octal). And which parts of the 16-bit digit encodes which information ?

Thanks for any help.

回答1:

I think you are mixing concepts here.

In memory, integer values are always in binary, let's call it native format. st_mode is of type mode_t and that type is an unspecified integer type, probably int.

The concept of base, that is decimal, octal or hexadecimal, is useful only when you covert the number in memory in native format to a text representation, (or back from text to native).

For example:

int x = 42;

assigns the number 42 to the integer variable. Since source code is text, the 42 is input as text, and we know that it is a decimal value (no prefix). But note how we do not specify a base for the variable: it does not have one. This other code:

int x = 0x2A;

is exactly equivalent. Instead of the decimal constant 42 it uses the hexadecimal constant 0x2A, but that is identical, and x got the same value on both cases. Likewise:

int x = 052;

is also equivalent, but with an octal constant.

Now to your code. When you do:

printf("%d", file.st_mode);
33188

you tell the program to output the value of that variable as a decimal number. Remember that printf converts the number from native format to text, so the base of that text matters. If you prefer to see the value as octal, just write:

printf("%o", file.st_mode);
100644

Or in hexadecimal:

printf("%x", file.st_mode);
81A4

The nice thing about octal is that itrepresents exactly 3 bit per octal digit (4 bits per digit for hex.), so with a bit of practise you can see the bits without computations.

For example, your st_mode is 33188 in decimal or 0100644 in octal. The decimal tells me nothing, but the octal does mean something, because I remember that the last 9 bits (3 octal digits) are the permissions: 3 bits for the owner, 3 bits for the group, 3 bits for other. So:

* Owner: 6 that is rw-
* Group: 4 that is r--
* Other: 4 that is r--

BTW, the last 1 is this constant:

#define S_IFREG 0100000

that just means that it is a regular file.



回答2:

  1. The number is a set to a value. In printf() you can either use "%d" to print it in decimal form (as you did), or use "%o" (or "%lo") instead, to print it in octal form.

  2. Check man 2 stat about how each information is represented. Then, you can use a bitmask to extract the information you need. There's an example demonstrating it in the man page.



回答3:

You printed it as decimal, so you got decimal. If you'd printed it as octal, you'd have gotten octal; ditto for hex.

The st_mode field is most easily understood when printed as octal.

printf("mode = %07o\n", file.st_mode);

The last three digits are the familiar permissions (644, for example) for chmod etc. The 4th digit from the right is usually 0 but will be non-zero for set-UID, set-GID or sticky-bit files or directories. The remaining digits identify the type of the file.

You can use the macros from <sys/stat.h> to dissect the information.