Subversion Repositories QNX 8.QNX8 utilities

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
// qnx6label.c -- read/set the volume labels of qnx6fs partitions by Pierre-Marie Baty <pm@pmbaty.com>
2
 
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <stdint.h>
6
#include <stdbool.h>
7
#include <string.h>
8
#include <errno.h>
9
#include <ctype.h>
10
 
11
 
12
#define QNX6FS_SUPERBLOCK_MAGIC 0x68191122
13
 
14
 
15
static uint32_t crc32_be (uint32_t crc, const uint8_t *p, size_t len)
16
{
17
        // big endian CRC32 function to recompute a qnx6fs superblock checksum
18
 
19
        const uint32_t CRCPOLY_BE = 0x04c11db7;
20
        size_t bit_index;
21
 
22
        while (len--)
23
        {
24
                crc ^= *p++ << 24;
25
                for (bit_index = 0; bit_index < 8; bit_index++)
26
                        crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
27
        }
28
 
29
        return (crc);
30
}
31
 
32
 
33
int main (int argc, char **argv)
34
{
35
        // program entrypoint
36
 
37
        // references:
38
        // https://docs.kernel.org/filesystems/qnx6.html
39
        // https://github.com/torvalds/linux/blob/master/fs/qnx6/qnx6.h
40
        // https://github.com/torvalds/linux/blob/master/include/linux/qnx6_fs.h
41
        // https://github.com/torvalds/linux/blob/master/fs/qnx6/inode.c
42
 
43
        typedef struct __attribute__((packed)) qnx6_rootnode_s
44
        {
45
                uint64_t size;
46
                uint32_t ptr[16]; // 16 block pointers in sbl/inode
47
                uint8_t levels;
48
                uint8_t mode;
49
                uint8_t spare[6];
50
        } qnx6_rootnode_t;
51
        typedef struct __attribute__((packed)) qnx6_superblock_s
52
        {
53
                uint32_t sb_magic; // offset 0 -- "\x22\x11\x19\x68" or 0x68191122
54
                uint32_t sb_checksum; // offset 4
55
                uint64_t sb_serial; // offset 8
56
                uint32_t sb_ctime; // offset 16 -- time the fs was created
57
                uint32_t sb_atime; // offset 20 -- last access time
58
                uint32_t sb_flags; // offset 24
59
                uint16_t sb_version1; // offset 28 -- filesystem version info
60
                uint16_t sb_version2; // offset 30 -- filesystem version info
61
                uint8_t sb_volumeid[16]; // offset 32
62
                uint32_t sb_blocksize; // offset 48
63
                uint32_t sb_num_inodes; // offset 52
64
                uint32_t sb_free_inodes; // offset 56
65
                uint32_t sb_num_blocks; // offset 60
66
                uint32_t sb_free_blocks; // offset 64
67
                uint32_t sb_allocgroup; // offset 68
68
                qnx6_rootnode_t rootnode_inode; // offset 72
69
                qnx6_rootnode_t rootnode_bitmap; // offset 152
70
                qnx6_rootnode_t rootnode_longfile; // offset 232
71
                qnx6_rootnode_t rootnode_unknown; // offset 312
72
        } qnx6_superblock_t; // size 392
73
        typedef struct qnx6_superblock_info_s
74
        {
75
                bool is_mandatory;
76
                size_t offset;
77
                uint8_t bytes[512];
78
                qnx6_superblock_t *sb; // points at beginning of bytes array
79
        } qnx6_superblock_info_t;
80
 
81
        qnx6_superblock_info_t qnx6_superblocks[4] =
82
        {
83
                { true,  0x2000,   "", (qnx6_superblock_t *) qnx6_superblocks[0].bytes }, // mandatory first superblock
84
                { false, 0x2e00,   "", (qnx6_superblock_t *) qnx6_superblocks[1].bytes },
85
                { true,  SIZE_MAX, "", (qnx6_superblock_t *) qnx6_superblocks[2].bytes }, // mandatory last superblock
86
                { false, SIZE_MAX, "", (qnx6_superblock_t *) qnx6_superblocks[3].bytes },
87
        };
88
        qnx6_superblock_t *latest_superblock;
89
        const char *dev_pathname;
90
        size_t superblock_index;
91
        size_t label_offset;
92
        size_t char_index;
93
        size_t end_pos;
94
        uint8_t *p;
95
        char *new_label;
96
        char label[17];
97
        FILE *fp;
98
 
99
        // consistency checks
100
        if ((argc < 2) || (*(argv[1]) == '-'))
101
        {
102
                fprintf (stderr, "usage: qnx6label <device> [new-label]\n");
103
                exit (1);
104
        }
105
 
106
        dev_pathname = argv[1]; // read arguments
107
        new_label = (argc > 2 ? argv[2] : NULL);
108
 
109
        // open and read all mandatory superblocks of the passed device pathname
110
        if ((fp = fopen (dev_pathname, "r+b")) == NULL)
111
        {
112
                fprintf (stderr, "error: can't open %s: %s\n", dev_pathname, strerror (errno));
113
                exit (1);
114
        }
115
        fseek (fp, 0, SEEK_END);
116
        end_pos = ftell (fp);
117
        if (end_pos < 0x4000)
118
        {
119
                fprintf (stderr, "error: %s too small to contain a qnx6 filesystem\n", dev_pathname);
120
                exit (1);
121
        }
122
        qnx6_superblocks[2].offset = (end_pos & ~0xfff) - 0x1000; // compute mandatory last superblock offset
123
        qnx6_superblocks[3].offset = (end_pos & ~0xfff) - 0x1000 + 0xe00; // compute optional last superblock offset
124
        latest_superblock = NULL;
125
        for (superblock_index = 0; superblock_index < sizeof (qnx6_superblocks) / sizeof (qnx6_superblocks[0]); superblock_index++)
126
        {
127
                // seek at the right offset and read the (supposed) qnx6fs superblock data
128
                if ((fseek (fp, qnx6_superblocks[superblock_index].offset, SEEK_SET) != 0)
129
                    || (fread (qnx6_superblocks[superblock_index].bytes, sizeof (qnx6_superblocks[superblock_index].bytes), 1, fp) != 1))
130
                {
131
                        fprintf (stderr, "error: can't read superblock #%zd of %s at 0x%zx: %s\n", superblock_index, dev_pathname, qnx6_superblocks[superblock_index].offset, strerror (errno));
132
                        exit (1);
133
                }
134
 
135
                // validate that all mandatory superblocks are present and keep track of the newest one
136
                if (qnx6_superblocks[superblock_index].sb->sb_magic == QNX6FS_SUPERBLOCK_MAGIC)
137
                {
138
                        if ((latest_superblock == NULL) || (qnx6_superblocks[superblock_index].sb->sb_serial > latest_superblock->sb_serial))
139
                                latest_superblock = qnx6_superblocks[superblock_index].sb; // update newest superblock pointer
140
                }
141
                else if (qnx6_superblocks[superblock_index].is_mandatory)
142
                {
143
                        fprintf (stderr, "error: superblock #%zd invalid: this does not appear to be a qnx6 filesystem\n", superblock_index);
144
                        exit (1);
145
                }
146
        }
147
        if (latest_superblock == NULL)
148
        {
149
                fprintf (stderr, "error: no qnx6fs superblock found: this does not appear to be a qnx6 filesystem\n");
150
                exit (1);
151
        }
152
 
153
        // do we want to read or set the volume label ?
154
        if (new_label == NULL)
155
        {
156
                // read the label from the newest superblock
157
                memcpy (label, latest_superblock->sb_volumeid, 16);
158
                label[16] = 0;
159
 
160
                // print it to the standard output
161
                printf ("%s\n", label);
162
        }
163
        else if (argc == 3)
164
        {
165
                // update the label in all superblocks, fix checksums and update the blocks
166
                for (superblock_index = 0; superblock_index < sizeof (qnx6_superblocks) / sizeof (qnx6_superblocks[0]); superblock_index++)
167
                {
168
                        if (qnx6_superblocks[superblock_index].sb->sb_magic != QNX6FS_SUPERBLOCK_MAGIC)
169
                                continue; // skip invalid superblocks
170
                        strncpy ((char *) qnx6_superblocks[superblock_index].sb->sb_volumeid, new_label, sizeof (qnx6_superblocks[superblock_index].sb->sb_volumeid));
171
                        qnx6_superblocks[superblock_index].sb->sb_checksum = crc32_be (0, &qnx6_superblocks[superblock_index].bytes[8], 504); // fix the superblock checksum
172
                        if ((fseek (fp, qnx6_superblocks[superblock_index].offset, SEEK_SET) != 0)
173
                            || (fwrite (qnx6_superblocks[superblock_index].bytes, sizeof (qnx6_superblocks[superblock_index].bytes), 1, fp) != 1))
174
                        {
175
                                fprintf (stderr, "error: can't update superblock #%zd of %s at 0x%zx: %s\n", superblock_index, dev_pathname, qnx6_superblocks[superblock_index].offset, strerror (errno));
176
                                fprintf (stderr, "*** FILESYSTEM LEFT INCONSISTENT, USE chkqnx6fs TO FIX ***\n");
177
                                exit (1);
178
                        }
179
                }
180
        }
181
 
182
        fclose (fp);
183
        exit (0);
184
}