| 1 | /*
 | 
|---|
| 2 |  * Copyright (c) 2011 Maurizio Lombardi
 | 
|---|
| 3 |  * All rights reserved.
 | 
|---|
| 4 |  *
 | 
|---|
| 5 |  * Redistribution and use in source and binary forms, with or without
 | 
|---|
| 6 |  * modification, are permitted provided that the following conditions
 | 
|---|
| 7 |  * are met:
 | 
|---|
| 8 |  *
 | 
|---|
| 9 |  * - Redistributions of source code must retain the above copyright
 | 
|---|
| 10 |  *   notice, this list of conditions and the following disclaimer.
 | 
|---|
| 11 |  * - Redistributions in binary form must reproduce the above copyright
 | 
|---|
| 12 |  *   notice, this list of conditions and the following disclaimer in the
 | 
|---|
| 13 |  *   documentation and/or other materials provided with the distribution.
 | 
|---|
| 14 |  * - The name of the author may not be used to endorse or promote products
 | 
|---|
| 15 |  *   derived from this software without specific prior written permission.
 | 
|---|
| 16 |  *
 | 
|---|
| 17 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 | 
|---|
| 18 |  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | 
|---|
| 19 |  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | 
|---|
| 20 |  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
|---|
| 21 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | 
|---|
| 22 |  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
|---|
| 23 |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
|---|
| 24 |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
|---|
| 25 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | 
|---|
| 26 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
|---|
| 27 |  */
 | 
|---|
| 28 | 
 | 
|---|
| 29 | /** @addtogroup mkmfs
 | 
|---|
| 30 |  * @{
 | 
|---|
| 31 |  */
 | 
|---|
| 32 | 
 | 
|---|
| 33 | /**
 | 
|---|
| 34 |  * @file        mkmfs.c
 | 
|---|
| 35 |  * @brief       Tool for creating new Minix file systems.
 | 
|---|
| 36 |  *
 | 
|---|
| 37 |  */
 | 
|---|
| 38 | 
 | 
|---|
| 39 | #include <stdio.h>
 | 
|---|
| 40 | #include <stdlib.h>
 | 
|---|
| 41 | #include <block.h>
 | 
|---|
| 42 | #include <errno.h>
 | 
|---|
| 43 | #include <inttypes.h>
 | 
|---|
| 44 | #include <getopt.h>
 | 
|---|
| 45 | #include <mem.h>
 | 
|---|
| 46 | #include <str.h>
 | 
|---|
| 47 | #include <time.h>
 | 
|---|
| 48 | #include <minix.h>
 | 
|---|
| 49 | 
 | 
|---|
| 50 | #define NAME    "mkmfs"
 | 
|---|
| 51 | 
 | 
|---|
| 52 | #define FREE    0
 | 
|---|
| 53 | #define USED    1
 | 
|---|
| 54 | 
 | 
|---|
| 55 | #define UPPER(n, size)          (((n) / (size)) + (((n) % (size)) != 0))
 | 
|---|
| 56 | #define NEXT_DENTRY(p, dirsize) (p += (dirsize))
 | 
|---|
| 57 | 
 | 
|---|
| 58 | typedef enum {
 | 
|---|
| 59 |         HELP_SHORT,
 | 
|---|
| 60 |         HELP_LONG
 | 
|---|
| 61 | } help_level_t;
 | 
|---|
| 62 | 
 | 
|---|
| 63 | /* Generic MFS superblock */
 | 
|---|
| 64 | struct mfs_sb_info {
 | 
|---|
| 65 |         uint64_t n_inodes;
 | 
|---|
| 66 |         uint64_t n_zones;
 | 
|---|
| 67 |         aoff64_t dev_nblocks;
 | 
|---|
| 68 |         unsigned long ibmap_blocks;
 | 
|---|
| 69 |         unsigned long zbmap_blocks;
 | 
|---|
| 70 |         unsigned long first_data_zone;
 | 
|---|
| 71 |         unsigned long itable_size;
 | 
|---|
| 72 |         int log2_zone_size;
 | 
|---|
| 73 |         int ino_per_block;
 | 
|---|
| 74 |         int dirsize;
 | 
|---|
| 75 |         uint32_t max_file_size;
 | 
|---|
| 76 |         uint16_t magic;
 | 
|---|
| 77 |         uint32_t block_size;
 | 
|---|
| 78 |         int fs_version;
 | 
|---|
| 79 |         bool longnames;
 | 
|---|
| 80 | };
 | 
|---|
| 81 | 
 | 
|---|
| 82 | static void     help_cmd_mkmfs(help_level_t level);
 | 
|---|
| 83 | static bool     is_power_of_two(uint32_t n);
 | 
|---|
| 84 | static errno_t  init_superblock(struct mfs_sb_info *sb);
 | 
|---|
| 85 | static errno_t  write_superblock(const struct mfs_sb_info *sbi);
 | 
|---|
| 86 | static errno_t  write_superblock3(const struct mfs_sb_info *sbi);
 | 
|---|
| 87 | static errno_t  init_bitmaps(const struct mfs_sb_info *sb);
 | 
|---|
| 88 | static errno_t  init_inode_table(const struct mfs_sb_info *sb);
 | 
|---|
| 89 | static errno_t  make_root_ino(const struct mfs_sb_info *sb);
 | 
|---|
| 90 | static errno_t  make_root_ino2(const struct mfs_sb_info *sb);
 | 
|---|
| 91 | static void     mark_bmap(uint32_t *bmap, int idx, int v);
 | 
|---|
| 92 | static errno_t  insert_dentries(const struct mfs_sb_info *sb);
 | 
|---|
| 93 | 
 | 
|---|
| 94 | static inline errno_t write_block(aoff64_t off, size_t size, const void *data);
 | 
|---|
| 95 | 
 | 
|---|
| 96 | static service_id_t service_id;
 | 
|---|
| 97 | static int shift;
 | 
|---|
| 98 | 
 | 
|---|
| 99 | static struct option const long_options[] = {
 | 
|---|
| 100 |         { "help", no_argument, 0, 'h' },
 | 
|---|
| 101 |         { "long-names", no_argument, 0, 'l' },
 | 
|---|
| 102 |         { "block-size", required_argument, 0, 'b' },
 | 
|---|
| 103 |         { "inodes", required_argument, 0, 'i' },
 | 
|---|
| 104 |         { NULL, no_argument, 0, '1' },
 | 
|---|
| 105 |         { NULL, no_argument, 0, '2' },
 | 
|---|
| 106 |         { 0, 0, 0, 0 }
 | 
|---|
| 107 | };
 | 
