0

i'm going crazy for this. I'm trying to write an archive library which can store and extract files. An archive file looks like this:

<0,/home/user/file.txt,154,0755>
file contents (154 byte)

Each file is identified by an header ( <...> ), with four "tags" (separated by commas), file type (0 for file and 1 for directory), path, size in bytes, permissions (in octal). I retrieve the size and the permissions with the stat system call (i'm on Linux). My problem is that i have to convert the octal value from st_mode to a string, store it in the archive file (the fourth tag in the header),then extract it and use it with the chmod syscall.

To convert it to string i use:

char mode[6];
sprintf (mode, "%o", statr.st_mode);

and to retrieve it i use atoi, but it does not seem to work. For example, the value stored in the 4th tag is 100644, but chmod set the permissions wrong (file not readable by my user).

I'm not sure if i explained well, i wil post the whole code if is needed (but it don't think there are implementation problem, is just a problem between conversion from octal to string)

EDIT: Solved! Actually the strtol method worked, but i had forgotten to apply it to the directories too (so extracting a directory with files inside caused Segfault because of the bad folder's permission mask). Thanks everybody for help!

Stephen Smally
  • 66
  • 1
  • 1
  • 6

3 Answers3

0

As far as I know, there's not a unix standard function to do this.

You will most likely have to mask st_mode to get each permissions bit that you want and translate into something that makes sense to you.

related post

There's maybe an easier way to do this, but I've including a simple example which shows how to get each bit that you are interested in. This does compile and seemed to run as expected.

st_mode.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>


#define SET_UID_BIT     0x4000
#define SET_GID_BIT     0x2000
#define STICKY_BIT      0x1000

// Readable USER bits defined here
#define USR_READ_BIT    0x400
#define USR_WRITE_BIT   0x200
#define USR_EXEC_BIT    0x100

// Readable GROUP bits defined here
#define GRP_READ_BIT    0x040
#define GRP_WRITE_BIT   0x020
#define GRP_EXEC_BIT    0x010

// Readable OTHER bits defined here
#define OTH_READ_BIT    0x004
#define OTH_WRITE_BIT   0x002
#define OTH_EXEC_BIT    0x001


int main()
{
    // The file I'm opening
    const char fp[] = "myfile.txt"; // -rw-rw-r--
    struct stat stats;

    // Get the stats
    if ( 0 == stat( fp, &stats ) )
    {
        // store st_mode
        mode_t mode = stats.st_mode;
        // init printable_mode
        unsigned int printable_mode = 0;        


        // Test for each permission bit in mode.
        // If the bit is present, mark the corresponding bit in printable_mode
        // using defined bits above.
        if( mode & S_ISUID )
        printable_mode |= SET_UID_BIT;

        if( mode & S_ISGID )
        printable_mode |= SET_GID_BIT;

        if( mode & S_ISVTX )
        printable_mode |= STICKY_BIT;

        if( mode & S_IRUSR )
        printable_mode |= USR_READ_BIT;

        if( mode & S_IWUSR )
        printable_mode |= USR_WRITE_BIT;

        if( mode & S_IXUSR )
        printable_mode |= USR_EXEC_BIT;

        if( mode & S_IRGRP )
        printable_mode |= GRP_READ_BIT;

        if( mode & S_IWGRP )
        printable_mode |= GRP_WRITE_BIT;

        if( mode & S_IXGRP )
        printable_mode |= GRP_EXEC_BIT;

        if( mode & S_IROTH )
        printable_mode |= OTH_READ_BIT;

        if( mode & S_IWOTH )
        printable_mode |= OTH_WRITE_BIT;

        if( mode & S_IXOTH )
        printable_mode |= OTH_EXEC_BIT;

        // Let's see what it looks like....
        printf("%04x\n", printable_mode);
        printf("%x\n", mode);
    }

    return 0;
}
Community
  • 1
  • 1
rkyser
  • 3,241
  • 1
  • 19
  • 28
  • This make sense, can you please explain me the %04x format in printf? – Stephen Smally Jul 26 '12 at 14:17
  • 2
    `printf("%04x", someunsignedinteger)`: Prints the number as hex (`x`), padded with zeroes (`0`) to a total width of 4. See the manpage of printf, where `0` is a "flag character", `4` is the field width, and `x` is the "conversion specifier". – tiwo Jul 26 '12 at 14:31
  • Yes, what ^he said. I did that to force the printing of all four octal digits. – rkyser Jul 26 '12 at 14:32
  • @rkyser: The [POSIX spec](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html) lists the numeric values, so I'd rely on 0644 meaning `rw-r--r--` everywhere. In your code, that would mean that `USR_WRITE_BIT == IW_USR == 0200` portably. Then it should be portable to just output the "raw" value of st_mode (truncated with `& 0777`). But I am not an expert; am I wrong? – tiwo Jul 26 '12 at 14:45
  • @tiwo: That seems right. I just wasn't familiar with octal printing when I formulated this. Your solution is much simpler. – rkyser Jul 26 '12 at 15:00
  • Am I missing something or should all of these bit masks be in octal instead of hex? I'm referencing the [Python stat implementation](https://github.com/python/cpython/blob/a9a8c8712665377cfa83af4b632b0db529ec1853/Lib/stat.py#L92-L110) – Ben Nov 04 '22 at 21:57
0

If you write your data in octal with a leading 0 you can then read it with strtol:

char mode[8];
snprintf(mode, 8, "0%o", statr.st_mode);
strtol(mode, NULL, 0);

Alternatively you can read it with strtol supplying 8 as the base argument, in which case the leading 0 is unnecessary:

char mode[7];
snprintf(mode, 8, "%o", statr.st_mode);
strtol(mode, NULL, 8);
ecatmur
  • 152,476
  • 27
  • 293
  • 366
0

Use strtoul. For example, the following test program correctly inteprets "100644" (octal). After the conversion, *endptr points to the first char not converted. If *endptr == 0, the whole string has been consumed, else there have been errors (for example, calling strtoul with "1234X" stops at 'X'). Note that octal 10644 equals 33188 (decimal).

#include <stdlib.h>
#include <stdio.h>

int main() {

   char *s = "100644";
   char *endptr;

   unsigned long result = strtoul(s, &endptr, 8);

   if (*endptr)
      printf("conversion error!\n");

   printf("result: %lu == octal %lo\n", result, result);

   return 0;
}
tiwo
  • 3,238
  • 1
  • 20
  • 33