Ticket #4602: dosfs_volume.diff

File dosfs_volume.diff, 15.7 KB (added by romain, 14 years ago)

updated patch

  • dosfs.c

     
    160160    }
    161161}
    162162
     163
    163164static bool
    164165dosfs_read_label(bool fat32, uint8 *buffer, char *label)
    165166{
     
    177178}
    178179
    179180
    180 static int
    181 lock_removable_device(int fd, bool state)
     181static nspace*
     182volume_init(int fd, uint8* buf,
     183    const int flags, int fs_flags,
     184    device_geometry *geo)
    182185{
    183     return ioctl(fd, B_SCSI_PREVENT_ALLOW, &state, sizeof(state));
    184 }
    185 
    186 
    187 static status_t
    188 mount_fat_disk(const char *path, fs_volume *_vol, const int flags,
    189     nspace** newVol, int fs_flags, int op_sync_mode)
    190 {
    191186    nspace *vol = NULL;
    192     uint8 buf[512];
     187    uint8 media_buf[512];
    193188    int i;
    194     device_geometry geo;
    195189    status_t err;
    196 
    197     *newVol = NULL;
     190           
    198191    if ((vol = (nspace *)calloc(sizeof(nspace), 1)) == NULL) {
    199192        dprintf("dosfs error: out of memory\n");
    200         return B_NO_MEMORY;
     193        return NULL;
    201194    }
    202195
    203     vol->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME;
     196    vol->flags = flags;
    204197    vol->fs_flags = fs_flags;
     198    vol->fd = fd;
    205199
    206     // open read-only for now
    207     if ((err = (vol->fd = open(path, O_RDONLY | O_NOCACHE))) < 0) {
    208         dprintf("dosfs error: unable to open %s (%s)\n", path, strerror(err));
    209         goto error0;
    210     }
    211 
    212     // get device characteristics
    213     if (ioctl(vol->fd, B_GET_GEOMETRY, &geo) < 0) {
    214         struct stat st;
    215         if (fstat(vol->fd, &st) >= 0 && S_ISREG(st.st_mode)) {
    216             /* support mounting disk images */
    217             geo.bytes_per_sector = 0x200;
    218             geo.sectors_per_track = 1;
    219             geo.cylinder_count = st.st_size / 0x200;
    220             geo.head_count = 1;
    221             geo.read_only = !(st.st_mode & S_IWUSR);
    222             geo.removable = true;
    223         } else {
    224             dprintf("dosfs error: error getting device geometry\n");
    225             goto error0;
    226         }
    227     }
    228 
    229     if (geo.bytes_per_sector != 0x200 && geo.bytes_per_sector != 0x400
    230         && geo.bytes_per_sector != 0x800 && geo.bytes_per_sector != 0x1000) {
    231         dprintf("dosfs error: unsupported device block size (%lu)\n",
    232             geo.bytes_per_sector);
    233         goto error0;
    234     }
    235 
    236     if (geo.removable) {
    237         DPRINTF(0, ("%s is removable\n", path));
    238         vol->flags |= B_FS_IS_REMOVABLE;
    239     }
    240 
    241     if (geo.read_only || (flags & B_MOUNT_READ_ONLY)) {
    242         DPRINTF(0, ("%s is read-only\n", path));
    243         vol->flags |= B_FS_IS_READONLY;
    244     } else {
    245         // reopen it with read/write permissions
    246         close(vol->fd);
    247         if ((err = (vol->fd = open(path, O_RDWR | O_NOCACHE))) < 0) {
    248             dprintf("dosfs error: unable to open %s (%s)\n", path,
    249                 strerror(err));
    250             goto error0;
    251         }
    252 
    253         if ((vol->flags & B_FS_IS_REMOVABLE)
    254             && (vol->fs_flags & FS_FLAGS_LOCK_DOOR))
    255             lock_removable_device(vol->fd, true);
    256     }
    257 
    258     // see if we need to go into op sync mode
    259     vol->fs_flags &= ~FS_FLAGS_OP_SYNC;
    260     switch (op_sync_mode) {
    261         case 1:
    262             if ((vol->flags & B_FS_IS_REMOVABLE) == 0) {
    263                 // we're not removable, so skip op_sync
    264                 break;
    265             }
    266             // supposed to fall through
    267 
    268         case 2:
    269             dprintf("dosfs: mounted with op_sync enabled\n");
    270             vol->fs_flags |= FS_FLAGS_OP_SYNC;
    271             break;
    272 
    273         case 0:
    274         default:
    275             break;
    276     }
    277 
    278     // read in the boot sector
    279     if ((err = read_pos(vol->fd, 0, (void *)buf, 512)) != 512) {
    280         dprintf("dosfs error: error reading boot sector\n");
    281         goto error;
    282     }
    283 
    284200    // only check boot signature on hard disks to account for broken mtools
    285201    // behavior
    286202    if ((buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) && buf[0x15] == 0xf8)
     
    376292        if (vol->total_sectors == 0)
    377293            vol->total_sectors = read32(buf, 0x20);
    378294
    379         {
     295        if (geo != NULL) {
    380296            /*
    381               Zip disks that were formatted at iomega have an incorrect number
    382               of sectors.  They say that they have 196576 sectors but they
    383               really only have 196192.  This check is a work-around for their
    384               brain-deadness.
     297                Zip disks that were formatted at iomega have an incorrect number
     298                of sectors.  They say that they have 196576 sectors but they
     299                really only have 196192.  This check is a work-around for their
     300                brain-deadness.
    385301            */
    386302            unsigned char bogus_zip_data[] = {
    387303                0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,
    388304                0xf8, 0xc0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00
    389305            };
    390 
     306   
    391307            if (memcmp(buf + 0x0b, bogus_zip_data, sizeof(bogus_zip_data)) == 0
    392308                && vol->total_sectors == 196576
    393                 && ((off_t)geo.sectors_per_track * (off_t)geo.cylinder_count
    394                     * (off_t)geo.head_count) == 196192) {
     309                && ((off_t)geo->sectors_per_track * (off_t)geo->cylinder_count
     310                    * (off_t)geo->head_count) == 196192) {
    395311                vol->total_sectors = 196192;
    396312            }
    397313        }
     
    422338            vol->vol_entry = -1;
    423339    }
    424340
    425     /* check that the partition is large enough to contain the file system */
    426     if (vol->total_sectors > geo.sectors_per_track * geo.cylinder_count
    427             * geo.head_count) {
    428         dprintf("dosfs: volume extends past end of partition\n");
    429         err = B_PARTITION_TOO_SMALL;
    430         goto error;
    431     }
    432 
    433341    // perform sanity checks on the FAT
    434342
    435343    // the media descriptor in active FAT should match the one in the BPB
    436344    if ((err = read_pos(vol->fd, vol->bytes_per_sector * (vol->reserved_sectors
    437345            + vol->active_fat * vol->sectors_per_fat),
    438             (void *)buf, 0x200)) != 0x200) {
     346            (void *)media_buf, 0x200)) != 0x200) {
    439347        dprintf("dosfs error: error reading FAT\n");
    440348        goto error;
    441349    }
    442350
    443     if (buf[0] != vol->media_descriptor) {
    444         dprintf("dosfs error: media descriptor mismatch (%x != %x)\n", buf[0],
     351    if (media_buf[0] != vol->media_descriptor) {
     352        dprintf("dosfs error: media descriptor mismatch (%x != %x)\n", media_buf[0],
    445353            vol->media_descriptor);
    446354        goto error;
    447355    }
    448356
    449357    if (vol->fat_mirrored) {
    450358        uint32 i;
    451         uint8 buf2[512];
     359        uint8 mirror_media_buf[512];
    452360        for (i = 0; i < vol->fat_count; i++) {
    453361            if (i != vol->active_fat) {
    454362                DPRINTF(1, ("checking fat #%ld\n", i));
    455                 buf2[0] = ~buf[0];
     363                mirror_media_buf[0] = ~media_buf[0];
    456364                if ((err = read_pos(vol->fd, vol->bytes_per_sector
    457365                        * (vol->reserved_sectors + vol->sectors_per_fat * i),
    458                         (void *)buf2, 0x200)) != 0x200) {
     366                        (void *)mirror_media_buf, 0x200)) != 0x200) {
    459367                    dprintf("dosfs error: error reading FAT %ld\n", i);
    460368                    goto error;
    461369                }
    462370
    463                 if (buf2[0] != vol->media_descriptor) {
     371                if (mirror_media_buf[0] != vol->media_descriptor) {
    464372                    dprintf("dosfs error: media descriptor mismatch in fat # "
    465                         "%ld (%x != %x)\n", i, buf2[0], vol->media_descriptor);
     373                        "%ld (%x != %x)\n", i, mirror_media_buf[0], vol->media_descriptor);
    466374                    goto error;
    467375                }
    468376#if 0
    469377                // checking for exact matches of fats is too
    470378                // restrictive; allow these to go through in
    471379                // case the fat is corrupted for some reason
    472                 if (memcmp(buf, buf2, 0x200)) {
     380                if (memcmp(media_buf, mirror_media_buf, 0x200)) {
    473381                    dprintf("dosfs error: fat %d doesn't match active fat "
    474382                        "(%d)\n", i, vol->active_fat);
    475383                    goto error;
     
    479387        }
    480388    }
    481389
     390
    482391    // now we are convinced of the drive's validity
    483392
    484     vol->volume = _vol;
    485     vol->id = _vol->id;
    486     strncpy(vol->device, path, sizeof(vol->device));
    487 
    488393    // this will be updated later if fsinfo exists
    489394    vol->last_allocated = 2;
    490 
    491395    vol->beos_vnid = INVALID_VNID_BITS_MASK;
    492     {
    493         void *handle;
    494         handle = load_driver_settings("fat");
    495         vol->respect_disk_image =
    496                 get_driver_boolean_parameter(handle, "respect", true, true);
    497         unload_driver_settings(handle);
    498     }
    499396
    500397    // initialize block cache
    501398    vol->fBlockCache = block_cache_create(vol->fd, vol->total_sectors,
     
    505402        goto error;
    506403    }
    507404
    508     // as well as the vnode cache
    509     if (init_vcache(vol) != B_OK) {
    510         dprintf("dosfs error: error initializing vnode cache\n");
    511         goto error1;
    512     }
     405    // find volume label (supercedes any label in the bpb)
     406    {
     407        struct diri diri;
     408        uint8 *buffer;
     409        buffer = diri_init(vol, vol->root_vnode.cluster, 0, &diri);
     410        for (; buffer; buffer = diri_next_entry(&diri)) {
     411            if ((buffer[0x0b] & FAT_VOLUME) && (buffer[0x0b] != 0xf)
     412                    && (buffer[0] != 0xe5)) {
     413                vol->vol_entry = diri.current_index;
     414                memcpy(vol->vol_label, buffer, 11);
     415                dosfs_trim_spaces(vol->vol_label);
     416                break;
     417            }
     418        }
    513419
    514     // and the dlist cache
    515     if (dlist_init(vol) != B_OK) {
    516         dprintf("dosfs error: error initializing dlist cache\n");
    517         goto error2;
     420        diri_free(&diri);
    518421    }
    519422
     423    DPRINTF(0, ("root vnode id = %Lx\n", vol->root_vnode.vnid));
     424    DPRINTF(0, ("volume label [%s] (%lx)\n", vol->vol_label, vol->vol_entry));
     425
     426    // steal a trick from bfs
     427    if (!memcmp(vol->vol_label, "__RO__     ", 11))
     428        vol->flags |= B_FS_IS_READONLY;
     429
     430    return vol;
     431
     432error:
     433    free(vol);
     434    return NULL;
     435}
     436
     437
     438static void
     439volume_uninit(nspace *vol)
     440{
     441    block_cache_delete(vol->fBlockCache, false);
     442    free(vol);
     443}
     444
     445
     446static void
     447volume_count_free_cluster(nspace *vol)
     448{
     449    status_t err;
     450
    520451    if (vol->flags & B_FS_IS_READONLY)
    521452        vol->free_clusters = 0;
    522453    else {
     
    539470            if ((err = count_free_clusters(vol)) < 0) {
    540471                dprintf("dosfs error: error counting free clusters (%s)\n",
    541472                    strerror(err));
    542                 goto error3;
     473                return;
    543474            }
    544475            vol->free_clusters = err;
    545476        }
    546477    }
     478}
    547479
     480
     481static int
     482lock_removable_device(int fd, bool state)
     483{
     484    return ioctl(fd, B_SCSI_PREVENT_ALLOW, &state, sizeof(state));
     485}
     486
     487
     488static status_t
     489mount_fat_disk(const char *path, fs_volume *_vol, const int flags,
     490    nspace** newVol, int fs_flags, int op_sync_mode)
     491{
     492    nspace *vol = NULL;
     493    uint8 buf[512];
     494    device_geometry geo;
     495    status_t err;
     496    int fd;
     497    int vol_flags;
     498
     499    vol_flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME;
     500 
     501    // open read-only for now
     502    if ((err = (fd = open(path, O_RDONLY | O_NOCACHE))) < 0) {
     503        dprintf("dosfs error: unable to open %s (%s)\n", path, strerror(err));
     504        goto error0;
     505    }
     506
     507    // get device characteristics
     508    if (ioctl(fd, B_GET_GEOMETRY, &geo) < 0) {
     509        struct stat st;
     510        if (fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) {
     511            /* support mounting disk images */
     512            geo.bytes_per_sector = 0x200;
     513            geo.sectors_per_track = 1;
     514            geo.cylinder_count = st.st_size / 0x200;
     515            geo.head_count = 1;
     516            geo.read_only = !(st.st_mode & S_IWUSR);
     517            geo.removable = true;
     518        } else {
     519            dprintf("dosfs error: error getting device geometry\n");
     520            goto error1;
     521        }
     522    }
     523
     524    if (geo.bytes_per_sector != 0x200 && geo.bytes_per_sector != 0x400
     525        && geo.bytes_per_sector != 0x800 && geo.bytes_per_sector != 0x1000) {
     526        dprintf("dosfs error: unsupported device block size (%lu)\n",
     527            geo.bytes_per_sector);
     528        goto error1;
     529    }
     530
     531    if (geo.removable) {
     532        DPRINTF(0, ("%s is removable\n", path));
     533        vol_flags |= B_FS_IS_REMOVABLE;
     534    }
     535
     536    if (geo.read_only || (flags & B_MOUNT_READ_ONLY)) {
     537        DPRINTF(0, ("%s is read-only\n", path));
     538        vol_flags |= B_FS_IS_READONLY;
     539    } else {
     540        // reopen it with read/write permissions
     541        close(fd);
     542        if ((err = (fd = open(path, O_RDWR | O_NOCACHE))) < 0) {
     543            dprintf("dosfs error: unable to open %s (%s)\n", path,
     544                strerror(err));
     545            goto error0;
     546        }
     547
     548        if ((vol_flags & B_FS_IS_REMOVABLE)
     549            && (fs_flags & FS_FLAGS_LOCK_DOOR))
     550            lock_removable_device(fd, true);
     551    }
     552
     553    // see if we need to go into op sync mode
     554    fs_flags &= ~FS_FLAGS_OP_SYNC;
     555    switch (op_sync_mode) {
     556        case 1:
     557            if ((vol_flags & B_FS_IS_REMOVABLE) == 0) {
     558                // we're not removable, so skip op_sync
     559                break;
     560            }
     561            // supposed to fall through
     562
     563        case 2:
     564            dprintf("dosfs: mounted with op_sync enabled\n");
     565            fs_flags |= FS_FLAGS_OP_SYNC;
     566            break;
     567
     568        case 0:
     569        default:
     570            break;
     571    }
     572
     573    // read in the boot sector
     574    if ((err = read_pos(fd, 0, (void *)buf, 512)) != 512) {
     575        dprintf("dosfs error: error reading boot sector\n");
     576        goto error1;
     577    }
     578   
     579    vol = volume_init(fd, buf, vol_flags, fs_flags, &geo);
     580
     581    /* check that the partition is large enough to contain the file system */
     582    if (vol->total_sectors > geo.sectors_per_track * geo.cylinder_count
     583            * geo.head_count) {
     584        dprintf("dosfs: volume extends past end of partition\n");
     585        err = B_PARTITION_TOO_SMALL;
     586        goto error2;
     587    }
     588
     589    vol->volume = _vol;
     590    vol->id = _vol->id;
     591    strncpy(vol->device, path, sizeof(vol->device));
     592
     593    {
     594        void *handle;
     595        handle = load_driver_settings("fat");
     596        vol->respect_disk_image =
     597            get_driver_boolean_parameter(handle, "respect", true, true);
     598        unload_driver_settings(handle);
     599    }
     600
     601    // Initialize the vnode cache
     602    if (init_vcache(vol) != B_OK) {
     603        dprintf("dosfs error: error initializing vnode cache\n");
     604        goto error2;
     605    }
     606
     607    // and the dlist cache
     608    if (dlist_init(vol) != B_OK) {
     609        dprintf("dosfs error: error initializing dlist cache\n");
     610        goto error3;
     611    }
     612
     613    volume_count_free_cluster(vol);
     614
    548615    DPRINTF(0, ("built at %s on %s\n", build_time, build_date));
    549     DPRINTF(0, ("mounting %s (id %lx, device %x, media descriptor %x)\n", vol->device, vol->id, vol->fd, vol->media_descriptor));
    550     DPRINTF(0, ("%lx bytes/sector, %lx sectors/cluster\n", vol->bytes_per_sector, vol->sectors_per_cluster));
    551     DPRINTF(0, ("%lx reserved sectors, %lx total sectors\n", vol->reserved_sectors, vol->total_sectors));
    552     DPRINTF(0, ("%lx %d-bit fats, %lx sectors/fat, %lx root entries\n", vol->fat_count, vol->fat_bits, vol->sectors_per_fat, vol->root_entries_count));
    553     DPRINTF(0, ("root directory starts at sector %lx (cluster %lx), data at sector %lx\n", vol->root_start, vol->root_vnode.cluster, vol->data_start));
    554     DPRINTF(0, ("%lx total clusters, %lx free\n", vol->total_clusters, vol->free_clusters));
    555     DPRINTF(0, ("fat mirroring is %s, fs info sector at sector %x\n", (vol->fat_mirrored) ? "on" : "off", vol->fsinfo_sector));
     616    DPRINTF(0, ("mounting %s (id %lx, device %x, media descriptor %x)\n",
     617                vol->device, vol->id, vol->fd, vol->media_descriptor));
     618    DPRINTF(0, ("%lx bytes/sector, %lx sectors/cluster\n",
     619                vol->bytes_per_sector, vol->sectors_per_cluster));
     620    DPRINTF(0, ("%lx reserved sectors, %lx total sectors\n",
     621                vol->reserved_sectors, vol->total_sectors));
     622    DPRINTF(0, ("%lx %d-bit fats, %lx sectors/fat, %lx root entries\n",
     623                vol->fat_count, vol->fat_bits, vol->sectors_per_fat,
     624                vol->root_entries_count));
     625    DPRINTF(0, ("root directory starts at sector %lx (cluster %lx), data at sector %lx\n",
     626                vol->root_start, vol->root_vnode.cluster, vol->data_start));
     627    DPRINTF(0, ("%lx total clusters, %lx free\n",
     628                vol->total_clusters, vol->free_clusters));
     629    DPRINTF(0, ("fat mirroring is %s, fs info sector at sector %x\n",
     630                (vol->fat_mirrored) ? "on" : "off", vol->fsinfo_sector));
    556631    DPRINTF(0, ("last allocated cluster = %lx\n", vol->last_allocated));
    557632
    558633    if (vol->fat_bits == 32) {
     
    575650    vol->root_vnode.dirty = false;
    576651    dlist_add(vol, vol->root_vnode.vnid);
    577652
    578     // find volume label (supercedes any label in the bpb)
    579     {
    580         struct diri diri;
    581         uint8 *buffer;
    582         buffer = diri_init(vol, vol->root_vnode.cluster, 0, &diri);
    583         for (; buffer; buffer = diri_next_entry(&diri)) {
    584             if ((buffer[0x0b] & FAT_VOLUME) && (buffer[0x0b] != 0xf)
    585                     && (buffer[0] != 0xe5)) {
    586                 vol->vol_entry = diri.current_index;
    587                 memcpy(vol->vol_label, buffer, 11);
    588                 dosfs_trim_spaces(vol->vol_label);
    589                 break;
    590             }
    591         }
    592653
    593         diri_free(&diri);
    594     }
    595 
    596654    DPRINTF(0, ("root vnode id = %Lx\n", vol->root_vnode.vnid));
    597655    DPRINTF(0, ("volume label [%s] (%lx)\n", vol->vol_label, vol->vol_entry));
    598656
     
    604662    return B_NO_ERROR;
    605663
    606664error3:
    607     dlist_uninit(vol);
     665    uninit_vcache(vol);
    608666error2:
    609     uninit_vcache(vol);
     667    volume_uninit(vol);
    610668error1:
    611     block_cache_delete(vol->fBlockCache, false);
    612 error:
    613669    if (!(vol->flags & B_FS_IS_READONLY) && (vol->flags & B_FS_IS_REMOVABLE)
    614670        && (vol->fs_flags & FS_FLAGS_LOCK_DOOR)) {
    615         lock_removable_device(vol->fd, false);
     671        lock_removable_device(fd, false);
    616672    }
     673    close(fd);
    617674error0:
    618     close(vol->fd);
    619     free(vol);
    620675    return err >= B_NO_ERROR ? EINVAL : err;
    621676}
    622677
     
    693748        dosfs_read_label(false, buf, name);
    694749    }
    695750
     751    // find volume label (supercedes any label in the bpb)
     752    {
     753        nspace *vol;
     754        vol = volume_init(fd, buf, 0, 0, NULL);
     755        if (vol != NULL)
     756        {
     757            strlcpy(name, vol->vol_label, 12);
     758            volume_uninit(vol);
     759        }
     760    }
     761
    696762    cookie = (identify_cookie *)malloc(sizeof(identify_cookie));
    697763    if (!cookie)
    698764        return -1;