|---|
| 108 | 
 | 
|---|
| 109 | int main (int argc, char **argv)
 | 
|---|
| 110 | {
 | 
|---|
| 111 |         errno_t rc;
 | 
|---|
| 112 |         int c, opt_ind;
 | 
|---|
| 113 |         char *device_name;
 | 
|---|
| 114 |         size_t devblock_size;
 | 
|---|
| 115 | 
 | 
|---|
| 116 |         struct mfs_sb_info sb;
 | 
|---|
| 117 | 
 | 
|---|
| 118 |         /* Default is MinixFS V3 */
 | 
|---|
| 119 |         sb.magic = MFS_MAGIC_V3;
 | 
|---|
| 120 |         sb.fs_version = 3;
 | 
|---|
| 121 | 
 | 
|---|
| 122 |         /* Default block size is 4Kb */
 | 
|---|
| 123 |         sb.block_size = MFS_MAX_BLOCKSIZE;
 | 
|---|
| 124 |         sb.dirsize = MFS3_DIRSIZE;
 | 
|---|
| 125 |         sb.n_inodes = 0;
 | 
|---|
| 126 |         sb.longnames = false;
 | 
|---|
| 127 |         sb.ino_per_block = V3_INODES_PER_BLOCK(MFS_MAX_BLOCKSIZE);
 | 
|---|
| 128 | 
 | 
|---|
| 129 |         if (argc == 1) {
 | 
|---|
| 130 |                 help_cmd_mkmfs(HELP_SHORT);
 | 
|---|
| 131 |                 printf("Incorrect number of arguments, try `mkmfs --help'\n");
 | 
|---|
| 132 |                 exit(0);
 | 
|---|
| 133 |         }
 | 
|---|
| 134 | 
 | 
|---|
| 135 |         c = 0;
 | 
|---|
| 136 |         optind = 0;
 | 
|---|
| 137 |         opt_ind = 0;
 | 
|---|
| 138 |         while (c != -1) {
 | 
|---|
| 139 |                 c = getopt_long(argc, argv, "lh12b:i:",
 | 
|---|
| 140 |                     long_options, &opt_ind);
 | 
|---|
| 141 |                 switch (c) {
 | 
|---|
| 142 |                 case 'h':
 | 
|---|
| 143 |                         help_cmd_mkmfs(HELP_LONG);
 | 
|---|
| 144 |                         exit(0);
 | 
|---|
| 145 |                 case '1':
 | 
|---|
| 146 |                         sb.magic = MFS_MAGIC_V1;
 | 
|---|
| 147 |                         sb.block_size = MFS_BLOCKSIZE;
 | 
|---|
| 148 |                         sb.fs_version = 1;
 | 
|---|
| 149 |                         sb.ino_per_block = V1_INODES_PER_BLOCK;
 | 
|---|
| 150 |                         sb.dirsize = MFS_DIRSIZE;
 | 
|---|
| 151 |                         break;
 | 
|---|
| 152 |                 case '2':
 | 
|---|
| 153 |                         sb.magic = MFS_MAGIC_V2;
 | 
|---|
| 154 |                         sb.block_size = MFS_BLOCKSIZE;
 | 
|---|
| 155 |                         sb.fs_version = 2;
 | 
|---|
| 156 |                         sb.ino_per_block = V2_INODES_PER_BLOCK;
 | 
|---|
| 157 |                         sb.dirsize = MFS_DIRSIZE;
 | 
|---|
| 158 |                         break;
 | 
|---|
| 159 |                 case 'b':
 | 
|---|
| 160 |                         sb.block_size = (uint32_t) strtol(optarg, NULL, 10);
 | 
|---|
| 161 |                         break;
 | 
|---|
| 162 |                 case 'i':
 | 
|---|
| 163 |                         sb.n_inodes = (uint64_t) strtol(optarg, NULL, 10);
 | 
|---|
| 164 |                         break;
 | 
|---|
| 165 |                 case 'l':
 | 
|---|
| 166 |                         sb.longnames = true;
 | 
|---|
| 167 |                         sb.dirsize = MFSL_DIRSIZE;
 | 
|---|
| 168 |                         break;
 | 
|---|
| 169 |                 }
 | 
|---|
| 170 |         }
 | 
|---|
| 171 | 
 | 
|---|
| 172 |         if (sb.block_size < MFS_MIN_BLOCKSIZE ||
 | 
|---|
| 173 |             sb.block_size > MFS_MAX_BLOCKSIZE) {
 | 
|---|
| 174 |                 printf(NAME ":Error! Invalid block size.\n");
 | 
|---|
| 175 |                 exit(0);
 | 
|---|
| 176 |         } else if (!is_power_of_two(sb.block_size)) {
 | 
|---|
| 177 |                 /* Block size must be a power of 2. */
 | 
|---|
| 178 |                 printf(NAME ":Error! Invalid block size.\n");
 | 
|---|
| 179 |                 exit(0);
 | 
|---|
| 180 |         } else if (sb.block_size > MFS_BLOCKSIZE &&
 | 
|---|
| 181 |             sb.fs_version != 3) {
 | 
|---|
| 182 |                 printf(NAME ":Error! Block size > 1024 is "
 | 
|---|
| 183 |                     "supported by V3 filesystem only.\n");
 | 
|---|
| 184 |                 exit(0);
 | 
|---|
| 185 |         } else if (sb.fs_version == 3 && sb.longnames) {
 | 
|---|
| 186 |                 printf(NAME ":Error! Long filenames are supported "
 | 
|---|
| 187 |                     "by V1/V2 filesystem only.\n");
 | 
|---|
| 188 |                 exit(0);
 | 
|---|
| 189 |         }
 | 
|---|
| 190 | 
 | 
|---|
| 191 |         if (sb.block_size == MFS_MIN_BLOCKSIZE)
 | 
|---|
| 192 |                 shift = 1;
 | 
|---|
| 193 |         else if (sb.block_size == MFS_MAX_BLOCKSIZE)
 | 
|---|
| 194 |                 shift = 3;
 | 
|---|
| 195 |         else
 | 
|---|
| 196 |                 shift = 2;
 | 
|---|
| 197 | 
 | 
|---|
| 198 |         argv += optind;
 | 
|---|
| 199 | 
 | 
|---|
| 200 |         device_name = argv[0];
 | 
|---|
| 201 | 
 | 
|---|
| 202 |         if (!device_name) {
 | 
|---|
| 203 |                 help_cmd_mkmfs(HELP_LONG);
 | 
|---|
| 204 |                 exit(0);
 | 
|---|
| 205 |         }
 | 
|---|
| 206 | 
 | 
|---|
| 207 |         rc = loc_service_get_id(device_name, &service_id, 0);
 | 
|---|
| 208 |         if (rc != EOK) {
 | 
|---|
| 209 |                 printf(NAME ": Error resolving device `%s'.\n", device_name);
 | 
|---|
| 210 |                 return 2;
 | 
|---|
| 211 |         }
 | 
|---|
| 212 | 
 | 
|---|
| 213 |         rc = block_init(service_id, 2048);
 | 
|---|
| 214 |         if (rc != EOK)  {
 | 
|---|
| 215 |                 printf(NAME ": Error initializing libblock.\n");
 | 
|---|
| 216 |                 return 2;
 | 
|---|
| 217 |         }
 | 
|---|
| 218 | 
 | 
|---|
| 219 |         rc = block_get_bsize(service_id, &devblock_size);
 | 
|---|
| 220 |         if (rc != EOK) {
 | 
|---|
| 221 |                 printf(NAME ": Error determining device block size.\n");
 | 
|---|
| 222 |                 return 2;
 | 
|---|
| 223 |         }
 | 
|---|
| 224 | 
 | 
|---|
| 225 |         rc = block_get_nblocks(service_id, &sb.dev_nblocks);
 | 
|---|
| 226 |         if (rc != EOK) {
 | 
|---|
| 227 |                 printf(NAME ": Warning, failed to obtain "
 | 
|---|
| 228 |                     "block device size.\n");
 | 
|---|
| 229 |         } else {
 | 
|---|
| 230 |                 printf(NAME ": Block device has %" PRIuOFF64 " blocks.\n",
 | 
|---|
| 231 |                     sb.dev_nblocks);
 | 
|---|
| 232 |         }
 | 
|---|
| 233 | 
 | 
|---|
| 234 |         if (devblock_size != 512) {
 | 
|---|
| 235 |                 printf(NAME ": Error. Device block size is not 512 bytes.\n");
 | 
|---|
| 236 |                 return 2;
 | 
|---|
| 237 |         }
 | 
|---|
| 238 | 
 | 
|---|
| 239 |         /* Minimum block size is 1 Kb */
 | 
|---|
| 240 |         sb.dev_nblocks /= 2;
 | 
|---|
| 241 | 
 | 
|---|
| 242 |         printf(NAME ": Creating Minix file system on device\n");
 | 
|---|
| 243 |         printf(NAME ": Writing superblock\n");
 | 
|---|
| 244 | 
 | 
|---|
| 245 |         /* Initialize superblock */
 | 
|---|
| 246 |         if (init_superblock(&sb) != EOK) {
 | 
|---|
| 247 |                 printf(NAME ": Error. Superblock initialization failed\n");
 | 
|---|
| 248 |                 return 2;
 | 
|---|
| 249 |         }
 | 
|---|
| 250 | 
 | 
|---|
| 251 |         printf(NAME ": Initializing bitmaps\n");
 | 
|---|
| 252 | 
 | 
|---|
| 253 |         /* Initialize bitmaps */
 | 
|---|
| 254 |         if (init_bitmaps(&sb) != EOK) {
 | 
|---|
| 255 |                 printf(NAME ": Error. Bitmaps initialization failed\n");
 | 
|---|
| 256 |                 return 2;
 | 
|---|
| 257 |         }
 | 
|---|
| 258 | 
 | 
|---|
| 259 |         printf(NAME ": Initializing the inode table\n");
 | 
|---|
| 260 | 
 | 
|---|
| 261 |         /* Init inode table */
 | 
|---|
| 262 |         if (init_inode_table(&sb) != EOK) {
 | 
|---|
| 263 |                 printf(NAME ": Error. Inode table initialization failed\n");
 | 
|---|
| 264 |                 return 2;
 | 
|---|
| 265 |         }
 | 
|---|
| 266 | 
 | 
|---|
| 267 |         printf(NAME ": Creating the root directory inode\n");
 | 
|---|
| 268 | 
 | 
|---|
| 269 |         /* Make the root inode */
 | 
|---|
| 270 |         if (sb.fs_version == 1)
 | 
|---|
| 271 |                 rc = make_root_ino(&sb);
 | 
|---|
| 272 |         else
 | 
|---|
| 273 |                 rc = make_root_ino2(&sb);
 | 
|---|
| 274 | 
 | 
|---|
| 275 |         if (rc != EOK) {
 | 
|---|
| 276 |                 printf(NAME ": Error. Root inode initialization failed\n");
 | 
|---|
| 277 |                 return 2;
 | 
|---|
| 278 |         }
 | 
|---|
| 279 | 
 | 
|---|
| 280 |         /* Insert directory entries . and .. */
 | 
|---|
| 281 |         if (insert_dentries(&sb) != EOK) {
 | 
|---|
| 282 |                 printf(NAME ": Error. Root directory initialization failed\n");
 | 
|---|
| 283 |                 return 2;
 | 
|---|
| 284 |         }
 | 
|---|
| 285 | 
 | 
|---|
| 286 |         block_fini(service_id);
 | 
|---|
| 287 | 
 | 
|---|
| 288 |         return 0;
 | 
|---|
| 289 | }
 | 
