The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/adfs/super.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  *  linux/fs/adfs/super.c
    3  *
    4  *  Copyright (C) 1997-1999 Russell King
    5  *
    6  * This program is free software; you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License version 2 as
    8  * published by the Free Software Foundation.
    9  */
   10 #include <linux/version.h>
   11 #include <linux/module.h>
   12 #include <linux/errno.h>
   13 #include <linux/fs.h>
   14 #include <linux/adfs_fs.h>
   15 #include <linux/slab.h>
   16 #include <linux/sched.h>
   17 #include <linux/stat.h>
   18 #include <linux/string.h>
   19 #include <linux/locks.h>
   20 #include <linux/init.h>
   21 
   22 #include <asm/bitops.h>
   23 #include <asm/uaccess.h>
   24 #include <asm/system.h>
   25 
   26 #include <stdarg.h>
   27 
   28 #include "adfs.h"
   29 #include "dir_f.h"
   30 #include "dir_fplus.h"
   31 
   32 void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
   33 {
   34         char error_buf[128];
   35         va_list args;
   36 
   37         va_start(args, fmt);
   38         vsprintf(error_buf, fmt, args);
   39         va_end(args);
   40 
   41         printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",
   42                 bdevname(sb->s_dev), function ? ": " : "",
   43                 function ? function : "", error_buf);
   44 }
   45 
   46 static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
   47 {
   48         int i;
   49 
   50         /* sector size must be 256, 512 or 1024 bytes */
   51         if (dr->log2secsize != 8 &&
   52             dr->log2secsize != 9 &&
   53             dr->log2secsize != 10)
   54                 return 1;
   55 
   56         /* idlen must be at least log2secsize + 3 */
   57         if (dr->idlen < dr->log2secsize + 3)
   58                 return 1;
   59 
   60         /* we cannot have such a large disc that we
   61          * are unable to represent sector offsets in
   62          * 32 bits.  This works out at 2.0 TB.
   63          */
   64         if (dr->disc_size_high >> dr->log2secsize)
   65                 return 1;
   66 
   67         /*
   68          * The following checks are not required for F+
   69          * stage 1.
   70          */
   71 #if 0
   72         /* idlen must be smaller be no greater than 15 */
   73         if (dr->idlen > 15)
   74                 return 1;
   75 
   76         /* nzones must be less than 128 for the root
   77          * directory to be addressable
   78          */
   79         if (dr->nzones >= 128 && dr->nzones_high == 0)
   80                 return 1;
   81 
   82         /* root must be of the form 0x2.. */
   83         if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200)
   84                 return 1;
   85 #else
   86         /*
   87          * Stage 2 F+ does not require the following check
   88          */
   89 #if 0
   90         /* idlen must be no greater than 16 v2 [1.0] */
   91         if (dr->idlen > 16)
   92                 return 1;
   93 
   94         /* we can't handle F+ discs yet */
   95         if (dr->format_version || dr->root_size)
   96                 return 1;
   97 
   98 #else
   99         /* idlen must be no greater than 19 v2 [1.0] */
  100         if (dr->idlen > 19)
  101                 return 1;
  102 #endif
  103 #endif
  104 
  105         /* reserved bytes should be zero */
  106         for (i = 0; i < sizeof(dr->unused52); i++)
  107                 if (dr->unused52[i] != 0)
  108                         return 1;
  109 
  110         return 0;
  111 }
  112 
  113 static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
  114 {
  115         unsigned int v0, v1, v2, v3;
  116         int i;
  117 
  118         v0 = v1 = v2 = v3 = 0;
  119         for (i = sb->s_blocksize - 4; i; i -= 4) {
  120                 v0 += map[i]     + (v3 >> 8);
  121                 v3 &= 0xff;
  122                 v1 += map[i + 1] + (v0 >> 8);
  123                 v0 &= 0xff;
  124                 v2 += map[i + 2] + (v1 >> 8);
  125                 v1 &= 0xff;
  126                 v3 += map[i + 3] + (v2 >> 8);
  127                 v2 &= 0xff;
  128         }
  129         v0 +=           v3 >> 8;
  130         v1 += map[1] + (v0 >> 8);
  131         v2 += map[2] + (v1 >> 8);
  132         v3 += map[3] + (v2 >> 8);
  133 
  134         return v0 ^ v1 ^ v2 ^ v3;
  135 }
  136 
  137 static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
  138 {
  139         unsigned char crosscheck = 0, zonecheck = 1;
  140         int i;
  141 
  142         for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
  143                 unsigned char *map;
  144 
  145                 map = dm[i].dm_bh->b_data;
  146 
  147                 if (adfs_calczonecheck(sb, map) != map[0]) {
  148                         adfs_error(sb, "zone %d fails zonecheck", i);
  149                         zonecheck = 0;
  150                 }
  151                 crosscheck ^= map[3];
  152         }
  153         if (crosscheck != 0xff)
  154                 adfs_error(sb, "crosscheck != 0xff");
  155         return crosscheck == 0xff && zonecheck;
  156 }
  157 
  158 static void adfs_put_super(struct super_block *sb)
  159 {
  160         int i;
  161 
  162         for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
  163                 brelse(sb->u.adfs_sb.s_map[i].dm_bh);
  164         kfree(sb->u.adfs_sb.s_map);
  165 }
  166 
  167 static int parse_options(struct super_block *sb, char *options)
  168 {
  169         char *value, *opt;
  170 
  171         if (!options)
  172                 return 0;
  173 
  174         for (opt = strtok(options, ","); opt != NULL; opt = strtok(NULL, ",")) {
  175                 value = strchr(opt, '=');
  176                 if (value)
  177                         *value++ = '\0';
  178 
  179                 if (!strcmp(opt, "uid")) {      /* owner of all files */
  180                         if (!value || !*value)
  181                                 return -EINVAL;
  182                         sb->u.adfs_sb.s_uid = simple_strtoul(value, &value, 0);
  183                         if (*value)
  184                                 return -EINVAL;
  185                 } else
  186                 if (!strcmp(opt, "gid")) {      /* group owner of all files */
  187                         if (!value || !*value)
  188                                 return -EINVAL;
  189                         sb->u.adfs_sb.s_gid = simple_strtoul(value, &value, 0);
  190                         if (*value)
  191                                 return -EINVAL;
  192                 } else
  193                 if (!strcmp(opt, "ownmask")) {  /* owner permission mask */
  194                         if (!value || !*value)
  195                                 return -EINVAL;
  196                         sb->u.adfs_sb.s_owner_mask = simple_strtoul(value, &value, 8);
  197                         if (*value)
  198                                 return -EINVAL;
  199                 } else
  200                 if (!strcmp(opt, "othmask")) {  /* others permission mask */
  201                         if (!value || !*value)
  202                                 return -EINVAL;
  203                         sb->u.adfs_sb.s_other_mask = simple_strtoul(value, &value, 8);
  204                         if (*value)
  205                                 return -EINVAL;
  206                 } else {                        /* eh? say again. */
  207                         printk("ADFS-fs: unrecognised mount option %s\n", opt);
  208                         return -EINVAL;
  209                 }
  210         }
  211         return 0;
  212 }
  213 
  214 static int adfs_remount(struct super_block *sb, int *flags, char *data)
  215 {
  216         return parse_options(sb, data);
  217 }
  218 
  219 static int adfs_statfs(struct super_block *sb, struct statfs *buf)
  220 {
  221         struct adfs_sb_info *asb = &sb->u.adfs_sb;
  222 
  223         buf->f_type    = ADFS_SUPER_MAGIC;
  224         buf->f_namelen = asb->s_namelen;
  225         buf->f_bsize   = sb->s_blocksize;
  226         buf->f_blocks  = asb->s_size;
  227         buf->f_files   = asb->s_ids_per_zone * asb->s_map_size;
  228         buf->f_bavail  =
  229         buf->f_bfree   = adfs_map_free(sb);
  230         buf->f_ffree   = buf->f_bfree * buf->f_files / buf->f_blocks;
  231 
  232         return 0;
  233 }
  234 
  235 static struct super_operations adfs_sops = {
  236         write_inode:    adfs_write_inode,
  237         put_super:      adfs_put_super,
  238         statfs:         adfs_statfs,
  239         remount_fs:     adfs_remount,
  240 };
  241 
  242 static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
  243 {
  244         struct adfs_discmap *dm;
  245         unsigned int map_addr, zone_size, nzones;
  246         int i, zone;
  247 
  248         nzones    = sb->u.adfs_sb.s_map_size;
  249         zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
  250         map_addr  = (nzones >> 1) * zone_size -
  251                      ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
  252         map_addr  = signed_asl(map_addr, sb->u.adfs_sb.s_map2blk);
  253 
  254         sb->u.adfs_sb.s_ids_per_zone = zone_size / (sb->u.adfs_sb.s_idlen + 1);
  255 
  256         dm = kmalloc(nzones * sizeof(*dm), GFP_KERNEL);
  257         if (dm == NULL) {
  258                 adfs_error(sb, "not enough memory");
  259                 return NULL;
  260         }
  261 
  262         for (zone = 0; zone < nzones; zone++, map_addr++) {
  263                 dm[zone].dm_startbit = 0;
  264                 dm[zone].dm_endbit   = zone_size;
  265                 dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
  266                 dm[zone].dm_bh       = sb_bread(sb, map_addr);
  267 
  268                 if (!dm[zone].dm_bh) {
  269                         adfs_error(sb, "unable to read map");
  270                         goto error_free;
  271                 }
  272         }
  273 
  274         /* adjust the limits for the first and last map zones */
  275         i = zone - 1;
  276         dm[0].dm_startblk = 0;
  277         dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
  278         dm[i].dm_endbit   = (dr->disc_size_high << (32 - dr->log2bpmb)) +
  279                             (dr->disc_size >> dr->log2bpmb) +
  280                             (ADFS_DR_SIZE_BITS - i * zone_size);
  281 
  282         if (adfs_checkmap(sb, dm))
  283                 return dm;
  284 
  285         adfs_error(sb, NULL, "map corrupted");
  286 
  287 error_free:
  288         while (--zone >= 0)
  289                 brelse(dm[zone].dm_bh);
  290 
  291         kfree(dm);
  292         return NULL;
  293 }
  294 
  295 static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits)
  296 {
  297         unsigned long discsize;
  298 
  299         discsize  = le32_to_cpu(dr->disc_size_high) << (32 - block_bits);
  300         discsize |= le32_to_cpu(dr->disc_size) >> block_bits;
  301 
  302         return discsize;
  303 }
  304 
  305 struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent)
  306 {
  307         struct adfs_discrecord *dr;
  308         struct buffer_head *bh;
  309         struct object_info root_obj;
  310         unsigned char *b_data;
  311         kdev_t dev = sb->s_dev;
  312 
  313         /* set default options */
  314         sb->u.adfs_sb.s_uid = 0;
  315         sb->u.adfs_sb.s_gid = 0;
  316         sb->u.adfs_sb.s_owner_mask = S_IRWXU;
  317         sb->u.adfs_sb.s_other_mask = S_IRWXG | S_IRWXO;
  318 
  319         if (parse_options(sb, data))
  320                 goto error;
  321 
  322         sb->s_blocksize = BLOCK_SIZE;
  323         set_blocksize(dev, BLOCK_SIZE);
  324         if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) {
  325                 adfs_error(sb, "unable to read superblock");
  326                 goto error;
  327         }
  328 
  329         b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
  330 
  331         if (adfs_checkbblk(b_data)) {
  332                 if (!silent)
  333                         printk("VFS: Can't find an adfs filesystem on dev "
  334                                 "%s.\n", bdevname(dev));
  335                 goto error_free_bh;
  336         }
  337 
  338         dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
  339 
  340         /*
  341          * Do some sanity checks on the ADFS disc record
  342          */
  343         if (adfs_checkdiscrecord(dr)) {
  344                 if (!silent)
  345                         printk("VPS: Can't find an adfs filesystem on dev "
  346                                 "%s.\n", bdevname(dev));
  347                 goto error_free_bh;
  348         }
  349 
  350         sb->s_blocksize_bits = dr->log2secsize;
  351         sb->s_blocksize = 1 << sb->s_blocksize_bits;
  352         if (sb->s_blocksize != BLOCK_SIZE &&
  353             (sb->s_blocksize == 512 || sb->s_blocksize == 1024 ||
  354              sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) {
  355 
  356                 brelse(bh);
  357                 set_blocksize(dev, sb->s_blocksize);
  358                 bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize);
  359                 if (!bh) {
  360                         adfs_error(sb, "couldn't read superblock on "
  361                                 "2nd try.");
  362                         goto error;
  363                 }
  364                 b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
  365                 if (adfs_checkbblk(b_data)) {
  366                         adfs_error(sb, "disc record mismatch, very weird!");
  367                         goto error_free_bh;
  368                 }
  369                 dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
  370         }
  371         if (sb->s_blocksize != bh->b_size) {
  372                 if (!silent)
  373                         printk(KERN_ERR "VFS: Unsupported blocksize on dev "
  374                                 "%s.\n", bdevname(dev));
  375                 goto error_free_bh;
  376         }
  377 
  378         /*
  379          * blocksize on this device should now be set to the ADFS log2secsize
  380          */
  381 
  382         sb->s_magic              = ADFS_SUPER_MAGIC;
  383         sb->u.adfs_sb.s_idlen    = dr->idlen;
  384         sb->u.adfs_sb.s_map_size = dr->nzones | (dr->nzones_high << 8);
  385         sb->u.adfs_sb.s_map2blk  = dr->log2bpmb - dr->log2secsize;
  386         sb->u.adfs_sb.s_size     = adfs_discsize(dr, sb->s_blocksize_bits);
  387         sb->u.adfs_sb.s_version  = dr->format_version;
  388         sb->u.adfs_sb.s_log2sharesize = dr->log2sharesize;
  389         
  390         sb->u.adfs_sb.s_map = adfs_read_map(sb, dr);
  391         if (!sb->u.adfs_sb.s_map)
  392                 goto error_free_bh;
  393 
  394         brelse(bh);
  395 
  396         /*
  397          * set up enough so that we can read an inode
  398          */
  399         sb->s_op = &adfs_sops;
  400 
  401         dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0].dm_bh->b_data + 4);
  402 
  403         root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);
  404         root_obj.name_len  = 0;
  405         root_obj.loadaddr  = 0;
  406         root_obj.execaddr  = 0;
  407         root_obj.size      = ADFS_NEWDIR_SIZE;
  408         root_obj.attr      = ADFS_NDA_DIRECTORY   | ADFS_NDA_OWNER_READ |
  409                              ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ;
  410 
  411         /*
  412          * If this is a F+ disk with variable length directories,
  413          * get the root_size from the disc record.
  414          */
  415         if (sb->u.adfs_sb.s_version) {
  416                 root_obj.size = dr->root_size;
  417                 sb->u.adfs_sb.s_dir     = &adfs_fplus_dir_ops;
  418                 sb->u.adfs_sb.s_namelen = ADFS_FPLUS_NAME_LEN;
  419         } else {
  420                 sb->u.adfs_sb.s_dir     = &adfs_f_dir_ops;
  421                 sb->u.adfs_sb.s_namelen = ADFS_F_NAME_LEN;
  422         }
  423 
  424         sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj));
  425         if (!sb->s_root) {
  426                 int i;
  427 
  428                 for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
  429                         brelse(sb->u.adfs_sb.s_map[i].dm_bh);
  430                 kfree(sb->u.adfs_sb.s_map);
  431                 adfs_error(sb, "get root inode failed\n");
  432                 goto error;
  433         } else
  434                 sb->s_root->d_op = &adfs_dentry_operations;
  435         return sb;
  436 
  437 error_free_bh:
  438         brelse(bh);
  439 error:
  440         return NULL;
  441 }
  442 
  443 static DECLARE_FSTYPE_DEV(adfs_fs_type, "adfs", adfs_read_super);
  444 
  445 static int __init init_adfs_fs(void)
  446 {
  447         return register_filesystem(&adfs_fs_type);
  448 }
  449 
  450 static void __exit exit_adfs_fs(void)
  451 {
  452         unregister_filesystem(&adfs_fs_type);
  453 }
  454 
  455 EXPORT_NO_SYMBOLS;
  456 
  457 module_init(init_adfs_fs)
  458 module_exit(exit_adfs_fs)

Cache object: 0f5e10c73c261c5518cdb4746362676d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.