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 | } |