|---|
| 290 | 
 | 
|---|
| 291 | /**Inserts the '.' and '..' directory entries in the root directory.
 | 
|---|
| 292 |  *
 | 
|---|
| 293 |  * @param sb            Pointer to the superblock structure.
 | 
|---|
| 294 |  *
 | 
|---|
| 295 |  * @return              EOK on success or an error code.
 | 
|---|
| 296 |  */
 | 
|---|
| 297 | static errno_t insert_dentries(const struct mfs_sb_info *sb)
 | 
|---|
| 298 | {
 | 
|---|
| 299 |         void *root_block;
 | 
|---|
| 300 |         uint8_t *dentry_ptr;
 | 
|---|
| 301 |         errno_t rc;
 | 
|---|
| 302 |         const long root_dblock = sb->first_data_zone;
 | 
|---|
| 303 | 
 | 
|---|
| 304 |         root_block = malloc(sb->block_size);
 | 
|---|
| 305 |         memset(root_block, 0x00, sb->block_size);
 | 
|---|
| 306 | 
 | 
|---|
| 307 |         if (!root_block)
 | 
|---|
| 308 |                 return ENOMEM;
 | 
|---|
| 309 | 
 | 
|---|
| 310 |         dentry_ptr = root_block;
 | 
|---|
| 311 | 
 | 
|---|
| 312 |         if (sb->fs_version != 3) {
 | 
|---|
| 313 |                 /* Directory entries for V1/V2 filesystem */
 | 
|---|
| 314 |                 struct mfs_dentry *dentry = root_block;
 | 
|---|
| 315 | 
 | 
|---|
| 316 |                 dentry->d_inum = MFS_ROOT_INO;
 | 
|---|
| 317 |                 memcpy(dentry->d_name, ".\0", 2);
 | 
|---|
| 318 | 
 | 
|---|
| 319 |                 dentry = (struct mfs_dentry *) NEXT_DENTRY(dentry_ptr,
 | 
|---|
| 320 |                     sb->dirsize);
 | 
|---|
| 321 | 
 | 
|---|
| 322 |                 dentry->d_inum = MFS_ROOT_INO;
 | 
|---|
| 323 |                 memcpy(dentry->d_name, "..\0", 3);
 | 
|---|
| 324 |         } else {
 | 
|---|
| 325 |                 /* Directory entries for V3 filesystem */
 | 
|---|
| 326 |                 struct mfs3_dentry *dentry = root_block;
 | 
|---|
| 327 | 
 | 
|---|
| 328 |                 dentry->d_inum = MFS_ROOT_INO;
 | 
|---|
| 329 |                 memcpy(dentry->d_name, ".\0", 2);
 | 
|---|
| 330 | 
 | 
|---|
| 331 |                 dentry = (struct mfs3_dentry *) NEXT_DENTRY(dentry_ptr,
 | 
|---|
| 332 |                     sb->dirsize);
 | 
|---|
| 333 | 
 | 
|---|
| 334 |                 dentry->d_inum = MFS_ROOT_INO;
 | 
|---|
| 335 |                 memcpy(dentry->d_name, "..\0", 3);
 | 
|---|
| 336 |         }
 | 
|---|
| 337 | 
 | 
|---|
| 338 |         rc = write_block(root_dblock, 1, root_block);
 | 
|---|
| 339 | 
 | 
|---|
| 340 |         free(root_block);
 | 
|---|
| 341 |         return rc;
 | 
|---|
| 342 | }
 | 
