copied dosfs R5 version to tests

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18582 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Jérôme Duval 2006-08-23 09:12:05 +00:00
parent 8d99ad5edc
commit 1d130feff7
31 changed files with 8428 additions and 0 deletions

View File

@ -0,0 +1,16 @@
SubDir HAIKU_TOP src add-ons kernel file_systems dos ;
KernelAddon dos : kernel file_systems :
attr.c
dir.c
dlist.c
dosfs.c
encodings.cpp
fat.c
file.c
iter.c
mime_table.c
util.c
vcache.c
version.c
;

View File

@ -0,0 +1,31 @@
----------------------
Be Sample Code License
----------------------
Copyright 1991-2001, Be Incorporated.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,265 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
/* attr.c
* handles mime type information for dosfs
* gets/sets mime information in vnode
*/
#define MIME_STRING_TYPE 'MIMS'
#include <SupportDefs.h>
#include <KernelExport.h>
#include <dirent.h>
#include <fs_attr.h>
#include <string.h>
#include <malloc.h>
#include <fsproto.h>
#include <lock.h>
#include "dosfs.h"
#include "attr.h"
#include "mime_table.h"
#define DPRINTF(a,b) if (debug_attr > (a)) dprintf b
status_t set_mime_type(vnode *node, const char *filename)
{
struct ext_mime *p;
int32 namelen, ext_len;
DPRINTF(0, ("get_mime_type of (%s)\n", filename));
node->mime = NULL;
namelen = strlen(filename);
for (p=mimes;p->extension;p++) {
ext_len = strlen(p->extension);
if (namelen <= ext_len)
continue;
if (filename[namelen-ext_len-1] != '.')
continue;
if (!strcasecmp(filename + namelen - ext_len, p->extension))
break;
}
node->mime = p->mime;
return B_OK;
}
int dosfs_open_attrdir(void *_vol, void *_node, void **_cookie)
{
nspace *vol = (nspace *)_vol;
TOUCH(_node);
DPRINTF(0, ("dosfs_open_attrdir called\n"));
LOCK_VOL(vol);
if (check_nspace_magic(vol, "dosfs_open_attrdir")) {
UNLOCK_VOL(vol);
return EINVAL;
}
if ((*_cookie = malloc(sizeof(uint32))) == NULL) {
UNLOCK_VOL(vol);
return ENOMEM;
}
*(int32 *)(*_cookie) = 0;
UNLOCK_VOL(vol);
return 0;
}
int dosfs_close_attrdir(void *_vol, void *_node, void *_cookie)
{
nspace *vol = (nspace *)_vol;
TOUCH(_node);
DPRINTF(0, ("dosfs_close_attrdir called\n"));
LOCK_VOL(vol);
if (check_nspace_magic(vol, "dosfs_open_attrdir")) {
UNLOCK_VOL(vol);
return EINVAL;
}
*(int32 *)_cookie = 1;
UNLOCK_VOL(vol);
return 0;
}
int dosfs_free_attrcookie(void *_vol, void *_node, void *_cookie)
{
TOUCH(_vol); TOUCH(_node);
DPRINTF(0, ("dosfs_free_attrcookie called\n"));
if (_cookie == NULL) {
dprintf("error: dosfs_free_attrcookie called with null cookie\n");
return EINVAL;
}
*(int32 *)_cookie = 0x87654321;
free(_cookie);
return 0;
}
int dosfs_rewind_attrdir(void *_vol, void *_node, void *_cookie)
{
TOUCH(_vol); TOUCH(_node);
DPRINTF(0, ("dosfs_rewind_attrdir called\n"));
if (_cookie == NULL) {
dprintf("error: dosfs_rewind_attrcookie called with null cookie\n");
return EINVAL;
}
*(uint32 *)_cookie = 0;
return 0;
}
int dosfs_read_attrdir(void *_vol, void *_node, void *_cookie, long *num,
struct dirent *entry, size_t bufsize)
{
nspace *vol = (nspace *)_vol;
vnode *node = (vnode *)_node;
int32 *cookie = (int32 *)_cookie;
TOUCH(bufsize);
DPRINTF(0, ("dosfs_read_attrdir called\n"));
*num = 0;
LOCK_VOL(vol);
if (check_nspace_magic(vol, "dosfs_read_attrdir") ||
check_vnode_magic(node, "dosfs_read_attrdir")) {
UNLOCK_VOL(vol);
return EINVAL;
}
if ((*cookie == 0) && (node->mime)) {
*num = 1;
entry->d_ino = node->vnid;
entry->d_dev = vol->id;
entry->d_reclen = 10;
strcpy(entry->d_name, "BEOS:TYPE");
}
*cookie = 1;
UNLOCK_VOL(vol);
return 0;
}
int dosfs_stat_attr(void *_vol, void *_node, const char *name, struct attr_info *buf)
{
nspace *vol = (nspace *)_vol;
vnode *node = (vnode *)_node;
DPRINTF(0, ("dosfs_stat_attr (%s)\n", name));
if (strcmp(name, "BEOS:TYPE"))
return ENOENT;
LOCK_VOL(vol);
if (check_nspace_magic(vol, "dosfs_read_attr") ||
check_vnode_magic(node, "dosfs_read_attr")) {
UNLOCK_VOL(vol);
return EINVAL;
}
if (node->mime == NULL) {
UNLOCK_VOL(vol);
return ENOENT;
}
buf->type = MIME_STRING_TYPE;
buf->size = strlen(node->mime) + 1;
UNLOCK_VOL(vol);
return 0;
}
int dosfs_read_attr(void *_vol, void *_node, const char *name, int type, void *buf,
size_t *len, off_t pos)
{
nspace *vol = (nspace *)_vol;
vnode *node = (vnode *)_node;
DPRINTF(0, ("dosfs_read_attr (%s)\n", name));
if (strcmp(name, "BEOS:TYPE"))
return ENOENT;
if (type != MIME_STRING_TYPE)
return ENOENT;
LOCK_VOL(vol);
if (check_nspace_magic(vol, "dosfs_read_attr") ||
check_vnode_magic(node, "dosfs_read_attr")) {
UNLOCK_VOL(vol);
return EINVAL;
}
if (node->mime == NULL) {
UNLOCK_VOL(vol);
return ENOENT;
}
if ((pos < 0) || (pos > strlen(node->mime))) {
UNLOCK_VOL(vol);
return EINVAL;
}
strncpy(buf, node->mime + pos, *len - 1);
((char *)buf)[*len - 1] = 0;
*len = strlen(buf) + 1;
UNLOCK_VOL(vol);
return 0;
}
// suck up application attempts to set mime types; this hides an unsightly
// error message printed out by zip
int dosfs_write_attr(void *_vol, void *_node, const char *name, int type,
const void *buf, size_t *len, off_t pos)
{
TOUCH(_vol); TOUCH(_node); TOUCH(name); TOUCH(type); TOUCH(buf);
TOUCH(len); TOUCH(pos);
DPRINTF(0, ("dosfs_write_attr (%s)\n", name));
*len = 0;
if (strcmp(name, "BEOS:TYPE"))
return ENOSYS;
if (type != MIME_STRING_TYPE)
return ENOSYS;
return 0;
}

View File

@ -0,0 +1,22 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_ATTR_H_
#define _DOSFS_ATTR_H_
status_t set_mime_type(vnode *node, const char *filename);
int dosfs_open_attrdir(void *_vol, void *_node, void **_cookie);
int dosfs_close_attrdir(void *_vol, void *_node, void *_cookie);
int dosfs_free_attrcookie(void *_vol, void *_node, void *_cookie);
int dosfs_rewind_attrdir(void *_vol, void *_node, void *_cookie);
int dosfs_read_attrdir(void *_vol, void *_node, void *_cookie, long *num,
struct dirent *buf, size_t bufsize);
int dosfs_stat_attr(void *_vol, void *_node, const char *name, struct attr_info *buf);
int dosfs_read_attr(void *_vol, void *_node, const char *name, int type, void *buf,
size_t *len, off_t pos);
int dosfs_write_attr(void *_vol, void *_node, const char *name, int type,
const void *buf, size_t *len, off_t pos);
#endif

View File

@ -0,0 +1,108 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _CACHE_H_
#define _CACHE_H_
#include <BeBuild.h>
typedef struct hash_ent {
int dev;
off_t bnum;
off_t hash_val;
void *data;
struct hash_ent *next;
} hash_ent;
typedef struct hash_table {
hash_ent **table;
int max;
int mask; /* == max - 1 */
int num_elements;
} hash_table;
#define HT_DEFAULT_MAX 128
typedef struct cache_ent {
int dev;
off_t block_num;
int bsize;
volatile int flags;
void *data;
void *clone; /* copy of data by set_block_info() */
int lock;
void (*func)(off_t bnum, size_t num_blocks, void *arg);
off_t logged_bnum;
void *arg;
struct cache_ent *next, /* points toward mru end of list */
*prev; /* points toward lru end of list */
} cache_ent;
#define CE_NORMAL 0x0000 /* a nice clean pristine page */
#define CE_DIRTY 0x0002 /* needs to be written to disk */
#define CE_BUSY 0x0004 /* this block has i/o happening, don't touch it */
typedef struct cache_ent_list {
cache_ent *lru; /* tail of the list */
cache_ent *mru; /* head of the list */
} cache_ent_list;
typedef struct block_cache {
lock lock;
int flags;
int cur_blocks;
int max_blocks;
hash_table ht;
cache_ent_list normal, /* list of "normal" blocks (clean & dirty) */
locked; /* list of clean and locked blocks */
} block_cache;
#if 0 /* XXXdbg -- need to deal with write through caches */
#define DC_WRITE_THROUGH 0x0001 /* cache is write-through (for floppies) */
#endif
#define ALLOW_WRITES 1
#define NO_WRITES 0
extern _IMPEXP_KERNEL int init_block_cache(int max_blocks, int flags);
extern _IMPEXP_KERNEL void shutdown_block_cache(void);
extern _IMPEXP_KERNEL void force_cache_flush(int dev, int prefer_log_blocks);
extern _IMPEXP_KERNEL int flush_blocks(int dev, off_t bnum, int nblocks);
extern _IMPEXP_KERNEL int flush_device(int dev, int warn_locked);
extern _IMPEXP_KERNEL int init_cache_for_device(int fd, off_t max_blocks);
extern _IMPEXP_KERNEL int remove_cached_device_blocks(int dev, int allow_write);
extern _IMPEXP_KERNEL void *get_block(int dev, off_t bnum, int bsize);
extern _IMPEXP_KERNEL void *get_empty_block(int dev, off_t bnum, int bsize);
extern _IMPEXP_KERNEL int release_block(int dev, off_t bnum);
extern _IMPEXP_KERNEL int mark_blocks_dirty(int dev, off_t bnum, int nblocks);
extern _IMPEXP_KERNEL int cached_read(int dev, off_t bnum, void *data, off_t num_blocks, int bsize);
extern _IMPEXP_KERNEL int cached_write(int dev, off_t bnum, const void *data,
off_t num_blocks, int bsize);
extern _IMPEXP_KERNEL int cached_write_locked(int dev, off_t bnum, const void *data,
off_t num_blocks, int bsize);
extern _IMPEXP_KERNEL int set_blocks_info(int dev, off_t *blocks, int nblocks,
void (*func)(off_t bnum, size_t nblocks, void *arg),
void *arg);
extern _IMPEXP_KERNEL size_t read_phys_blocks (int fd, off_t bnum, void *data, uint num_blocks, int bsize);
extern _IMPEXP_KERNEL size_t write_phys_blocks(int fd, off_t bnum, void *data, uint num_blocks, int bsize);
#endif /* _CACHE_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_DIR_H_
#define _DOSFS_DIR_H_
bool is_filename_legal(const char *name);
status_t check_dir_empty(nspace *vol, vnode *dir);
status_t findfile_case(nspace *vol, vnode *dir, const char *file,
vnode_id *vnid, vnode **node);
status_t findfile_nocase(nspace *vol, vnode *dir, const char *file,
vnode_id *vnid, vnode **node);
status_t findfile_nocase_duplicates(nspace *vol, vnode *dir, const char *file,
vnode_id *vnid, vnode **node, bool *dups_exist);
status_t findfile_case_duplicates(nspace *vol, vnode *dir, const char *file,
vnode_id *vnid, vnode **node, bool *dups_exist);
status_t erase_dir_entry(nspace *vol, vnode *node);
status_t compact_directory(nspace *vol, vnode *dir);
status_t create_volume_label(nspace *vol, const char name[11], uint32 *index);
status_t create_dir_entry(nspace *vol, vnode *dir, vnode *node,
const char *name, uint32 *ns, uint32 *ne);
int dosfs_read_vnode(void *_vol, vnode_id vnid, char r, void **node);
int dosfs_walk(void *_vol, void *_dir, const char *file,
char **newpath, vnode_id *vnid);
int dosfs_access(void *_vol, void *_node, int mode);
int dosfs_readlink(void *_vol, void *_node, char *buf, size_t *bufsize);
int dosfs_opendir(void *_vol, void *_node, void **cookie);
int dosfs_readdir(void *_vol, void *_node, void *cookie,
long *num, struct dirent *buf, size_t bufsize);
int dosfs_rewinddir(void *_vol, void *_node, void *cookie);
int dosfs_closedir(void *_vol, void *_node, void *cookie);
int dosfs_free_dircookie(void *_vol, void *_node, void *cookie);
#endif