|---|
| 343 | 
 | 
|---|
| 344 | /**Initialize the inode table.
 | 
|---|
| 345 |  *
 | 
|---|
| 346 |  * @param sb            Pointer to the superblock structure.
 | 
|---|
| 347 |  *
 | 
|---|
| 348 |  * @return              EOK on success or an error code.
 | 
|---|
| 349 |  */
 | 
|---|
| 350 | static errno_t init_inode_table(const struct mfs_sb_info *sb)
 | 
|---|
| 351 | {
 | 
|---|
| 352 |         unsigned int i;
 | 
|---|
| 353 |         uint8_t *itable_buf;
 | 
|---|
| 354 |         errno_t rc = EOK;
 | 
|---|
| 355 | 
 | 
|---|
| 356 |         long itable_off = sb->zbmap_blocks + sb->ibmap_blocks + 2;
 | 
|---|
| 357 |         unsigned long itable_size = sb->itable_size;
 | 
|---|
| 358 | 
 | 
|---|
| 359 |         itable_buf = malloc(sb->block_size);
 | 
|---|
| 360 | 
 | 
|---|
| 361 |         if (!itable_buf)
 | 
|---|
| 362 |                 return ENOMEM;
 | 
|---|
| 363 | 
 | 
|---|
| 364 |         memset(itable_buf, 0x00, sb->block_size);
 | 
|---|
| 365 | 
 | 
|---|
| 366 |         for (i = 0; i < itable_size; ++i, ++itable_off) {
 | 
|---|
| 367 |                 rc = write_block(itable_off, 1, itable_buf);
 | 
|---|
| 368 | 
 | 
|---|
| 369 |                 if (rc != EOK)
 | 
|---|
| 370 |                         break;
 | 
|---|
| 371 |         }
 | 
|---|
| 372 | 
 | 
|---|
| 373 |         free(itable_buf);
 | 
|---|
| 374 |         return rc;
 | 
|---|
| 375 | }
 | 
|---|
| 376 | 
 | 
|---|
| 377 | /**Initialize a V1 root inode.
 | 
|---|
| 378 |  *
 | 
|---|
| 379 |  * @param sb            Ponter to the superblock structure.
 | 
|---|
| 380 |  *
 | 
|---|
| 381 |  * @return              EOK on success or an error code.
 | 
|---|
| 382 |  */
 | 
|---|
| 383 | static errno_t make_root_ino(const struct mfs_sb_info *sb)
 | 
|---|
| 384 | {
 | 
|---|
| 385 |         struct mfs_inode *ino_buf;
 | 
|---|
| 386 |         errno_t rc;
 | 
|---|
| 387 | 
 | 
|---|
| 388 |         const long itable_off = sb->zbmap_blocks + sb->ibmap_blocks + 2;
 | 
|---|
| 389 | 
 | 
|---|
| 390 |         const time_t sec = time(NULL);
 | 
|---|
| 391 | 
 | 
|---|
| 392 |         ino_buf = malloc(MFS_BLOCKSIZE);
 | 
|---|
| 393 | 
 | 
|---|
| 394 |         if (!ino_buf)
 | 
|---|
| 395 |                 return ENOMEM;
 | 
|---|
| 396 | 
 | 
|---|
| 397 |         memset(ino_buf, 0x00, MFS_BLOCKSIZE);
 | 
|---|
| 398 | 
 | 
|---|
| 399 |         ino_buf[MFS_ROOT_INO - 1].i_mode = S_IFDIR;
 | 
|---|
| 400 |         ino_buf[MFS_ROOT_INO - 1].i_uid = 0;
 | 
|---|
| 401 |         ino_buf[MFS_ROOT_INO - 1].i_gid = 0;
 | 
|---|
| 402 |         ino_buf[MFS_ROOT_INO - 1].i_size = (sb->longnames ? MFSL_DIRSIZE :
 | 
|---|
| 403 |             MFS_DIRSIZE) * 2;
 | 
|---|
| 404 |         ino_buf[MFS_ROOT_INO - 1].i_mtime = sec;
 | 
|---|
| 405 |         ino_buf[MFS_ROOT_INO - 1].i_nlinks = 2;
 | 
|---|
| 406 |         ino_buf[MFS_ROOT_INO - 1].i_dzone[0] = sb->first_data_zone;
 | 
|---|
| 407 | 
 | 
|---|
| 408 |         rc = write_block(itable_off, 1, ino_buf);
 | 
|---|
| 409 | 
 | 
|---|
| 410 |         free(ino_buf);
 | 
|---|
| 411 |         return rc;
 | 
|---|
| 412 | }
 | 
|---|
| 413 | 
 | 
|---|
| 414 | /**Initialize a Minix V2 root inode on disk, also valid for V3 filesystem.
 | 
|---|
| 415 |  *
 | 
|---|
| 416 |  * @param sb            Pointer to the superblock structure.
 | 
|---|
| 417 |  *
 | 
|---|
| 418 |  * @return              EOK on success or an error code.
 | 
|---|
| 419 |  */
 | 
|---|
| 420 | static errno_t make_root_ino2(const struct mfs_sb_info *sb)
 | 
|---|
| 421 | {
 | 
|---|
| 422 |         struct mfs2_inode *ino_buf;
 | 
|---|
| 423 |         errno_t rc;
 | 
|---|
| 424 | 
 | 
|---|
| 425 |         /* Compute offset of the first inode table block */
 | 
|---|
| 426 |         const long itable_off = sb->zbmap_blocks + sb->ibmap_blocks + 2;
 | 
|---|
| 427 | 
 | 
|---|
| 428 |         const time_t sec = time(NULL);
 | 
|---|
| 429 | 
 | 
|---|
| 430 |         ino_buf = malloc(sb->block_size);
 | 
|---|
| 431 | 
 | 
|---|
| 432 |         if (!ino_buf)
 | 
|---|
| 433 |                 return ENOMEM;
 | 
|---|
| 434 | 
 | 
|---|
| 435 |         memset(ino_buf, 0x00, sb->block_size);
 | 
|---|
| 436 | 
 | 
|---|
| 437 |         ino_buf[MFS_ROOT_INO - 1].i_mode = S_IFDIR;
 | 
|---|
| 438 |         ino_buf[MFS_ROOT_INO - 1].i_uid = 0;
 | 
|---|
| 439 |         ino_buf[MFS_ROOT_INO - 1].i_gid = 0;
 | 
|---|
| 440 |         ino_buf[MFS_ROOT_INO - 1].i_size = MFS3_DIRSIZE * 2;
 | 
|---|
| 441 |         ino_buf[MFS_ROOT_INO - 1].i_mtime = sec;
 | 
|---|
| 442 |         ino_buf[MFS_ROOT_INO - 1].i_atime = sec;
 | 
|---|
| 443 |         ino_buf[MFS_ROOT_INO - 1].i_ctime = sec;
 | 
|---|
| 444 |         ino_buf[MFS_ROOT_INO - 1].i_nlinks = 2;
 | 
|---|
| 445 |         ino_buf[MFS_ROOT_INO - 1].i_dzone[0] = sb->first_data_zone;
 | 
|---|
| 446 | 
 | 
|---|
| 447 |         rc = write_block(itable_off, 1, ino_buf);
 | 
|---|
| 448 | 
 | 
|---|
| 449 |         free(ino_buf);
 | 
|---|
| 450 |         return rc;
 | 
|---|
| 451 | }
 | 
|---|
| 452 | 
 | 
|---|
| 453 | /**Initialize the superblock structure on disk.
 | 
|---|
| 454 |  *
 | 
|---|
| 455 |  * @param sb            Pointer to the superblock structure.
 | 
|---|
| 456 |  *
 | 
|---|
| 457 |  * @return              EOK on success or an error code.
 | 
|---|
| 458 |  */
 | 
|---|
| 459 | static errno_t init_superblock(struct mfs_sb_info *sb)
 | 