View File

@ -0,0 +1,162 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
/*
directory vnode id list
We only add to this list as we encounter directories; there is no need to
scan through the directories ourselves since we aren't worried about preserving
vnid's across reboots.
We don't worry about aliases for directories since their cluster values will
always be the same -- searches are performed only on the starting cluster
number of the directories.
TODO:
XXX: make this more efficient
*/
#define DPRINTF(a,b) if (debug_dlist > (a)) dprintf b
#include <KernelExport.h>
#include <fsproto.h>
#include <string.h>
#include "dosfs.h"
#include "dlist.h"
#include "util.h"
#include "vcache.h"
#if DEBUG
#define DLIST_ENTRY_QUANTUM 1
#else
#define DLIST_ENTRY_QUANTUM 0x20
#endif
status_t dlist_init(nspace *vol)
{
DPRINTF(0, ("dlist_init called\n"));
vol->dlist.entries = 0;
vol->dlist.allocated = DLIST_ENTRY_QUANTUM;
vol->dlist.vnid_list = malloc(sizeof(vnode_id) * vol->dlist.allocated);
if (vol->dlist.vnid_list == NULL) {
vol->dlist.allocated = 0;
dprintf("dlist_init: out of core\n");
return ENOMEM;
}
return B_OK;
}
status_t dlist_uninit(nspace *vol)
{
DPRINTF(0, ("dlist_uninit called\n"));
if (vol->dlist.vnid_list)
free(vol->dlist.vnid_list);
vol->dlist.entries = vol->dlist.allocated = 0;
vol->dlist.vnid_list = NULL;
return B_OK;
}
static status_t dlist_realloc(nspace *vol, uint32 allocate)
{
vnode_id *vnid_list;
DPRINTF(0, ("dlist_realloc %lx -> %lx\n", vol->dlist.allocated, allocate));
ASSERT(allocate != vol->dlist.allocated);
ASSERT(allocate > vol->dlist.entries);
vnid_list = malloc(sizeof(vnode_id) * allocate);
if (vnid_list == NULL) {
dprintf("dlist_realloc: out of core\n");
return ENOMEM;
}
memcpy(vnid_list, vol->dlist.vnid_list, sizeof(vnode_id) * vol->dlist.entries);
free(vol->dlist.vnid_list);
vol->dlist.vnid_list = vnid_list;
vol->dlist.allocated = allocate;
return B_OK;
}
status_t dlist_add(nspace *vol, vnode_id vnid)
{
DPRINTF(0, ("dlist_add vnid %Lx\n", vnid));
ASSERT(IS_DIR_CLUSTER_VNID(vnid) || IS_ARTIFICIAL_VNID(vnid));
ASSERT(vnid != 0);
// XXX: check for duplicate entries
if (vol->dlist.entries == vol->dlist.allocated) {
if (dlist_realloc(vol, vol->dlist.allocated + DLIST_ENTRY_QUANTUM) < 0)
return B_ERROR;
}
vol->dlist.vnid_list[vol->dlist.entries++] = vnid;
return B_OK;
}
status_t dlist_remove(nspace *vol, vnode_id vnid)
{
uint32 i;
DPRINTF(0, ("dlist_remove vnid %Lx\n", vnid));
for (i=0;i<vol->dlist.entries;i++)
if (vol->dlist.vnid_list[i] == vnid)
break;
ASSERT(i < vol->dlist.entries);
if (i == vol->dlist.entries)
return ENOENT;
for (;i<vol->dlist.entries-1;i++)
vol->dlist.vnid_list[i] = vol->dlist.vnid_list[i+1];
vol->dlist.entries--;
if (vol->dlist.allocated - vol->dlist.entries > 2*DLIST_ENTRY_QUANTUM)
return dlist_realloc(vol, vol->dlist.allocated - DLIST_ENTRY_QUANTUM);
return B_OK;
}
vnode_id dlist_find(nspace *vol, uint32 cluster)
{
uint32 i;
DPRINTF(1, ("dlist_find cluster %lx\n", cluster));
ASSERT(((cluster >= 2) && (cluster < vol->total_clusters + 2)) || (cluster == 1));
for (i=0;i<vol->dlist.entries;i++) {
vnode_id loc;
if (vcache_vnid_to_loc(vol, vol->dlist.vnid_list[i], &loc) < B_OK)
loc = vol->dlist.vnid_list[i];
ASSERT(IS_DIR_CLUSTER_VNID(loc));
if (CLUSTER_OF_DIR_CLUSTER_VNID(loc) == cluster)
return vol->dlist.vnid_list[i];
}
DPRINTF(1, ("dlist_find cluster %lx not found\n", cluster));
return -1LL;
}
void dlist_dump(nspace *vol)
{
uint32 i;
dprintf("%lx/%lx dlist entries filled, QUANTUM = %x\n",
vol->dlist.entries, vol->dlist.allocated, DLIST_ENTRY_QUANTUM);
for (i=0;i<vol->dlist.entries;i++)
dprintf("%s %Lx", ((i == 0) ? "entries:" : ","), vol->dlist.vnid_list[i]);
dprintf("\n");
}

View File