|---|
| 460 | {
 | 
|---|
| 461 |         aoff64_t inodes;
 | 
|---|
| 462 |         unsigned long ind;
 | 
|---|
| 463 |         unsigned long ind2;
 | 
|---|
| 464 |         unsigned long zones;
 | 
|---|
| 465 |         errno_t rc;
 | 
|---|
| 466 | 
 | 
|---|
| 467 |         if (sb->longnames)
 | 
|---|
| 468 |                 sb->magic = sb->fs_version == 1 ? MFS_MAGIC_V1L :
 | 
|---|
| 469 |                     MFS_MAGIC_V2L;
 | 
|---|
| 470 | 
 | 
|---|
| 471 |         /* Compute the number of zones on disk */
 | 
|---|
| 472 | 
 | 
|---|
| 473 |         if (sb->fs_version == 1) {
 | 
|---|
| 474 |                 /* Valid only for MFS V1 */
 | 
|---|
| 475 |                 sb->n_zones = sb->dev_nblocks > UINT16_MAX ?
 | 
|---|
| 476 |                     UINT16_MAX : sb->dev_nblocks;
 | 
|---|
| 477 |                 ind = MFS_BLOCKSIZE / sizeof(uint16_t);
 | 
|---|
| 478 |                 ind2 = ind * ind;
 | 
|---|
| 479 |                 sb->max_file_size = (V1_NR_DIRECT_ZONES + ind + ind2) *
 | 
|---|
| 480 |                     MFS_BLOCKSIZE;
 | 
|---|
| 481 |         } else {
 | 
|---|
| 482 |                 /*Valid for MFS V2/V3*/
 | 
|---|
| 483 |                 size_t ptrsize;
 | 
|---|
| 484 |                 if (sb->fs_version == 2)
 | 
|---|
| 485 |                         ptrsize = sizeof(uint16_t);
 | 
|---|
| 486 |                 else
 | 
|---|
| 487 |                         ptrsize = sizeof(uint32_t);
 | 
|---|
| 488 | 
 | 
|---|
| 489 |                 ind = sb->block_size / ptrsize;
 | 
|---|
| 490 |                 ind2 = ind * ind;
 | 
|---|
| 491 |                 zones = V2_NR_DIRECT_ZONES + ind + ind2;
 | 
|---|
| 492 |                 sb->max_file_size = zones * sb->block_size;
 | 
|---|
| 493 |                 sb->n_zones = sb->dev_nblocks > UINT32_MAX ?
 | 
|---|
| 494 |                     UINT32_MAX : sb->dev_nblocks;
 | 
|---|
| 495 | 
 | 
|---|
| 496 |                 if (sb->fs_version == 3) {
 | 
|---|
| 497 |                         if (INT32_MAX / sb->block_size < zones)
 | 
|---|
| 498 |                                 sb->max_file_size = INT32_MAX;
 | 
|---|
| 499 |                         sb->ino_per_block = V3_INODES_PER_BLOCK(sb->block_size);
 | 
|---|
| 500 |                         sb->n_zones /= (sb->block_size / MFS_MIN_BLOCKSIZE);
 | 
|---|
| 501 |                 }
 | 
|---|
| 502 |         }
 | 
|---|
| 503 | 
 | 
|---|
| 504 |         /* Round up the number of inodes to fill block size */
 | 
|---|
| 505 |         if (sb->n_inodes == 0)
 | 
|---|
| 506 |                 inodes = sb->dev_nblocks / 3;
 | 
|---|
| 507 |         else
 | 
|---|
| 508 |                 inodes = sb->n_inodes;
 | 
|---|
| 509 | 
 | 
|---|
| 510 |         if (inodes % sb->ino_per_block)
 | 
|---|
| 511 |                 inodes = ((inodes / sb->ino_per_block) + 1) *
 | 
|---|
| 512 |                     sb->ino_per_block;
 | 
|---|
| 513 | 
 | 
|---|
| 514 |         if (sb->fs_version < 3)
 | 
|---|
| 515 |                 sb->n_inodes = inodes > UINT16_MAX ? UINT16_MAX : inodes;
 | 
|---|
| 516 |         else
 | 
|---|
| 517 |                 sb->n_inodes = inodes > UINT32_MAX ? UINT32_MAX : inodes;
 | 
|---|
| 518 | 
 | 
|---|
| 519 |         /* Compute inode bitmap size in blocks */
 | 
|---|
| 520 |         sb->ibmap_blocks = UPPER(sb->n_inodes, sb->block_size * 8);
 | 
|---|
| 521 | 
 | 
|---|
| 522 |         /* Compute inode table size */
 | 
|---|
| 523 |         sb->itable_size = sb->n_inodes / sb->ino_per_block;
 | 
|---|
| 524 | 
 | 
|---|
| 525 |         /* Compute zone bitmap size in blocks */
 | 
|---|
| 526 |         sb->zbmap_blocks = UPPER(sb->n_zones, sb->block_size * 8);
 | 
|---|
| 527 | 
 | 
|---|
| 528 |         /* Compute first data zone position */
 | 
|---|
| 529 |         sb->first_data_zone = 2 + sb->itable_size +
 | 
|---|
| 530 |             sb->zbmap_blocks + sb->ibmap_blocks;
 | 
|---|
| 531 | 
 | 
|---|
| 532 |         /* Set log2 of zone to block ratio to zero */
 | 
|---|
| 533 |         sb->log2_zone_size = 0;
 | 
|---|
| 534 | 
 | 
|---|
| 535 |         /* Check for errors */
 | 
|---|
| 536 |         if (sb->first_data_zone >= sb->n_zones) {
 | 
|---|
| 537 |                 printf(NAME ": Error! Insufficient disk space");
 | 
|---|
| 538 |                 return ENOMEM;
 | 
|---|
| 539 |         }
 | 
|---|
| 540 | 
 | 
|---|
| 541 |         /* Superblock is now ready to be written on disk */
 | 
|---|
| 542 |         printf(NAME ": %d block size\n", sb->block_size);
 | 
|---|
| 543 |         printf(NAME ": %d inodes\n", (uint32_t) sb->n_inodes);
 | 
|---|
| 544 |         printf(NAME ": %d zones\n", (uint32_t) sb->n_zones);
 | 
|---|
| 545 |         printf(NAME ": inode table blocks = %ld\n", sb->itable_size);
 | 
|---|
| 546 |         printf(NAME ": inode bitmap blocks = %ld\n", sb->ibmap_blocks);
 | 
|---|
| 547 |         printf(NAME ": zone bitmap blocks = %ld\n", sb->zbmap_blocks);
 | 
|---|
| 548 |         printf(NAME ": first data zone = %d\n", (uint32_t)sb->first_data_zone);
 | 
|---|
| 549 |         printf(NAME ": max file size = %u\n", sb->max_file_size);
 | 
|---|
| 550 |         printf(NAME ": long fnames = %s\n", sb->longnames ? "Yes" : "No");
 | 
|---|
| 551 | 
 | 
|---|
| 552 |         if (sb->fs_version == 3)
 | 
|---|
| 553 |                 rc = write_superblock3(sb);
 | 
|---|
| 554 |         else
 | 
|---|
| 555 |                 rc = write_superblock(sb);
 | 
|---|
| 556 | 
 | 
|---|
| 557 |         return rc;
 | 
|---|
| 558 | }
 | 
|---|
| 559 | 
 | 
|---|
| 560 | /**Write the V1/V2 superblock on disk.
 | 
|---|
| 561 |  *
 | 
|---|
| 562 |  * @param sbi           Pointer to the superblock structure to write on disk.
 | 
|---|
| 563 |  *
 | 
|---|
| 564 |  * @return              EOK on success or an error code.
 | 
|---|
| 565 |  */
 | 
|---|
| 566 | static errno_t write_superblock(const struct mfs_sb_info *sbi)
 | 
|---|
| 567 | {
 | 
|---|
| 568 |         struct mfs_superblock *sb;
 | 
|---|
| 569 |         errno_t rc;
 | 
|---|
| 570 | 
 | 
|---|
| 571 |         sb = malloc(MFS_SUPERBLOCK_SIZE);
 | 
|---|
| 572 | 
 | 
|---|
| 573 |         if (!sb)
 | 
|---|
| 574 |                 return ENOMEM;
 | 
|---|
| 575 | 
 | 
|---|
| 576 |         sb->s_ninodes = (uint16_t) sbi->n_inodes;
 | 
|---|
| 577 |         sb->s_nzones = (uint16_t) sbi->n_zones;
 | 
|---|
| 578 |         sb->s_nzones2 = (uint32_t) sbi->n_zones;
 | 
|---|
| 579 |         sb->s_ibmap_blocks = (uint16_t) sbi->ibmap_blocks;
 | 
|---|
| 580 |         sb->s_zbmap_blocks = (uint16_t) sbi->zbmap_blocks;
 | 
|---|
| 581 |         sb->s_first_data_zone = (uint16_t) sbi->first_data_zone;
 | 
|---|
| 582 |         sb->s_log2_zone_size = sbi->log2_zone_size;
 | 
|---|
| 583 |         sb->s_max_file_size = sbi->max_file_size;
 | 
|---|
| 584 |         sb->s_magic = sbi->magic;
 | 
|---|
| 585 |         sb->s_state = MFS_VALID_FS;
 | 
|---|
| 586 | 
 | 
|---|
| 587 |         rc = write_block(MFS_SUPERBLOCK, 1, sb);
 | 
|---|
| 588 |         free(sb);
 | 
|---|
| 589 | 
 | 
|---|
| 590 |         return rc;
 | 
|---|
| 591 | }
 | 
|---|
| 592 | 
 | 
|---|
| 593 | /**Write the V3s superblock on disk.
 | 
|---|
| 594 |  *
 | 
|---|
| 595 |  * @param sbi           Pointer to the superblock structure to write on disk.
 | 
|---|
| 596 |  *
 | 
|---|
| 597 |  * @return              EOK on success or an error code.
 | 
|---|
| 598 |  */
 | 
|---|
| 599 | static errno_t write_superblock3(const struct mfs_sb_info *sbi)
 | 
|---|
| 600 | {
 | 
|---|
| 601 |         struct mfs3_superblock *sb;
 | 
|---|
| 602 |         errno_t rc;
 | 
|---|
| 603 | 
 | 
|---|
| 604 |         sb = malloc(MFS_SUPERBLOCK_SIZE);
 | 
|---|
| 605 | 
 | 
|---|
| 606 |         if (!sb)
 | 
|---|
| 607 |                 return ENOMEM;
 | 
|---|
| 608 | 
 | 
|---|
| 609 |         sb->s_ninodes = (uint32_t) sbi->n_inodes;
 | 
|---|
| 610 |         sb->s_nzones = (uint32_t) sbi->n_zones;
 | 
|---|
| 611 |         sb->s_ibmap_blocks = (uint16_t) sbi->ibmap_blocks;
 | 
|---|
| 612 |         sb->s_zbmap_blocks = (uint16_t) sbi->zbmap_blocks;
 | 
|---|
| 613 |         sb->s_first_data_zone = (uint16_t) sbi->first_data_zone;
 | 
|---|
| 614 |         sb->s_log2_zone_size = sbi->log2_zone_size;
 | 
|---|
| 615 |         sb->s_max_file_size = sbi->max_file_size;
 | 
|---|
| 616 |         sb->s_magic = sbi->magic;
 | 
|---|
| 617 |         sb->s_block_size = sbi->block_size;
 | 
|---|
| 618 |         sb->s_disk_version = 3;
 | 
|---|
| 619 | 
 | 
|---|
| 620 |         rc = block_write_direct(service_id, MFS_SUPERBLOCK << 1, 1 << 1, sb);
 | 
|---|
| 621 |         free(sb);
 | 
|---|
| 622 | 
 | 
|---|
| 623 |         return rc;
 | 
|---|
| 624 | }
 | 
|---|
| 625 | 
 | 
|---|
| 626 | /**Initialize the inode and block bitmaps on disk.
 | 
|---|
| 627 |  *
 | 
|---|
| 628 |  * @param sb            Pointer to the superblock structure.
 | 
|---|
| 629 |  *
 | 
|---|
| 630 |  * @return              EOK on success or an error code.
 | 
|---|
| 631 |  */
 | 
|---|
| 632 | static errno_t init_bitmaps(const struct mfs_sb_info *sb)
 | 
|---|
| 633 | {
 | 
|---|
| 634 |         uint32_t *ibmap_buf, *zbmap_buf;
 | 
|---|
| 635 |         uint8_t *ibmap_buf8, *zbmap_buf8;
 | 
|---|
| 636 |         const unsigned int ibmap_nblocks = sb->ibmap_blocks;
 | 
|---|
| 637 |         const unsigned int zbmap_nblocks = sb->zbmap_blocks;
 | 
|---|
| 638 |         unsigned int i;
 | 
|---|
| 639 |         errno_t rc = EOK;
 | 
|---|
| 640 | 
 | 
|---|
| 641 |         ibmap_buf = malloc(ibmap_nblocks * sb->block_size);
 | 
|---|
| 642 |         zbmap_buf = malloc(zbmap_nblocks * sb->block_size);
 | 
|---|
| 643 | 
 | 
|---|
| 644 |         if (!ibmap_buf || !zbmap_buf) {
 | 
|---|
| 645 |                 rc = ENOMEM;
 | 
|---|
| 646 |                 goto exit;
 | 
|---|
| 647 |         }
 | 
|---|
| 648 | 
 | 
|---|
| 649 |         memset(ibmap_buf, 0xFF, ibmap_nblocks * sb->block_size);
 | 
|---|
| 650 |         memset(zbmap_buf, 0xFF, zbmap_nblocks * sb->block_size);
 | 
|---|
| 651 | 
 | 
|---|
| 652 |         for (i = 2; i < sb->n_inodes + 1; ++i)
 | 
|---|
| 653 |                 mark_bmap(ibmap_buf, i, FREE);
 | 
|---|
| 654 | 
 | 
|---|
| 655 |         for (i = 2; i < sb->n_zones - sb->first_data_zone; ++i)
 | 
|---|
| 656 |                 mark_bmap(zbmap_buf, i, FREE);
 | 
|---|
| 657 | 
 | 
|---|
| 658 |         ibmap_buf8 = (uint8_t *) ibmap_buf;
 | 
|---|
| 659 |         zbmap_buf8 = (uint8_t *) zbmap_buf;
 | 
|---|
| 660 | 
 | 
|---|
| 661 |         int start_block = 2;
 | 
|---|
| 662 | 
 | 
|---|
| 663 |         for (i = 0; i < ibmap_nblocks; ++i) {
 | 
|---|
| 664 |                 if ((rc = write_block(start_block + i,
 | 
|---|
| 665 |                     1, (ibmap_buf8 + i * sb->block_size))) != EOK)
 | 
|---|
| 666 |                         goto exit;
 | 
|---|
| 667 |         }
 | 
|---|
| 668 | 
 | 
|---|
| 669 |         start_block = 2 + ibmap_nblocks;
 | 
|---|
| 670 | 
 | 
|---|
| 671 |         for (i = 0; i < zbmap_nblocks; ++i) {
 | 
|---|
| 672 |                 if ((rc = write_block(start_block + i,
 | 
|---|
| 673 |                     1, (zbmap_buf8 + i * sb->block_size))) != EOK)
 | 
|---|
| 674 |                         goto exit;
 | 
|---|
| 675 |         }
 | 
|---|
| 676 | 
 | 
|---|
| 677 | exit:
 | 
|---|
| 678 |         free(ibmap_buf);
 | 
|---|
| 679 |         free(zbmap_buf);
 | 
|---|
| 680 | 
 | 
|---|
| 681 |         return rc;
 | 
|---|
| 682 | }
 | 