@ -0,0 +1,16 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_DLIST_H_
#define _DOSFS_DLIST_H_
status_t dlist_init(nspace *vol);
status_t dlist_uninit(nspace *vol);
status_t dlist_add(nspace *vol, vnode_id vnid);
status_t dlist_remove(nspace *vol, vnode_id vnid);
vnode_id dlist_find(nspace *vol, uint32 cluster);
void dlist_dump(nspace *vol);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,211 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_H_
#define _DOSFS_H_
#if USE_DMALLOC
#include <dmalloc.h>
#else
/* allocate memory from swappable heap */
#define malloc smalloc
#define free sfree
#define calloc scalloc
#define realloc srealloc
#include <kalloc.h>
#endif
#include <KernelExport.h>
/* for multiple reader/single writer locks */
#define READERS 100000
/* Unfortunately, vnode_id's are defined as signed. This causes problems with
* programs (notably cp) that use the modulo of a vnode_id (or ino_t) as a
* hash function to index an array. This means the high bit of every vnode_id
* is off-limits. Luckily, FAT32 is actually FAT28, so dosfs can make do with
* only 63 bits.
*/
#define ARTIFICIAL_VNID_BITS (0x6LL << 60)
#define DIR_CLUSTER_VNID_BITS (0x4LL << 60)
#define DIR_INDEX_VNID_BITS 0
#define INVALID_VNID_BITS_MASK (0x9LL << 60)
#define IS_DIR_CLUSTER_VNID(vnid) \
(((vnid) & ARTIFICIAL_VNID_BITS) == DIR_CLUSTER_VNID_BITS)
#define IS_DIR_INDEX_VNID(vnid) \
(((vnid) & ARTIFICIAL_VNID_BITS) == DIR_INDEX_VNID_BITS)
#define IS_ARTIFICIAL_VNID(vnid) \
(((vnid) & ARTIFICIAL_VNID_BITS) == ARTIFICIAL_VNID_BITS)
#define IS_INVALID_VNID(vnid) \
((!IS_DIR_CLUSTER_VNID((vnid)) && \
!IS_DIR_INDEX_VNID((vnid)) && \
!IS_ARTIFICIAL_VNID((vnid))) || \
((vnid) & INVALID_VNID_BITS_MASK))
#define GENERATE_DIR_INDEX_VNID(dircluster, index) \
(DIR_INDEX_VNID_BITS | ((vnode_id)(dircluster) << 32) | (index))
#define GENERATE_DIR_CLUSTER_VNID(dircluster, filecluster) \
(DIR_CLUSTER_VNID_BITS | ((vnode_id)(dircluster) << 32) | (filecluster))
#define CLUSTER_OF_DIR_CLUSTER_VNID(vnid) \
((uint32)((vnid) & 0xffffffff))
#define INDEX_OF_DIR_INDEX_VNID(vnid) \
((uint32)((vnid) & 0xffffffff))
#define DIR_OF_VNID(vnid) \
((uint32)(((vnid) >> 32) & ~0xf0000000))
#define VNODE_PARENT_DIR_CLUSTER(vnode) \
CLUSTER_OF_DIR_CLUSTER_VNID((vnode)->dir_vnid)
#include <lock.h>
#define VNODE_MAGIC 'treB'
typedef struct vnode
{
uint32 magic;
vnode_id vnid; // self id
vnode_id dir_vnid; // parent vnode id (directory containing entry)
uint32 disk_image; // 0 = no, 1 = BEOS, 2 = IMAGE.BE
/* iteration is incremented each time the fat chain changes. it's used by
* the file read/write code to determine if it needs to retraverse the
* fat chain
*/
uint32 iteration;
/* any changes to this block of information should immediately be reflected
* on the disk (or at least in the cache) so that get_next_dirent continues
* to function properly
*/
uint32 sindex, eindex; // starting and ending index of directory entry
uint32 cluster; // starting cluster of the data
uint32 mode; // dos-style attributes
off_t st_size; // in bytes
time_t st_time;
uint32 end_cluster; // last cluster of the data
const char *mime; // mime type (null if none)
bool dirty; // track if vnode had been written to
#if TRACK_FILENAME
char *filename;
#endif
} vnode;
// mode bits
#define FAT_READ_ONLY 1
#define FAT_HIDDEN 2
#define FAT_SYSTEM 4
#define FAT_VOLUME 8
#define FAT_SUBDIR 16
#define FAT_ARCHIVE 32
#define NSPACE_MAGIC 'smaI'
struct vcache_entry;
typedef struct _nspace
{
uint32 magic;
nspace_id id; // ID passed in to fs_mount
int fd; // File descriptor
char device[256];
uint32 flags; // see <fcntl.be.h> for modes
// info from bpb
uint32 bytes_per_sector;
uint32 sectors_per_cluster;
uint32 reserved_sectors;
uint32 fat_count;
uint32 root_entries_count;
uint32 total_sectors;
uint32 sectors_per_fat;
uint8 media_descriptor;
uint16 fsinfo_sector;
uint32 total_clusters; // data clusters, that is
uint32 free_clusters;
uint8 fat_bits;
bool fat_mirrored; // true if fat mirroring on
uint8 active_fat;
uint32 root_start; // for fat12 + fat16 only
uint32 root_sectors; // for fat12 + fat16 only
vnode root_vnode; // root directory
int32 vol_entry; // index in root directory
char vol_label[12]; // lfn's need not apply
uint32 data_start;
uint32 last_allocated; // last allocated cluster
vnode_id beos_vnid; // vnid of \BEOS directory
bool respect_disk_image;
int fs_flags; // flags for this mount
lock vlock; // volume lock
// vcache state
struct {
sem_id vc_sem;
vnode_id cur_vnid;
uint32 cache_size;
struct vcache_entry **by_vnid, **by_loc;
} vcache;
struct {
uint32 entries;
uint32 allocated;
vnode_id *vnid_list;
} dlist;
} nspace;
#define FS_FLAGS_OP_SYNC 0x1
#define FS_FLAGS_LOCK_DOOR 0x2
#define LOCK_VOL(vol) \
if (vol == NULL) { dprintf("null vol\n"); return EINVAL; } else LOCK((vol)->vlock)
#define UNLOCK_VOL(vol) \
UNLOCK((vol)->vlock)
#define CHECK_MAGIC(name,struc,magick) \
int check_##name##_magic(struc *t, char *funcname) \
{ \
if (t == NULL) { \
dprintf("%s passed null " #name " pointer\n", funcname); \
return EINVAL; \
} else if (t->magic != magick) { \
dprintf(#name " (%x) passed to %s has invalid magic number\n", (int)t, funcname); \
return EINVAL; \
} else \
return 0; \
}
int check_vnode_magic(struct vnode *t, char *funcname);
int check_nspace_magic(struct _nspace *t, char *funcname);
#define TOUCH(x) ((void)(x))
/* debug levels */
extern int debug_attr, debug_dir, debug_dlist, debug_dosfs, debug_encodings,
debug_fat, debug_file, debug_iter, debug_vcache;
int _dosfs_sync(nspace *vol);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_ENCODINGS_H_
#define _DOSFS_ENCODINGS_H_
#ifdef __cplusplus
extern "C" {
#endif
status_t unicode_to_utf8(const uchar *uni, uint32 unilen, uint8 *utf8,
uint32 utf8len);
bool requires_munged_short_name(const uchar *utf8name,
const uchar nshort[11], int encoding);
bool requires_long_name(const char *utf8, const uchar *unicode);
status_t utf8_to_unicode(const char *utf8, uchar *uni, uint32 unilen);
status_t munge_short_name2(uchar nshort[11], int encoding);
status_t munge_short_name1(uchar nshort[11], int iteration, int encoding);
status_t generate_short_name(const uchar *name, const uchar *uni,
uint32 unilen, uchar nshort[11], int *encoding);
status_t msdos_to_utf8(uchar *msdos, uchar *utf8, uint32 utf8len, bool toLower);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,644 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include <KernelExport.h>
#include <fsproto.h>
#include <lock.h>
#include <cache.h>
#include <stdlib.h>
#include <string.h>
#include <ByteOrder.h>
#include "dosfs.h"
#include "fat.h"
#include "util.h"
#include "file.h"
#include "vcache.h"
#define END_FAT_ENTRY 0x0fffffff
#define BAD_FAT_ENTRY 0x0ffffff1
#define DPRINTF(a,b) if (debug_fat > (a)) dprintf b
static status_t mirror_fats(nspace *vol, uint32 sector, uint8 *buffer)
{
uint32 i;
if (!vol->fat_mirrored)
return B_OK;
sector -= vol->active_fat * vol->sectors_per_fat;
for (i=0;i<vol->fat_count;i++) {
if (i == vol->active_fat)
continue;
cached_write(vol->fd, sector + i*vol->sectors_per_fat, buffer, 1, vol->bytes_per_sector);
}
return B_OK;
}
static int32 _count_free_clusters_fat32(nspace *vol)
{
int32 count = 0;
uint8 *block;
uint32 fat_sector;
uint32 i;
uint32 cur_sector;
cur_sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat;
for(fat_sector = 0; fat_sector < vol->sectors_per_fat; fat_sector++) {
block = (uint8 *)get_block(vol->fd, cur_sector, vol->bytes_per_sector);
if(block == NULL) {
return EIO;
}
for(i=0; i < vol->bytes_per_sector; i += sizeof(uint32)) {
uint32 val = read32(block, i);
if(val == 0) count++;
}
release_block(vol->fd, cur_sector);
cur_sector++;
}
return count;
}
// count free: no parameters. returns int32
// get_entry: cluster #. returns int32 entry/status
// set_entry: cluster #, value. returns int32 status
// allocate: # clusters in N, returns int32 status/starting cluster
enum { _IOCTL_COUNT_FREE_, _IOCTL_GET_ENTRY_, _IOCTL_SET_ENTRY_, _IOCTL_ALLOCATE_N_ENTRIES_ };
static int32 _fat_ioctl_(nspace *vol, uint32 action, uint32 cluster, int32 N)
{
int32 result = 0;
uint32 n = 0, first = 0, last = 0;
uint32 i;
uint32 sector;
uint32 off, val = 0; /* quiet warning */
uint8 *block1, *block2 = NULL; /* quiet warning */
// mark end of chain for allocations
uint32 V = (action == _IOCTL_SET_ENTRY_) ? N : 0x0fffffff;
ASSERT((action >= _IOCTL_COUNT_FREE_) && (action <= _IOCTL_ALLOCATE_N_ENTRIES_));
if (check_nspace_magic(vol, "_fat_ioctl_")) return EINVAL;
DPRINTF(3, ("_fat_ioctl_: action %lx, cluster %lx, N %lx\n", action, cluster, N));
if (action == _IOCTL_COUNT_FREE_) {
if(vol->fat_bits == 32)
// use a optimized version of the cluster counting algorithms
return _count_free_clusters_fat32(vol);
else
cluster = 2;
}
if (action == _IOCTL_ALLOCATE_N_ENTRIES_)
cluster = vol->last_allocated;
if (action != _IOCTL_COUNT_FREE_) {
if (!IS_DATA_CLUSTER(cluster)) {
DPRINTF(0, ("_fat_ioctl_ called with invalid cluster (%lx)\n", cluster));
return EINVAL;
}
}
off = cluster * vol->fat_bits / 8;
sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat +
off / vol->bytes_per_sector;
off %= vol->bytes_per_sector;
if ((block1 = (uint8 *)get_block(vol->fd, sector, vol->bytes_per_sector)) == NULL) {
DPRINTF(0, ("_fat_ioctl_: error reading fat (sector %lx)\n", sector));
return EIO;
}
for (i=0;i<vol->total_clusters;i++) {
ASSERT(IS_DATA_CLUSTER(cluster));
ASSERT(off == ((cluster * vol->fat_bits / 8) % vol->bytes_per_sector));
if (vol->fat_bits == 12) {
if (off == vol->bytes_per_sector - 1) {
if ((block2 = (uint8 *)get_block(vol->fd, ++sector, vol->bytes_per_sector)) == NULL) {
DPRINTF(0, ("_fat_ioctl_: error reading fat (sector %lx)\n", sector));
result = EIO;
sector--;
goto bi;
}
}
if (action != _IOCTL_SET_ENTRY_) {
if (off == vol->bytes_per_sector - 1) {
val = block1[off] + 0x100*block2[0];
} else
val = block1[off] + 0x100*block1[off+1];
if (cluster & 1) {
val >>= 4;
} else {
val &= 0xfff;
}
if (val > 0xff0) val |= 0x0ffff000;
}
if (((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (val == 0)) ||
(action == _IOCTL_SET_ENTRY_)) {
uint32 andmask, ormask;
if (cluster & 1) {
ormask = (V & 0xfff) << 4;
andmask = 0xf;
} else {
ormask = V & 0xfff;
andmask = 0xf000;
}
block1[off] &= (andmask & 0xff);
block1[off] |= (ormask & 0xff);
if (off == vol->bytes_per_sector - 1) {
mark_blocks_dirty(vol->fd, sector - 1, 1);
mirror_fats(vol, sector - 1, block1);
block2[0] &= (andmask >> 8);
block2[0] |= (ormask >> 8);
} else {
block1[off+1] &= (andmask >> 8);
block1[off+1] |= (ormask >> 8);
}
}
if (off == vol->bytes_per_sector - 1) {
off = (cluster & 1) ? 1 : 0;
release_block(vol->fd, sector - 1);
block1 = block2;
} else {
off += (cluster & 1) ? 2 : 1;
}
} else if (vol->fat_bits == 16) {
if (action != _IOCTL_SET_ENTRY_) {
val = read16(block1, off);
// val = block1[off] + 0x100*block1[off+1];
if (val > 0xfff0) val |= 0x0fff0000;
}
if (((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (val == 0)) ||
(action == _IOCTL_SET_ENTRY_)) {
*(uint16 *)&block1[off] = B_HOST_TO_LENDIAN_INT16(V);
// block1[off] = V & 0xff;
// block1[off+1] = (V >> 8) & 0xff;
}
off += 2;
} else if (vol->fat_bits == 32) {
if (action != _IOCTL_SET_ENTRY_) {
val = read32(block1, off) & 0x0fffffff;
// val = block1[off] + 0x100*block1[off+1] +
// 0x10000*block1[off+2] + 0x1000000*(block1[off+3]&0x0f);
// if (val > 0x0ffffff0) val |= 0x00000000;
}
if (((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (val == 0)) ||
(action == _IOCTL_SET_ENTRY_)) {
ASSERT((V & 0xf0000000) == 0);
*(uint32 *)&block1[off] = B_HOST_TO_LENDIAN_INT32(V);
// block1[off] = V & 0xff;
// block1[off+1] = (V >> 8) & 0xff;
// block1[off+2] = (V >> 16) & 0xff;
// block1[off+3] = (V >> 24) & 0x0f;
// ASSERT(V == (block1[off] + 0x100*block1[off+1] + 0x10000*block1[off+2] + 0x1000000*block1[off+3]));
}
off += 4;
} else
ASSERT(0);
if (action == _IOCTL_COUNT_FREE_) {
if (val == 0)
result++;
} else if (action == _IOCTL_GET_ENTRY_) {
result = val;
goto bi;
} else if (action == _IOCTL_SET_ENTRY_) {
mark_blocks_dirty(vol->fd, sector, 1);
mirror_fats(vol, sector, block1);
goto bi;
} else if ((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (val == 0)) {
vol->free_clusters--;
mark_blocks_dirty(vol->fd, sector, 1);
mirror_fats(vol, sector, block1);
if (n == 0) {
ASSERT(first == 0);
first = last = cluster;
} else {
ASSERT(IS_DATA_CLUSTER(first));
ASSERT(IS_DATA_CLUSTER(last));
// set last cluster to point to us
if ((result = _fat_ioctl_(vol,_IOCTL_SET_ENTRY_,last,cluster)) < 0) {
ASSERT(0);
goto bi;
}
last = cluster;
}
if (++n == N)
goto bi;
}
// iterate cluster and sector if needed
if (++cluster == vol->total_clusters + 2) {
release_block(vol->fd, sector);
cluster = 2;
off = 2 * vol->fat_bits / 8;
sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat;
block1 = (uint8 *)get_block(vol->fd, sector, vol->bytes_per_sector);
}
if (off >= vol->bytes_per_sector) {
release_block(vol->fd, sector);
off -= vol->bytes_per_sector; sector++;
ASSERT(sector < vol->reserved_sectors + (vol->active_fat + 1) * vol->sectors_per_fat);
block1 = (uint8 *)get_block(vol->fd, sector, vol->bytes_per_sector);
}
if (block1 == NULL) {
DPRINTF(0, ("_fat_ioctl_: error reading fat (sector %lx)\n", sector));
result = EIO;
goto bi;
}
}
bi:
if (block1) release_block(vol->fd, sector);
if (action == _IOCTL_ALLOCATE_N_ENTRIES_) {
if (result < 0) {
DPRINTF(0, ("pooh. there is a problem. clearing chain (%lx)\n", first));
if (first != 0) clear_fat_chain(vol, first);
} else if (n != N) {
DPRINTF(0, ("not enough free entries (%lx/%lx found)\n", n, N));
if (first != 0) clear_fat_chain(vol, first);
result = ENOSPC;
} else if (result == 0) {
vol->last_allocated = cluster;
result = first;
ASSERT(IS_DATA_CLUSTER(first));
}
}
if (result < B_OK)
DPRINTF(0, ("_fat_ioctl_ error: action = %lx cluster = %lx N = %lx (%s)\n", action, cluster, N, strerror(result)));
return result;
}
int32 count_free_clusters(nspace *vol)
{
return _fat_ioctl_(vol, _IOCTL_COUNT_FREE_, 0, 0);
}
static int32 get_fat_entry(nspace *vol, uint32 cluster)
{
int32 value = _fat_ioctl_(vol, _IOCTL_GET_ENTRY_, cluster, 0);
if (value < 0)
return value;
if ((value == 0) || IS_DATA_CLUSTER(value))
return value;
if (value > 0x0ffffff7)
return END_FAT_ENTRY;
if (value > 0x0ffffff0)
return BAD_FAT_ENTRY;
DPRINTF(0, ("invalid fat entry: %lx\n", value));
return BAD_FAT_ENTRY;
}
static status_t set_fat_entry(nspace *vol, uint32 cluster, int32 value)
{
return _fat_ioctl_(vol, _IOCTL_SET_ENTRY_, cluster, value);
}
// traverse n fat entries
int32 get_nth_fat_entry(nspace *vol, int32 cluster, uint32 n)
{
if (check_nspace_magic(vol, "get_nth_fat_entry")) return EINVAL;
while (n--) {
cluster = get_fat_entry(vol, cluster);
if (!IS_DATA_CLUSTER(cluster))
break;
}
ASSERT(cluster != 0);
return cluster;
}
// count number of clusters in fat chain starting at given cluster
// should only be used for calculating directory sizes because it doesn't
// return proper error codes
uint32 count_clusters(nspace *vol, int32 cluster)
{
int32 count = 0;
DPRINTF(2, ("count_clusters %lx\n", cluster));
if (check_nspace_magic(vol, "count_clusters")) return 0;
// not intended for use on root directory
if (!IS_DATA_CLUSTER(cluster)) {
DPRINTF(0, ("count_clusters called on invalid cluster (%lx)\n", cluster));
return 0;
}
while (IS_DATA_CLUSTER(cluster)) {
count++;
// break out of circular fat chains in a sketchy manner
if (count == vol->total_clusters)
return 0;
cluster = get_fat_entry(vol, cluster);
}
DPRINTF(2, ("count_clusters %lx = %lx\n", cluster, count));
if (cluster == END_FAT_ENTRY)
return count;
dprintf("cluster = %lx\n", cluster);
ASSERT(0);
return 0;
}
status_t clear_fat_chain(nspace *vol, uint32 cluster)
{
int32 c;
status_t result;
if (!IS_DATA_CLUSTER(cluster)) {
DPRINTF(0, ("clear_fat_chain called on invalid cluster (%lx)\n", cluster));
return EINVAL;
}
ASSERT(count_clusters(vol, cluster) != 0);
DPRINTF(2, ("clearing fat chain: %lx", cluster));
while (IS_DATA_CLUSTER(cluster)) {
if ((c = get_fat_entry(vol, cluster)) < 0) {
DPRINTF(0, ("clear_fat_chain: error clearing fat entry for cluster %lx (%s)\n", cluster, strerror(c)));
return c;
}
if ((result = set_fat_entry(vol, cluster, 0)) != B_OK) {
DPRINTF(0, ("clear_fat_chain: error clearing fat entry for cluster %lx (%s)\n", cluster, strerror(result)));
return result;
}
vol->free_clusters++;
cluster = c;
DPRINTF(2, (", %lx", cluster));
}
DPRINTF(2, ("\n"));
if (cluster != END_FAT_ENTRY)
dprintf("clear_fat_chain: fat chain terminated improperly with %lx\n", cluster);
return 0;
}
status_t allocate_n_fat_entries(nspace *vol, int32 n, int32 *start)
{
int32 c;
ASSERT(n > 0);
DPRINTF(2, ("allocating %lx fat entries\n", n));
c = _fat_ioctl_(vol, _IOCTL_ALLOCATE_N_ENTRIES_, 0, n);
if (c < 0)
return c;
ASSERT(IS_DATA_CLUSTER(c));
ASSERT(count_clusters(vol, c) == n);
DPRINTF(2, ("allocated %lx fat entries at %lx\n", n, c));
*start = c;
return 0;
}
status_t set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters)
{
status_t result;
int32 i, c, n;
DPRINTF(1, ("set_fat_chain_length: %Lx to %lx clusters (%lx)\n", node->vnid, clusters, node->cluster));
if (IS_FIXED_ROOT(node->cluster) || (!IS_DATA_CLUSTER(node->cluster) && (node->cluster != 0))) {
DPRINTF(0, ("set_fat_chain_length called on invalid cluster (%lx)\n", node->cluster));
return EINVAL;
}
if (clusters == 0) {
DPRINTF(1, ("truncating node to zero bytes\n"));
if (node->cluster == 0)
return B_OK;
c = node->cluster;
if ((result = clear_fat_chain(vol, c)) != B_OK)
return result;
node->cluster = 0;
node->end_cluster = 0;
// XXX: don't have to do this this way -- can clean up nicely
do {
result = vcache_set_entry(vol, node->vnid,
GENERATE_DIR_INDEX_VNID(node->dir_vnid, node->sindex));
// repeat until memory is freed up
if (result != B_OK)
snooze(5000LL);
} while (result != B_OK);
/* write to disk so that get_next_dirent doesn't barf */
write_vnode_entry(vol, node);
return result;
}
if (node->cluster == 0) {
DPRINTF(1, ("node has no clusters. adding %lx clusters\n", clusters));
if ((result = allocate_n_fat_entries(vol, clusters, &n)) != B_OK)
return result;
node->cluster = n;
node->end_cluster = get_nth_fat_entry(vol, n, clusters - 1);
// XXX: don't have to do this this way -- can clean up nicely
do {
result = vcache_set_entry(vol, node->vnid,
GENERATE_DIR_CLUSTER_VNID(node->dir_vnid, node->cluster));
// repeat until memory is freed up
if (result != B_OK)
snooze(5000LL);
} while (result != B_OK);
/* write to disk so that get_next_dirent doesn't barf */
write_vnode_entry(vol, node);
return result;
}
i = (node->st_size + vol->bytes_per_sector * vol->sectors_per_cluster - 1) /
vol->bytes_per_sector / vol->sectors_per_cluster;
if (i == clusters) return B_OK;
if (clusters > i) {
// add new fat entries
DPRINTF(1, ("adding %lx new fat entries\n", clusters - i));
if ((result = allocate_n_fat_entries(vol, clusters - i, &n)) != B_OK)
return result;
ASSERT(IS_DATA_CLUSTER(n));
result = set_fat_entry(vol, node->end_cluster, n);
if (result < B_OK) {
clear_fat_chain(vol, n);
return result;
}
node->end_cluster = get_nth_fat_entry(vol, n, clusters - i - 1);
return result;
}
// traverse fat chain
c = node->cluster;
n = get_fat_entry(vol,c);
for (i=1;i<clusters;i++) {
if (!IS_DATA_CLUSTER(n))
break;
c = n;
n = get_fat_entry(vol,c);
}
ASSERT(i == clusters); ASSERT(n != END_FAT_ENTRY);
if ((i == clusters) && (n == END_FAT_ENTRY)) return B_OK;
if (n < 0) return n;
if ((n != END_FAT_ENTRY) && !IS_DATA_CLUSTER(n)) return EINVAL;
// clear trailing fat entries
DPRINTF(1, ("clearing trailing fat entries\n"));
if ((result = set_fat_entry(vol, c, 0x0fffffff)) != B_OK)
return result;
node->end_cluster = c;
return clear_fat_chain(vol, n);
}
void dump_fat_chain(nspace *vol, uint32 cluster)
{
dprintf("fat chain: %lx", cluster);
while (IS_DATA_CLUSTER(cluster)) {
cluster = get_fat_entry(vol, cluster);
dprintf(" %lx", cluster);
}
dprintf("\n");
}
status_t fragment(nspace *vol, uint32 *pattern)
{
uint32 sector, offset, previous_entry, i, val;
uchar *buffer;
bool dirty = FALSE;
srand(time(NULL)|1);
if (vol->fat_bits == 16)
previous_entry = 0xffff;
else if (vol->fat_bits == 32)
previous_entry = 0x0fffffff;
else {
dprintf("fragment: only for FAT16 and FAT32\n");
return ENOSYS;
}
sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat +
((vol->total_clusters + 2 - 1) * (vol->fat_bits / 8)) /
vol->bytes_per_sector;
offset = ((vol->total_clusters + 2 - 1) * (vol->fat_bits / 8)) %
vol->bytes_per_sector;
buffer = (uchar *)get_block(vol->fd, sector, vol->bytes_per_sector);
if (!buffer) {
dprintf("fragment: error getting fat block %lx\n", sector);
return EINVAL;
}
val = pattern ? *pattern : rand();
for (i=vol->total_clusters+1;i>=2;i--) {
if (val & (1 << (i & 31))) {
if (vol->fat_bits == 16) {
if (read16(buffer, offset) == 0) {
buffer[offset+0] = (previous_entry ) & 0xff;
buffer[offset+1] = (previous_entry >> 8) & 0xff;
previous_entry = i;
dirty = TRUE;
vol->free_clusters--;
}
} else {
if (read32(buffer, offset) == 0) {
buffer[offset+0] = (previous_entry ) & 0xff;
buffer[offset+1] = (previous_entry >> 8) & 0xff;
buffer[offset+2] = (previous_entry >> 16) & 0xff;
buffer[offset+3] = (previous_entry >> 24) & 0xff;
previous_entry = i;
dirty = TRUE;
vol->free_clusters--;
}
}
}
if (!offset) {
if (dirty) {
mark_blocks_dirty(vol->fd, sector, 1);
mirror_fats(vol, sector, buffer);
}
release_block(vol->fd, sector);
dirty = FALSE;
sector--;
buffer = (uchar *)get_block(vol->fd, sector,
vol->bytes_per_sector);
if (!buffer) {
dprintf("fragment: error getting fat block %lx\n", sector);
return EINVAL;
}
}
offset = (offset - vol->fat_bits / 8 + vol->bytes_per_sector) %
vol->bytes_per_sector;
if (!pattern && ((i & 31) == 31))
val = rand();
}
if (dirty) {
mark_blocks_dirty(vol->fd, sector, 1);
mirror_fats(vol, sector, buffer);
}
release_block(vol->fd, sector);
vol->last_allocated = (rand() % vol->total_clusters) + 2;
return B_OK;
}

View File

@ -0,0 +1,31 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_FAT_H_
#define _DOSFS_FAT_H_
#define vIS_DATA_CLUSTER(vol,cluster) (((cluster) >= 2) && ((cluster) < vol->total_clusters + 2))
#define IS_DATA_CLUSTER(cluster) vIS_DATA_CLUSTER(vol,cluster)
// cluster 1 represents root directory for fat12 and fat16
#define IS_FIXED_ROOT(cluster) ((cluster) == 1)
int32 count_free_clusters(nspace *vol);
int32 get_nth_fat_entry(nspace *vol, int32 cluster, uint32 n);
uint32 count_clusters(nspace *vol, int32 cluster);
/* remember to update vnode iteration after calling this function */
status_t clear_fat_chain(nspace *vol, uint32 cluster);
/* remember to set end of chain field when merging into a vnode */
status_t allocate_n_fat_entries(nspace *vol, int32 n, int32 *start);
/* remember to update vnode iteration after calling this function */
status_t set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters);
void dump_fat_chain(nspace *vol, uint32 cluster);
status_t fragment(nspace *vol, uint32 *pattern);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_FILE_H_
#define _DOSFS_FILE_H_
status_t write_vnode_entry(nspace *vol, vnode *node);
int dosfs_write_vnode(void *_vol, void *_node, char r);
int dosfs_rstat(void *_vol, void *_node, struct stat *st);
int dosfs_open(void *_vol, void *_node, int omode, void **cookie);
int dosfs_read(void *_vol, void *_node, void *cookie, off_t pos,
void *buf, size_t *len);
int dosfs_free_cookie(void *vol, void *node, void *cookie);
int dosfs_close(void *vol, void *node, void *cookie);
int dosfs_remove_vnode(void *vol, void *node, char r);
int dosfs_create(void *vol, void *dir, const char *name,
int perms, int omode, vnode_id *vnid, void **cookie);
int dosfs_mkdir(void *vol, void *dir, const char *name, int perms);
int dosfs_rename(void *vol, void *olddir, const char *oldname,
void *newdir, const char *newname);
int dosfs_unlink(void *vol, void *dir, const char *name);
int dosfs_rmdir(void *vol, void *dir, const char *name);
int dosfs_wstat(void *vol, void *node, struct stat *st, long mask);
int dosfs_write(void *vol, void *node, void *cookie, off_t pos,
const void *buf, size_t *len);
#endif

View File

@ -0,0 +1,245 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _FSPROTO_H
#define _FSPROTO_H
#include <sys/dirent.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iovec.h>
#include <OS.h>
#include <fs_attr.h>
#include <fs_info.h>
#include <BeBuild.h>
#include <Drivers.h>
typedef dev_t nspace_id;
typedef ino_t vnode_id;
/*
* PUBLIC PART OF THE FILE SYSTEM PROTOCOL
*/
#define WSTAT_MODE 0x0001
#define WSTAT_UID 0x0002
#define WSTAT_GID 0x0004
#define WSTAT_SIZE 0x0008
#define WSTAT_ATIME 0x0010
#define WSTAT_MTIME 0x0020
#define WSTAT_CRTIME 0x0040
#define WFSSTAT_NAME 0x0001
#define B_ENTRY_CREATED 1
#define B_ENTRY_REMOVED 2
#define B_ENTRY_MOVED 3
#define B_STAT_CHANGED 4
#define B_ATTR_CHANGED 5
#define B_DEVICE_MOUNTED 6
#define B_DEVICE_UNMOUNTED 7
#define B_STOP_WATCHING 0x0000
#define B_WATCH_NAME 0x0001
#define B_WATCH_STAT 0x0002
#define B_WATCH_ATTR 0x0004
#define B_WATCH_DIRECTORY 0x0008
#define SELECT_READ 1
#define SELECT_WRITE 2
#define SELECT_EXCEPTION 3
#define B_CUR_FS_API_VERSION 2
struct attr_info;
struct index_info;
typedef int op_read_vnode(void *ns, vnode_id vnid, char r, void **node);
typedef int op_write_vnode(void *ns, void *node, char r);
typedef int op_remove_vnode(void *ns, void *node, char r);
typedef int op_secure_vnode(void *ns, void *node);
typedef int op_walk(void *ns, void *base, const char *file, char **newpath,
vnode_id *vnid);
typedef int op_access(void *ns, void *node, int mode);
typedef int op_create(void *ns, void *dir, const char *name,
int omode, int perms, vnode_id *vnid, void **cookie);
typedef int op_mkdir(void *ns, void *dir, const char *name, int perms);
typedef int op_symlink(void *ns, void *dir, const char *name,
const char *path);
typedef int op_link(void *ns, void *dir, const char *name, void *node);
typedef int op_rename(void *ns, void *olddir, const char *oldname,
void *newdir, const char *newname);
typedef int op_unlink(void *ns, void *dir, const char *name);
typedef int op_rmdir(void *ns, void *dir, const char *name);
typedef int op_readlink(void *ns, void *node, char *buf, size_t *bufsize);
typedef int op_opendir(void *ns, void *node, void **cookie);
typedef int op_closedir(void *ns, void *node, void *cookie);
typedef int op_rewinddir(void *ns, void *node, void *cookie);
typedef int op_readdir(void *ns, void *node, void *cookie, long *num,
struct dirent *buf, size_t bufsize);
typedef int op_open(void *ns, void *node, int omode, void **cookie);
typedef int op_close(void *ns, void *node, void *cookie);
typedef int op_free_cookie(void *ns, void *node, void *cookie);
typedef int op_read(void *ns, void *node, void *cookie, off_t pos, void *buf,
size_t *len);
typedef int op_write(void *ns, void *node, void *cookie, off_t pos,
const void *buf, size_t *len);
typedef int op_readv(void *ns, void *node, void *cookie, off_t pos, const iovec *vec,
size_t count, size_t *len);
typedef int op_writev(void *ns, void *node, void *cookie, off_t pos, const iovec *vec,
size_t count, size_t *len);
typedef int op_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf,
size_t len);
typedef int op_setflags(void *ns, void *node, void *cookie, int flags);
typedef int op_rstat(void *ns, void *node, struct stat *);
typedef int op_wstat(void *ns, void *node, struct stat *, long mask);
typedef int op_fsync(void *ns, void *node);
typedef int op_select(void *ns, void *node, void *cookie, uint8 event,
uint32 ref, selectsync *sync);
typedef int op_deselect(void *ns, void *node, void *cookie, uint8 event,
selectsync *sync);
typedef int op_initialize(const char *devname, void *parms, size_t len);
typedef int op_mount(nspace_id nsid, const char *devname, ulong flags,
void *parms, size_t len, void **data, vnode_id *vnid);
typedef int op_unmount(void *ns);
typedef int op_sync(void *ns);
typedef int op_rfsstat(void *ns, struct fs_info *);
typedef int op_wfsstat(void *ns, struct fs_info *, long mask);
typedef int op_open_attrdir(void *ns, void *node, void **cookie);
typedef int op_close_attrdir(void *ns, void *node, void *cookie);
typedef int op_rewind_attrdir(void *ns, void *node, void *cookie);
typedef int op_read_attrdir(void *ns, void *node, void *cookie, long *num,
struct dirent *buf, size_t bufsize);
typedef int op_remove_attr(void *ns, void *node, const char *name);
typedef int op_rename_attr(void *ns, void *node, const char *oldname,
const char *newname);
typedef int op_stat_attr(void *ns, void *node, const char *name,
struct attr_info *buf);
typedef int op_write_attr(void *ns, void *node, const char *name, int type,
const void *buf, size_t *len, off_t pos);
typedef int op_read_attr(void *ns, void *node, const char *name, int type,
void *buf, size_t *len, off_t pos);
typedef int op_open_indexdir(void *ns, void **cookie);
typedef int op_close_indexdir(void *ns, void *cookie);
typedef int op_rewind_indexdir(void *ns, void *cookie);
typedef int op_read_indexdir(void *ns, void *cookie, long *num,
struct dirent *buf, size_t bufsize);
typedef int op_create_index(void *ns, const char *name, int type, int flags);
typedef int op_remove_index(void *ns, const char *name);
typedef int op_rename_index(void *ns, const char *oldname,
const char *newname);
typedef int op_stat_index(void *ns, const char *name, struct index_info *buf);
typedef int op_open_query(void *ns, const char *query, ulong flags,
port_id port, long token, void **cookie);
typedef int op_close_query(void *ns, void *cookie);
typedef int op_read_query(void *ns, void *cookie, long *num,
struct dirent *buf, size_t bufsize);
typedef struct vnode_ops {
op_read_vnode (*read_vnode);
op_write_vnode (*write_vnode);
op_remove_vnode (*remove_vnode);
op_secure_vnode (*secure_vnode);
op_walk (*walk);
op_access (*access);
op_create (*create);
op_mkdir (*mkdir);
op_symlink (*symlink);
op_link (*link);
op_rename (*rename);
op_unlink (*unlink);
op_rmdir (*rmdir);
op_readlink (*readlink);
op_opendir (*opendir);
op_closedir (*closedir);
op_free_cookie (*free_dircookie);
op_rewinddir (*rewinddir);
op_readdir (*readdir);
op_open (*open);
op_close (*close);
op_free_cookie (*free_cookie);
op_read (*read);
op_write (*write);
op_readv (*readv);
op_writev (*writev);
op_ioctl (*ioctl);
op_setflags (*setflags);
op_rstat (*rstat);
op_wstat (*wstat);
op_fsync (*fsync);
op_initialize (*initialize);
op_mount (*mount);
op_unmount (*unmount);
op_sync (*sync);
op_rfsstat (*rfsstat);
op_wfsstat (*wfsstat);
op_select (*select);
op_deselect (*deselect);
op_open_indexdir (*open_indexdir);
op_close_indexdir (*close_indexdir);
op_free_cookie (*free_indexdircookie);
op_rewind_indexdir (*rewind_indexdir);
op_read_indexdir (*read_indexdir);
op_create_index (*create_index);
op_remove_index (*remove_index);
op_rename_index (*rename_index);
op_stat_index (*stat_index);
op_open_attrdir (*open_attrdir);
op_close_attrdir (*close_attrdir);
op_free_cookie (*free_attrdircookie);
op_rewind_attrdir (*rewind_attrdir);
op_read_attrdir (*read_attrdir);
op_write_attr (*write_attr);
op_read_attr (*read_attr);
op_remove_attr (*remove_attr);
op_rename_attr (*rename_attr);
op_stat_attr (*stat_attr);
op_open_query (*open_query);
op_close_query (*close_query);
op_free_cookie (*free_querycookie);
op_read_query (*read_query);
} vnode_ops;
extern _IMPEXP_KERNEL int new_path(const char *path, char **copy);
extern _IMPEXP_KERNEL void free_path(char *p);
extern _IMPEXP_KERNEL int notify_listener(int op, nspace_id nsid,
vnode_id vnida, vnode_id vnidb,
vnode_id vnidc, const char *name);
extern _IMPEXP_KERNEL int send_notification(port_id port, long token,
ulong what, long op, nspace_id nsida,
nspace_id nsidb, vnode_id vnida,
vnode_id vnidb, vnode_id vnidc,
const char *name);
extern _IMPEXP_KERNEL int get_vnode(nspace_id nsid, vnode_id vnid, void **data);
extern _IMPEXP_KERNEL int put_vnode(nspace_id nsid, vnode_id vnid);
extern _IMPEXP_KERNEL int new_vnode(nspace_id nsid, vnode_id vnid, void *data);
extern _IMPEXP_KERNEL int remove_vnode(nspace_id nsid, vnode_id vnid);
extern _IMPEXP_KERNEL int unremove_vnode(nspace_id nsid, vnode_id vnid);
extern _IMPEXP_KERNEL int is_vnode_removed(nspace_id nsid, vnode_id vnid);
extern _EXPORT vnode_ops fs_entry;
extern _EXPORT int32 api_version;
#endif

View File

@ -0,0 +1,310 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include <KernelExport.h>
#include <fsproto.h>
#include <lock.h>
#include <cache.h>
#include "iter.h"
#include "dosfs.h"
#include "fat.h"
#include "util.h"
#define DPRINTF(a,b) if (debug_iter > (a)) dprintf b
CHECK_MAGIC(diri,struct diri,DIRI_MAGIC)
static int _validate_cs_(nspace *vol, uint32 cluster, uint32 sector)
{
if (sector < 0) return -1;
if ((vol->fat_bits != 32) && IS_FIXED_ROOT(cluster)) { // fat12 or fat16 root
if (sector >= vol->root_sectors)
return -1;
return 0;
}
if (sector >= vol->sectors_per_cluster) return -1;
if (!IS_DATA_CLUSTER(cluster)) return -1;
return 0;
}
static off_t _csi_to_block_(struct csi *csi)
{
// presumes the caller has already called _validate_cs_ on the argument
ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
return EINVAL;
if (IS_FIXED_ROOT(csi->cluster))
return csi->vol->root_start + csi->sector;
return csi->vol->data_start +
(off_t)(csi->cluster - 2)* csi->vol->sectors_per_cluster +
csi->sector;
}
int init_csi(nspace *vol, uint32 cluster, uint32 sector, struct csi *csi)
{
int ret;
if ((ret = _validate_cs_(vol,cluster,sector)) != 0)
return ret;
csi->vol = vol; csi->cluster = cluster; csi->sector = sector;
return 0;
}
int iter_csi(struct csi *csi, int sectors)
{
if (csi->sector == 0xffff) // check if already at end of chain
return -1;
if (sectors < 0)
return EINVAL;
if (sectors == 0)
return 0;
if (IS_FIXED_ROOT(csi->cluster)) {
csi->sector += sectors;
if (csi->sector < csi->vol->root_sectors)
return 0;
} else {
csi->sector += sectors;
if (csi->sector < csi->vol->sectors_per_cluster)
return 0;
csi->cluster = get_nth_fat_entry(csi->vol, csi->cluster, csi->sector / csi->vol->sectors_per_cluster);
if ((int32)csi->cluster < 0) {
csi->sector = 0xffff;
return csi->cluster;
}
if (vIS_DATA_CLUSTER(csi->vol,csi->cluster)) {
csi->sector %= csi->vol->sectors_per_cluster;
return 0;
}
}
csi->sector = 0xffff;
return -1;
}
uint8 *csi_get_block(struct csi *csi)
{
if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
return NULL;
return get_block(csi->vol->fd, _csi_to_block_(csi), csi->vol->bytes_per_sector);
}
status_t csi_release_block(struct csi *csi)
{
status_t err;
if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
return EINVAL;
err = release_block(csi->vol->fd, _csi_to_block_(csi));
ASSERT(err == B_OK);
return err;
}
status_t csi_mark_block_dirty(struct csi *csi)
{
status_t err;
ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
return EINVAL;
err = mark_blocks_dirty(csi->vol->fd, _csi_to_block_(csi), 1);
ASSERT(err == B_OK);
return err;
}
/* XXX: not the most efficient implementation, but it gets the job done */
status_t csi_read_blocks(struct csi *csi, uint8 *buffer, ssize_t len)
{
struct csi old_csi;
uint32 sectors;
off_t block;
status_t err;
ASSERT(len >= csi->vol->bytes_per_sector);
if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
return EINVAL;
sectors = 1;
block = _csi_to_block_(csi);
while (1) {
old_csi = *csi;
err = iter_csi(csi, 1);
if (len < (sectors + 1) * csi->vol->bytes_per_sector)
break;
if ((err < B_OK) || (block + sectors != _csi_to_block_(csi)))
break;
sectors++;
}
err = cached_read(csi->vol->fd, block, buffer, sectors, csi->vol->bytes_per_sector);
if (err < B_OK)
return err;
*csi = old_csi;
return sectors * csi->vol->bytes_per_sector;
}
status_t csi_write_blocks(struct csi *csi, uint8 *buffer, ssize_t len)
{
struct csi old_csi;
uint32 sectors;
off_t block;
status_t err;
ASSERT(len >= csi->vol->bytes_per_sector);
ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
return EINVAL;
sectors = 1;
block = _csi_to_block_(csi);
while (1) {
old_csi = *csi;
err = iter_csi(csi, 1);
if (len < (sectors + 1) * csi->vol->bytes_per_sector)
break;
if ((err < B_OK) || (block + sectors != _csi_to_block_(csi)))
break;
sectors++;
}
err = cached_write(csi->vol->fd, block, buffer, sectors, csi->vol->bytes_per_sector);
if (err < B_OK)
return err;
/* return the last state of the iterator because that's what dosfs_write
* expects. this lets it meaningfully cache the state even when it's
* writing to the end of the file. */
*csi = old_csi;
return sectors * csi->vol->bytes_per_sector;
}
status_t csi_write_block(struct csi *csi, uint8 *buffer)
{
ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
return EINVAL;
return cached_write(csi->vol->fd, _csi_to_block_(csi), buffer, 1, csi->vol->bytes_per_sector);
}
static void _diri_release_current_block_(struct diri *diri)
{
ASSERT(diri->current_block);
if (diri->current_block == NULL)
return;
csi_release_block(&(diri->csi));
diri->current_block = NULL;
}
uint8 *diri_init(nspace *vol, uint32 cluster, uint32 index, struct diri *diri)
{
diri->magic = DIRI_MAGIC;
diri->current_block = NULL;
if (cluster >= vol->total_clusters + 2)
return NULL;
if (init_csi(vol,cluster,0,&(diri->csi)) != 0)
return NULL;
diri->starting_cluster = cluster;
diri->current_index = index;
if (index >= vol->bytes_per_sector / 0x20) {
if (iter_csi(&(diri->csi), diri->current_index / (vol->bytes_per_sector / 0x20)) != 0)
return NULL;
}
// get current sector
diri->current_block = csi_get_block(&(diri->csi));
if (diri->current_block == NULL)
return NULL;
return diri->current_block + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
}
int diri_free(struct diri *diri)
{
if (check_diri_magic(diri, "diri_free")) return EINVAL;
diri->magic = ~DIRI_MAGIC; // trash magic number
if (diri->current_block)
_diri_release_current_block_(diri);
return 0;
}
uint8 *diri_current_entry(struct diri *diri)
{
if (check_diri_magic(diri, "diri_current_entry")) return NULL;
if (diri->current_block == NULL)
return NULL;
return diri->current_block + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
}
uint8 *diri_next_entry(struct diri *diri)
{
if (check_diri_magic(diri, "diri_next_entry")) return NULL;
if (diri->current_block == NULL)
return NULL;
if ((++diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20)) == 0) {
_diri_release_current_block_(diri);
if (iter_csi(&(diri->csi), 1) != 0)
return NULL;
diri->current_block = csi_get_block(&(diri->csi));
if (diri->current_block == NULL)
return NULL;
}
return diri->current_block + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
}
uint8 *diri_rewind(struct diri *diri)
{
if (check_diri_magic(diri, "diri_rewind")) return NULL;
if (diri->current_index > (diri->csi.vol->bytes_per_sector / 0x20 - 1)) {
if (diri->current_block)
_diri_release_current_block_(diri);
if (init_csi(diri->csi.vol, diri->starting_cluster, 0, &(diri->csi)) != 0)
return NULL;
diri->current_block = csi_get_block(&(diri->csi));
}
diri->current_index = 0;
return diri->current_block;
}
void diri_mark_dirty(struct diri *diri)
{
csi_mark_block_dirty(&(diri->csi));
}

View File

@ -0,0 +1,47 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_ITER_H_
#define _DOSFS_ITER_H_
struct _nspace;
/* csi keeps track of current cluster and sector info */
struct csi
{
struct _nspace *vol;
uint32 cluster;
uint32 sector;
};
int init_csi(struct _nspace *vol, uint32 cluster, uint32 sector, struct csi *csi);
int iter_csi(struct csi *csi, int sectors);
uint8 *csi_get_block(struct csi *csi);
status_t csi_release_block(struct csi *csi);
status_t csi_mark_block_dirty(struct csi *csi);
status_t csi_read_blocks(struct csi *csi, uint8 *buffer, ssize_t len);
status_t csi_write_blocks(struct csi *csi, uint8 *buffer, ssize_t len);
status_t csi_write_block(struct csi *csi, uint8 *buffer);
/* directory entry iterator */
#define DIRI_MAGIC '!duM'
struct diri
{
uint32 magic;
struct csi csi;
uint32 starting_cluster;
uint32 current_index;
uint8 *current_block;
};
uint8 *diri_init(struct _nspace *vol, uint32 cluster, uint32 index, struct diri *diri);
int diri_free(struct diri *diri);
uint8 *diri_current_entry(struct diri *diri);
uint8 *diri_next_entry(struct diri *diri);
uint8 *diri_rewind(struct diri *diri);
void diri_mark_dirty(struct diri *diri);
int check_diri_magic(struct diri *t, char *funcname);
#endif

View File

@ -0,0 +1,23 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _ALLOC_H
#define _ALLOC_H
#include <SupportDefs.h>
#if KMALLOC_TRACKING
extern bool kmalloc_tracking_enabled;
#endif
extern void init_malloc(void);
extern void init_smalloc(void);
extern void * smalloc(unsigned int nbytes);
extern void sfree(void *ptr);
extern void * scalloc(unsigned int nobj, unsigned int size);
extern void * srealloc(void *p, unsigned int newsize);
#endif

View File

@ -0,0 +1,46 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _LOCK_H
#define _LOCK_H
#include <BeBuild.h>
#include <OS.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct lock lock;
typedef struct mlock mlock;
struct lock {
sem_id s;
long c;
};
struct mlock {
sem_id s;
};
extern _IMPEXP_KERNEL int new_lock(lock *l, const char *name);
extern _IMPEXP_KERNEL int free_lock(lock *l);
#define LOCK(l) if (atomic_add(&l.c, -1) <= 0) acquire_sem(l.s);
#define UNLOCK(l) if (atomic_add(&l.c, 1) < 0) release_sem(l.s);
extern _IMPEXP_KERNEL int new_mlock(mlock *l, long c, const char *name);
extern _IMPEXP_KERNEL int free_mlock(mlock *l);
#define LOCKM(l,cnt) acquire_sem_etc(l.s, cnt, 0, 0)
#define UNLOCKM(l,cnt) release_sem_etc(l.s, cnt, 0)
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View File

@ -0,0 +1,105 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
extended: 2001-12-11 by Marcus Overhagen
*/
struct ext_mime {
char *extension;
char *mime;
};
struct ext_mime mimes[] = {
{ "gz", "application/x-gzip" },
{ "hqx", "application/x-binhex40" },
{ "lha", "application/x-lharc" },
{ "lzh", "application/x-lharc" },
{ "pcl", "application/x-pcl" },
{ "pdf", "application/pdf" },
{ "ps", "application/postscript" },
{ "sit", "application/x-stuff-it" },
{ "tar", "application/x-tar" },
{ "tgz", "application/x-gzip" },
{ "uue", "application/x-uuencode" },
{ "z", "application/x-compress" },
{ "zip", "application/zip" },
{ "zoo", "application/x-zoo" },
{ "pkg", "application/x-scode-UPkg" },
{ "vdwn", "application/x-scode-UPkg" },
{ "proj", "application/x-mw-project" },
{ "swf", "application/x-shockwave-flash" },
{ "clp", "application/x-codeliege-project" },
{ "aif", "audio/x-aiff" },
{ "aifc", "audio/x-aifc" },
{ "aiff", "audio/x-aiff" },
{ "au", "audio/basic" },
{ "mid", "audio/x-midi" },
{ "midi", "audio/x-midi" },
{ "mod", "audio/mod" },
{ "ra", "audio/x-real-audio" },
{ "wav", "audio/x-wav" },
{ "mp2", "audio/x-mpeg" },
{ "mp3", "audio/x-mpeg" },
{ "ogg", "audio/x-vorbis" },
{ "mpc", "audio/x-mpc" },
{ "asf", "application/x-asf" },
{ "riff", "application/x-riff" },
{ "wma", "audio/x-ms-wma" },
{ "bmp", "image/x-bmp" },
{ "fax", "image/g3fax" },
{ "gif", "image/gif" },
{ "iff", "image/x-iff" },
{ "jpg", "image/jpeg" },
{ "jpeg", "image/jpeg" },
{ "pbm", "image/x-portable-bitmap" },
{ "pcx", "image/x-pcx" },
{ "pgm", "image/x-portable-graymap" },
{ "png", "image/png" },
{ "ppm", "image/x-portable-pixmap" },
{ "rgb", "image/x-rgb" },
{ "tga", "image/x-targa" },
{ "tif", "image/tiff" },
{ "tiff", "image/tiff" },
{ "xbm", "image/x-xbitmap" },
{ "txt", "text/plain" },
{ "ini", "text/plain" },
{ "log", "text/plain" },
{ "bat", "text/plain" },
{ "doc", "text/plain" },
{ "cfg", "text/plain" },
{ "inf", "text/plain" },
{ "htm", "text/html" },
{ "html", "text/html" },
{ "rtf", "text/rtf" },
{ "c", "text/x-source-code" },
{ "cc", "text/x-source-code" },
{ "c++", "text/x-source-code" },
{ "h", "text/x-source-code" },
{ "h++", "text/x-source-code" },
{ "hh", "text/x-source-code" },
{ "hpp", "text/x-source-code" },
{ "pl", "text/x-source-code" },
{ "py", "text/x-source-code" },
{ "cxx", "text/x-source-code" },
{ "cpp", "text/x-source-code" },
{ "S", "text/x-source-code" },
{ "asm", "text/x-source-code" },
{ "bas", "text/x-source-code" },
{ "pas", "text/x-source-code" },
{ "java", "text/x-source-code" },
{ "avi", "video/x-msvideo" },
{ "mov", "video/quicktime" },
{ "mpg", "video/mpeg" },
{ "mpeg", "video/mpeg" },
{ "ogm", "video/x-ogm" },
{ "wmv", "video/x-ms-wmv" },
{ "rm", "application/vnd.rn-realmedia" },
{ "rn", "application/vnd.rn-realmedia" },
{ 0, 0 }
};

View File

@ -0,0 +1,16 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef MIME_TYPES_H
#define MIME_TYPES_H
struct ext_mime {
char *extension;
char *mime;
};
extern struct ext_mime mimes[];
#endif

View File

@ -0,0 +1,23 @@
/* ++++++++++
FILE: rtc_info.h
NAME: mani
DATE: 2/1998
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
+++++ */
#ifndef _RTC_INFO_H
#define _RTC_INFO_H
typedef struct {
uint32 time;
bool is_gmt;
int32 tz_minuteswest;
int32 tz_dsttime;
} rtc_info;
#define RTC_SETTINGS_FILE "RTC_time_settings"
extern _IMPEXP_KERNEL status_t get_rtc_info(rtc_info *);
#endif /* _RTC_INFO_H */

View File

@ -0,0 +1,17 @@
# Sample settings file for the dosfs plugin
#
# This file should be moved to the directory
# /boot/home/config/settings/kernel/drivers/
#
# lock device:
# true = (default) locks the device's door if:
# the filesystem is RW
# the device is removable
# false = no door locking
lock_device true
# sync mode:
# 0 = (default) no sync after each operation
# 1 = sync after each operation on removable media only
# 2 = sync after each operation always
op_sync_mode 0

View File

@ -0,0 +1,149 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include <SupportDefs.h>
#include <KernelExport.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <fsproto.h>
#include <rtc_info.h>
#include "dosfs.h"
#include "fat.h"
#include "util.h"
static int32 tzoffset = -1; /* in minutes */
int _assert_(char *a, int b, char *c)
{
dprintf("tripped assertion in %s/%d (%s)\n", a, b, c);
kernel_debugger("tripped assertion");
return 0;
}
static void print_byte(uint8 c)
{
dprintf("%c", ((c >= ' ') && (c <= '~')) ? c : '.');
}
void dump_bytes(uint8 *buffer, uint32 count)
{
uint32 i, j, k;
for (i=0;i<0x10;i++)
dprintf(" %lX ", i);
dprintf("\n");
for (i=0;i<count;i+=0x10) {
j = (i + 0x10 > count) ? count - i : 0x10;
for (k=i;k<i+j;k++)
dprintf("%2.2X ", buffer[k]);
for (;k<i+0x10;k++)
dprintf(" ");
dprintf(" ");
for (k=i;k<i+j;k++)
print_byte(buffer[k]);
dprintf("\n");
}
}
void dump_directory(uint8 *buffer)
{
dump_bytes(buffer, 32);
}
static void get_tzoffset()
{
rtc_info info;
if (tzoffset != -1)
return;
if (get_rtc_info(&info) < 0) {
dprintf("error getting rtc info\n");
} else {
tzoffset = info.tz_minuteswest;
}
}
// If divisible by 4, but not divisible by 100, but divisible by 400, it's a leap year
// 1996 is leap, 1900 is not, 2000 is, 2100 is not
#define IS_LEAP_YEAR(y) ((((y) % 4) == 0) && (((y) % 100) || ((((y)) % 400) == 0)))
/* returns leap days since 1970 */
static int leaps(int yr, int mon)
{
// yr is 1970-based, mon 0-based
int result = (yr+2)/4 - (yr + 70) / 100;
if((yr+70) >= 100) result++; // correct for 2000
if (IS_LEAP_YEAR(yr + 1970))
if (mon < 2) result--;
return result;
}
static int daze[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,0,0,0 };
time_t dos2time_t(uint32 t)
{
time_t days;
get_tzoffset();
//dprintf("%d/%d/%d %d:%2.2d:%2.2d\n",
// (t>>25)+1980,((t>>21)&15),((t>>16)&31),
// (t>>11)&31,(t>>5)&63,2*(t&31));
days = daze[(t>>21)&15] + ((t>>25)+10)*365 + leaps((t>>25)+10,((t>>21)&15)-1)+((t>>16)&31)-1;
return (((days * 24) + ((t>>11)&31)) * 60 + ((t>>5)&63) + tzoffset) * 60 + 2*(t&31);
}
uint32 time_t2dos(time_t s)
{
uint32 t, d, y;
int days;
get_tzoffset();
t = (s % 60) / 2; s /= 60; s -= tzoffset;
t += (s % 60) << 5; s /= 60;
t += (s % 24) << 11;s /= 24;
s -= 10*365 + 2; // convert from 1970-based year to 1980-based year
for (y=0;;y++) {
days = IS_LEAP_YEAR(1980+y) ? 366 : 365;
if (s < days) break;
s -= days;
}
if (IS_LEAP_YEAR(1980+y)) {
if (s == 59) {
d = (1 << 5) + 28; /* 2/29, 0 based */
goto bi;
} else if (s > 59)
s--;
}
for (d=0;d<11;d++)
if (daze[d+2] > s)
break;
d = (d << 5) + (s - daze[d+1]);
bi:
d += (1 << 5) + 1; // make date 1-based
return t + (d << 16) + (y << 25);
}
uint8 hash_msdos_name(const char *name)
{
const uint8 *p = (const uint8 *)name;
int i;
uint8 c = 0;
for (i=0;i<11;i++)
c = (c << 7) + (c >> 1) + *(p++);
return c;
}

View File

@ -0,0 +1,43 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_UTIL_H_
#define _DOSFS_UTIL_H_
#include <ByteOrder.h>
// debugging functions
#ifndef DEBUG
#define ASSERT(c) ((void)0)
#else
int _assert_(char *,int,char *);
#define ASSERT(c) (!(c) ? _assert_(__FILE__,__LINE__,#c) : 0)
#endif
void dump_bytes(uint8 *buffer, uint32 count);
void dump_directory(uint8 *buffer);
// time
time_t dos2time_t(uint32 t);
uint32 time_t2dos(time_t s);
uint8 hash_msdos_name(const char *name);
#if 0
#define read32(buffer,off) \
(((uint8 *)buffer)[(off)] + (((uint8 *)buffer)[(off)+1] << 8) + \
(((uint8 *)buffer)[(off)+2] << 16) + (((uint8 *)buffer)[(off)+3] << 24))
#define read16(buffer,off) \
(((uint8 *)buffer)[(off)] + (((uint8 *)buffer)[(off)+1] << 8))
#endif
#define read32(buffer,off) \
B_LENDIAN_TO_HOST_INT32(*(uint32 *)&buffer[off])
#define read16(buffer,off) \
B_LENDIAN_TO_HOST_INT16(*(uint16 *)&buffer[off])
#endif

View File

@ -0,0 +1,443 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
/*
The FAT file system has no good way of assigning unique persistent values to
nodes. The only obvious choice, storing the starting cluster number of the
file, is unusable because 0 byte files exist as directory entries only.
Further, even if it were usable, it would potentially require a full directory
tree traversal to locate an arbitrary node. We must resort to some ickiness
in order to make persistent vnode id's (at least across a given mount) work.
There are three ways to encode a vnode id:
1. Combine the starting cluster of the entry with the starting cluster of the
directory it appears in. This is used for files with data.
2. Combine the starting cluster of the directory the entry appears in with the
index of the entry in the directory. This is used for 0-byte files.
3. A unique number that doesn't match any possible values from encodings 1 or
2.
With the first encoding, the vnode id is invalidated (i.e. no longer describes
the file's location) when the file moves to a different directory or when
its starting cluster changes (this can occur if the file is truncated and data
is subsequently written to it).
With the second encoding, the vnode id is invalidated when the file position
is moved within a directory (as a result of a renaming), when it's moved to a
different directory, or when data is written to it.
The third encoding doesn't describe the file's location on disk, and so it is
invalid from the start.
Since we can't change vnode id's once they are assigned, we have to create a
mapping table to translate vnode id's to locations. This file serves this
purpose.
*/
#define DPRINTF(a,b) if (debug_vcache > (a)) dprintf b
#define LOCK_CACHE_R \
acquire_sem(vol->vcache.vc_sem)
#define LOCK_CACHE_W \
acquire_sem_etc(vol->vcache.vc_sem, READERS, 0, 0)
#define UNLOCK_CACHE_R \
release_sem(vol->vcache.vc_sem)
#define UNLOCK_CACHE_W \
release_sem_etc(vol->vcache.vc_sem, READERS, 0)
#include <fsproto.h>
#include <KernelExport.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "dosfs.h"
#include "vcache.h"
#include "util.h"
struct vcache_entry {
vnode_id vnid; /* originally reported vnid */
vnode_id loc; /* where the file is now */
struct vcache_entry *next_vnid; /* next entry in vnid hash table */
struct vcache_entry *next_loc; /* next entry in location hash table */
};
void dump_vcache(nspace *vol)
{
uint32 i;
struct vcache_entry *c;
dprintf("vnid cache size %lx, cur vnid = %Lx\n"
"vnid loc\n",
vol->vcache.cache_size, vol->vcache.cur_vnid);
for (i=0;i<vol->vcache.cache_size;i++)
for (c = vol->vcache.by_vnid[i];c;c=c->next_vnid)
dprintf("%16Lx %16Lx\n", c->vnid, c->loc);
}
#define hash(v) ((v) & (vol->vcache.cache_size-1))
status_t init_vcache(nspace *vol)
{
char name[16];
DPRINTF(0, ("init_vcache called\n"));
vol->vcache.cur_vnid = ARTIFICIAL_VNID_BITS;
#if DEBUG
vol->vcache.cache_size = 1;
#else
vol->vcache.cache_size = 512; /* must be power of 2 */
#endif
vol->vcache.by_vnid = calloc(sizeof(struct vache_entry *), vol->vcache.cache_size);
if (vol->vcache.by_vnid == NULL) {
dprintf("init_vcache: out of core\n");
return ENOMEM;
}
vol->vcache.by_loc = calloc(sizeof(struct vache_entry *), vol->vcache.cache_size);
if (vol->vcache.by_loc == NULL) {
dprintf("init_vcache: out of core\n");
free(vol->vcache.by_vnid);
vol->vcache.by_vnid = NULL;
return ENOMEM;
}
sprintf(name, "fat cache %lx", vol->id);
if ((vol->vcache.vc_sem = create_sem(READERS, name)) < 0) {
free(vol->vcache.by_vnid); vol->vcache.by_vnid = NULL;
free(vol->vcache.by_loc); vol->vcache.by_loc = NULL;
return vol->vcache.vc_sem;
}
DPRINTF(0, ("init_vcache: initialized vnid cache with %lx entries\n", vol->vcache.cache_size));
return 0;
}
status_t uninit_vcache(nspace *vol)
{
uint32 i, count = 0;
struct vcache_entry *c, *n;
DPRINTF(0, ("uninit_vcache called\n"));
LOCK_CACHE_W;
/* free entries */
for (i=0;i<vol->vcache.cache_size;i++) {
c = vol->vcache.by_vnid[i];
while (c) {
count++;
n = c->next_vnid;
free(c);
c = n;
}
}
DPRINTF(0, ("%lx vcache entries removed\n", count));
free(vol->vcache.by_vnid); vol->vcache.by_vnid = NULL;
free(vol->vcache.by_loc); vol->vcache.by_loc = NULL;
delete_sem(vol->vcache.vc_sem);
return 0;
}
vnode_id generate_unique_vnid(nspace *vol)
{
DPRINTF(0, ("generate_unique_vnid\n"));
/* only one thread per volume will be in here at any given time anyway
* due to volume locking */
return vol->vcache.cur_vnid++;
}
static status_t _add_to_vcache_(nspace *vol, vnode_id vnid, vnode_id loc)
{
int hash1 = hash(vnid), hash2 = hash(loc);
struct vcache_entry *e, *c, *p;
DPRINTF(0, ("add_to_vcache %Lx/%Lx\n", vnid, loc));
ASSERT(vnid != loc);
e = malloc(sizeof(struct vcache_entry));
if (e == NULL)
return ENOMEM;
e->vnid = vnid; e->loc = loc; e->next_vnid = NULL; e->next_loc = NULL;
c = p = vol->vcache.by_vnid[hash1];
while (c) {
if (vnid < c->vnid)
break;
ASSERT(vnid != c->vnid); ASSERT(loc != c->loc);
p = c;
c = c->next_vnid;
}
ASSERT(!c || (vnid != c->vnid));
e->next_vnid = c;
if (p == c)
vol->vcache.by_vnid[hash1] = e;
else
p->next_vnid = e;
c = p = vol->vcache.by_loc[hash2];
while (c) {
if (loc < c->loc)
break;
ASSERT(vnid != c->vnid); ASSERT(loc != c->loc);
p = c;
c = c->next_loc;
}
ASSERT(!c || (loc != c->loc));
e->next_loc = c;
if (p == c)
vol->vcache.by_loc[hash2] = e;
else
p->next_loc = e;
return B_OK;
}
static status_t _remove_from_vcache_(nspace *vol, vnode_id vnid)
{
int hash1 = hash(vnid), hash2;
struct vcache_entry *c, *p, *e;
DPRINTF(0, ("remove_from_vcache %Lx\n", vnid));
c = p = vol->vcache.by_vnid[hash1];
while (c) {
if (vnid == c->vnid)
break;
ASSERT(c->vnid < vnid);
p = c;
c = c->next_vnid;
}
ASSERT(c);
if (!c) return ENOENT;
if (p == c)
vol->vcache.by_vnid[hash1] = c->next_vnid;
else
p->next_vnid = c->next_vnid;
e = c;
hash2 = hash(c->loc);
c = p = vol->vcache.by_loc[hash2];
while (c) {
if (vnid == c->vnid)
break;
ASSERT(c->loc < e->loc);
p = c;
c = c->next_loc;
}
ASSERT(c);
if (!c) return ENOENT;
if (p == c)
vol->vcache.by_loc[hash2] = c->next_loc;
else
p->next_loc = c->next_loc;
free(c);
return 0;
}
static struct vcache_entry *_find_vnid_in_vcache_(nspace *vol, vnode_id vnid)
{
int hash1 = hash(vnid);
struct vcache_entry *c;
c = vol->vcache.by_vnid[hash1];
while (c) {
if (c->vnid == vnid)
break;
if (c->vnid > vnid)
return NULL;
c = c->next_vnid;
}
return c;
}
static struct vcache_entry *_find_loc_in_vcache_(nspace *vol, vnode_id loc)
{
int hash2 = hash(loc);
struct vcache_entry *c;
c = vol->vcache.by_loc[hash2];
while (c) {
if (c->loc == loc)
break;
if (c->loc > loc)
return NULL;
c = c->next_loc;
}
return c;
}
status_t add_to_vcache(nspace *vol, vnode_id vnid, vnode_id loc)
{
status_t result;
LOCK_CACHE_W;
result = _add_to_vcache_(vol,vnid,loc);
UNLOCK_CACHE_W;
if (result < 0) DPRINTF(0, ("add_to_vcache failed (%s)\n", strerror(result)));
return result;
}
/* XXX: do this in a smarter fashion */
static status_t _update_loc_in_vcache_(nspace *vol, vnode_id vnid, vnode_id loc)
{
status_t result;
result = _remove_from_vcache_(vol, vnid);
if (result == 0)
result = _add_to_vcache_(vol, vnid, loc);
return result;
}
status_t remove_from_vcache(nspace *vol, vnode_id vnid)
{
status_t result;
LOCK_CACHE_W;
result = _remove_from_vcache_(vol, vnid);
UNLOCK_CACHE_W;
if (result < 0) DPRINTF(0, ("remove_from_vcache failed (%s)\n", strerror(result)));
return result;
}
status_t vcache_vnid_to_loc(nspace *vol, vnode_id vnid, vnode_id *loc)
{
struct vcache_entry *e;
DPRINTF(1, ("vcache_vnid_to_loc %Lx %lx\n", vnid, (uint32)loc));
LOCK_CACHE_R;
e = _find_vnid_in_vcache_(vol, vnid);
if (loc && e)
*loc = e->loc;
UNLOCK_CACHE_R;
return (e) ? B_OK : ENOENT;
}
status_t vcache_loc_to_vnid(nspace *vol, vnode_id loc, vnode_id *vnid)
{
struct vcache_entry *e;
DPRINTF(1, ("vcache_loc_to_vnid %Lx %lx\n", loc, (uint32)vnid));
LOCK_CACHE_R;
e = _find_loc_in_vcache_(vol, loc);
if (vnid && e)
*vnid = e->vnid;
UNLOCK_CACHE_R;
return (e) ? B_OK : ENOENT;
}
status_t vcache_set_entry(nspace *vol, vnode_id vnid, vnode_id loc)
{
struct vcache_entry *e;
status_t result = B_OK;
DPRINTF(0, ("vcache_set_entry: %Lx -> %Lx\n", vnid, loc));
if (is_vnode_removed(vol->id, vnid) > 0) {
if (!IS_ARTIFICIAL_VNID(loc))
return B_OK;
} else {
ASSERT(is_vnode_removed(vol->id, vnid) == 0);
}
LOCK_CACHE_W;
e = _find_vnid_in_vcache_(vol, vnid);
if (e) {
if (e->vnid == loc)
result = _remove_from_vcache_(vol, vnid);
else
result = _update_loc_in_vcache_(vol, vnid, loc);
} else {
if (vnid != loc)
result = _add_to_vcache_(vol,vnid,loc);
}
UNLOCK_CACHE_W;
return result;
}
#if DEBUG
int debug_dfvnid(int argc, char **argv)
{
int i;
nspace *vol;
if (argc < 3) {
kprintf("dfvnid nspace vnid\n");
return B_OK;
}
vol = (nspace *)strtoul(argv[1], NULL, 0);
if (vol == NULL)
return B_OK;
for (i=2;i<argc;i++) {
vnode_id vnid = strtoull(argv[i], NULL, 0);
struct vcache_entry *e;
if ((e = _find_vnid_in_vcache_(vol, vnid)) != NULL) {
kprintf("vnid %Lx -> loc %Lx @ %p\n", vnid, e->loc, e);
} else {
kprintf("vnid %Lx not found in vnid cache\n", vnid);
}
}
return B_OK;
}
int debug_dfloc(int argc, char **argv)
{
int i;
nspace *vol;
if (argc < 3) {
kprintf("dfloc nspace vnid\n");
return B_OK;
}
vol = (nspace *)strtoul(argv[1], NULL, 0);
if (vol == NULL)
return B_OK;
for (i=2;i<argc;i++) {
vnode_id loc = strtoull(argv[i], NULL, 0);
struct vcache_entry *e;
if ((e = _find_loc_in_vcache_(vol, loc)) != NULL) {
kprintf("loc %Lx -> vnid %Lx @ %p\n", loc, e->vnid, e);
} else {
kprintf("loc %Lx not found in vnid cache\n", loc);
}
}
return B_OK;
}
#endif

View File

@ -0,0 +1,30 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _DOSFS_VCACHE_H_
#define _DOSFS_VCACHE_H_
void dump_vcache(nspace *vol);
status_t init_vcache(nspace *vol);
status_t uninit_vcache(nspace *vol);
vnode_id generate_unique_vnid(nspace *vol);
status_t add_to_vcache(nspace *vol, vnode_id vnid, vnode_id loc);
status_t remove_from_vcache(nspace *vol, vnode_id vnid);
status_t vcache_vnid_to_loc(nspace *vol, vnode_id vnid, vnode_id *loc);
status_t vcache_loc_to_vnid(nspace *vol, vnode_id loc, vnode_id *vnid);
status_t vcache_set_entry(nspace *vol, vnode_id vnid, vnode_id loc);
#define find_vnid_in_vcache(vol,vnid) vcache_vnid_to_loc(vol,vnid,NULL)
#define find_loc_in_vcache(vol,loc) vcache_loc_to_vnid(vol,loc,NULL)
#if DEBUG
int debug_dfvnid(int argc, char **argv);
int debug_dfloc(int argc, char **argv);
#endif
#endif

View File

@ -0,0 +1,6 @@
/*
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
const char *build_time = __TIME__;
const char *build_date = __DATE__;