|---|
| 683 | 
 | 
|---|
| 684 | /**Mark a bitmap entry as used or free.
 | 
|---|
| 685 |  *
 | 
|---|
| 686 |  * @param bmap          32-bit pointer to the bitmap in memory.
 | 
|---|
| 687 |  * @param idx           The index in the bitmap of the bit to set at 1 or 0.
 | 
|---|
| 688 |  * @param v             FREE to clear the bit, USED to set the bit.
 | 
|---|
| 689 |  */
 | 
|---|
| 690 | static void mark_bmap(uint32_t *bmap, int idx, int v)
 | 
|---|
| 691 | {
 | 
|---|
| 692 |         if (v == FREE)
 | 
|---|
| 693 |                 bmap[idx / 32] &= ~(1 << (idx % 32));
 | 
|---|
| 694 |         else
 | 
|---|
| 695 |                 bmap[idx / 32] |= 1 << (idx % 32);
 | 
|---|
| 696 | }
 | 
|---|
| 697 | 
 | 
|---|
| 698 | /**Write a block on disk.
 | 
|---|
| 699 |  *
 | 
|---|
| 700 |  * @param off           64-bit block offset on disk.
 | 
|---|
| 701 |  * @param size          size of the block.
 | 
|---|
| 702 |  * @param data          Pointer to the block content.
 | 
|---|
| 703 |  *
 | 
|---|
| 704 |  * @return              EOK on success or a error number.
 | 
|---|
| 705 |  */
 | 
|---|
| 706 | static inline errno_t write_block(aoff64_t off, size_t size, const void *data)
 | 
|---|
| 707 | {
 | 
|---|
| 708 |         if (shift == 3) {
 | 
|---|
| 709 |                 errno_t rc;
 | 
|---|
| 710 |                 aoff64_t tmp_off = off << 1;
 | 
|---|
| 711 |                 uint8_t *data_ptr = (uint8_t *) data;
 | 
|---|
| 712 | 
 | 
|---|
| 713 |                 rc = block_write_direct(service_id, tmp_off << 2,
 | 
|---|
| 714 |                     size << 2, data_ptr);
 | 
|---|
| 715 | 
 | 
|---|
| 716 |                 if (rc != EOK)
 | 
|---|
| 717 |                         return rc;
 | 
|---|
| 718 | 
 | 
|---|
| 719 |                 data_ptr += 2048;
 | 
|---|
| 720 |                 tmp_off++;
 | 
|---|
| 721 | 
 | 
|---|
| 722 |                 return block_write_direct(service_id, tmp_off << 2,
 | 
|---|
| 723 |                     size << 2, data_ptr);
 | 
|---|
| 724 |         }
 | 
|---|
| 725 |         return block_write_direct(service_id, off << shift,
 | 
|---|
| 726 |             size << shift, data);
 | 
|---|
| 727 | }
 | 
|---|
| 728 | 
 | 
|---|
| 729 | static void help_cmd_mkmfs(help_level_t level)
 | 
|---|
| 730 | {
 | 
|---|
| 731 |         if (level == HELP_SHORT) {
 | 
|---|
| 732 |                 printf(NAME ": tool to create new Minix file systems\n");
 | 
|---|
| 733 |         } else {
 | 
|---|
| 734 |                 printf("Usage: [options] device\n"
 | 
|---|
| 735 |                     "-1         Make a Minix version 1 filesystem\n"
 | 
|---|
| 736 |                     "-2         Make a Minix version 2 filesystem\n"
 | 
|---|
| 737 |                     "-b ##      Specify the block size in bytes (V3 only),\n"
 | 
|---|
| 738 |                     "           valid block size values are 1024, 2048 and"
 | 
|---|
| 739 |                     /* ...   */ " 4096 bytes per block\n"
 | 
|---|
| 740 |                     "-i ##      Specify the number of inodes"
 | 
|---|
| 741 |                     /* ...   */ " for the filesystem\n"
 | 
|---|
| 742 |                     "-l         Use 30-char long filenames (V1/V2 only)\n");
 | 
|---|
| 743 |         }
 | 
|---|
| 744 | }
 | 
|---|
| 745 | 
 | 
|---|
| 746 | /** Check if a given number is a power of two.
 | 
|---|
| 747 |  *
 | 
|---|
| 748 |  * @param n     The number to check.
 | 
|---|
| 749 |  *
 | 
|---|
| 750 |  * @return      true if it is a power of two, false otherwise.
 | 
|---|
| 751 |  */
 | 
|---|
| 752 | static bool is_power_of_two(uint32_t n)
 | 
|---|
| 753 | {
 | 
|---|
| 754 |         if (n == 0)
 | 
|---|
| 755 |                 return false;
 | 
|---|
| 756 | 
 | 
|---|
| 757 |         return (n & (n - 1)) == 0;
 | 
|---|
| 758 | }
 | 
|---|
| 759 | 
 | 
|---|
| 760 | /**
 | 
|---|
| 761 |  * @}
 | 
|---|
| 762 |  */
 | 
|---|