The dos filesystem, published by Be Inc, March 2001.
To be used under the terms of the Be Sample Code License. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2981 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
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/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;
|
||||
}
|
||||
@@ -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 _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
|
||||
@@ -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
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
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");
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
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
|
||||
@@ -0,0 +1,992 @@
|
||||
/*
|
||||
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <Drivers.h>
|
||||
#include <driver_settings.h>
|
||||
|
||||
#include <scsi.h>
|
||||
|
||||
#include <fsproto.h>
|
||||
#include <lock.h>
|
||||
#include <cache.h>
|
||||
|
||||
#include "dosfs.h"
|
||||
#include "attr.h"
|
||||
#include "dir.h"
|
||||
#include "dlist.h"
|
||||
#include "fat.h"
|
||||
#include "file.h"
|
||||
#include "iter.h"
|
||||
#include "util.h"
|
||||
#include "vcache.h"
|
||||
|
||||
extern const char *build_time, *build_date;
|
||||
|
||||
/* debug levels */
|
||||
int debug_attr = 0, debug_dir = 0, debug_dlist = 0, debug_dosfs = 0,
|
||||
debug_encodings = 0, debug_fat = 0, debug_file = 0,
|
||||
debug_iter = 0, debug_vcache = 0;
|
||||
|
||||
#define DPRINTF(a,b) if (debug_dosfs > (a)) dprintf b
|
||||
|
||||
CHECK_MAGIC(vnode,struct vnode,VNODE_MAGIC)
|
||||
CHECK_MAGIC(nspace,struct _nspace, NSPACE_MAGIC)
|
||||
|
||||
#if DEBUG
|
||||
|
||||
int32 instances = 0;
|
||||
|
||||
int debug_dos(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
for (i=1;i<argc;i++) {
|
||||
nspace *vol = (nspace *)strtoul(argv[i], NULL, 0);
|
||||
if (vol == NULL)
|
||||
continue;
|
||||
|
||||
kprintf("dos nspace @ %p\n", vol);
|
||||
kprintf("magic: %lx\n", vol->magic);
|
||||
kprintf("id: %lx, fd: %x, device: %s, flags %lx\n",
|
||||
vol->id, vol->fd, vol->device, vol->flags);
|
||||
kprintf("bytes/sector = %lx, sectors/cluster = %lx, reserved sectors = %lx\n",
|
||||
vol->bytes_per_sector, vol->sectors_per_cluster,
|
||||
vol->reserved_sectors);
|
||||
kprintf("%lx fats, %lx root entries, %lx total sectors, %lx sectors/fat\n",
|
||||
vol->fat_count, vol->root_entries_count, vol->total_sectors,
|
||||
vol->sectors_per_fat);
|
||||
kprintf("media descriptor %x, fsinfo sector %x, %lx clusters, %lx free\n",
|
||||
vol->media_descriptor, vol->fsinfo_sector, vol->total_clusters,
|
||||
vol->free_clusters);
|
||||
kprintf("%x-bit fat, mirrored %x, active %x\n",
|
||||
vol->fat_bits, vol->fat_mirrored, vol->active_fat);
|
||||
kprintf("root start %lx, %lx root sectors, root vnode @ %p\n",
|
||||
vol->root_start, vol->root_sectors, &(vol->root_vnode));
|
||||
kprintf("label entry %lx, label %s\n", vol->vol_entry, vol->vol_label);
|
||||
kprintf("data start %lx, last allocated %lx\n",
|
||||
vol->data_start, vol->last_allocated);
|
||||
kprintf("last fake vnid %Lx, vnid cache %lx entries @ (%p %p)\n",
|
||||
vol->vcache.cur_vnid, vol->vcache.cache_size,
|
||||
vol->vcache.by_vnid, vol->vcache.by_loc);
|
||||
kprintf("dlist entries: %lx/%lx @ %p\n",
|
||||
vol->dlist.entries, vol->dlist.allocated, vol->dlist.vnid_list);
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
int debug_dvnode(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (argc < 2) {
|
||||
kprintf("dvnode vnode\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
for (i=1;i<argc;i++) {
|
||||
vnode *n = (vnode *)strtoul(argv[i], NULL, 0);
|
||||
if (!n) continue;
|
||||
|
||||
kprintf("vnode @ %p", n);
|
||||
#if TRACK_FILENAME
|
||||
kprintf(" (%s)", n->filename);
|
||||
#endif
|
||||
kprintf("\nmagic %lx, vnid %Lx, dir vnid %Lx\n",
|
||||
n->magic, n->vnid, n->dir_vnid);
|
||||
kprintf("iteration %lx, si=%lx, ei=%lx, cluster=%lx\n",
|
||||
n->iteration, n->sindex, n->eindex, n->cluster);
|
||||
kprintf("mode %lx, size %Lx, time %lx\n",
|
||||
n->mode, n->st_size, n->st_time);
|
||||
kprintf("end cluster = %lx\n", n->end_cluster);
|
||||
if (n->mime) kprintf("mime type %s\n", n->mime);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
int debug_dc2s(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
nspace *vol;
|
||||
|
||||
if (argc < 3) {
|
||||
kprintf("dc2s nspace cluster\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
vol = (nspace *)strtoul(argv[1], NULL, 0);
|
||||
if (vol == NULL)
|
||||
return B_OK;
|
||||
|
||||
for (i=2;i<argc;i++) {
|
||||
uint32 cluster = strtoul(argv[i], NULL, 0);
|
||||
kprintf("cluster %lx = block %Lx\n", cluster, vol->data_start +
|
||||
(off_t)(cluster - 2) * vol->sectors_per_cluster);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int lock_removable_device(int fd, bool state)
|
||||
{
|
||||
return ioctl(fd, B_SCSI_PREVENT_ALLOW, &state, sizeof(state));
|
||||
}
|
||||
|
||||
static status_t mount_fat_disk(const char *path, nspace_id nsid,
|
||||
const int flags, nspace** newVol)
|
||||
{
|
||||
nspace *vol = NULL;
|
||||
uint8 buf[512];
|
||||
int i;
|
||||
device_geometry geo;
|
||||
status_t err;
|
||||
|
||||
*newVol = NULL;
|
||||
if ((vol = (nspace*)calloc(sizeof(nspace), 1)) == NULL) {
|
||||
dprintf("dosfs error: out of memory\n");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
vol->magic = NSPACE_MAGIC;
|
||||
vol->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME;
|
||||
|
||||
// open read-only for now
|
||||
if ((err = (vol->fd = open(path, O_RDONLY))) < 0) {
|
||||
dprintf("dosfs: unable to open %s (%s)\n", path, strerror(err));
|
||||
goto error0;
|
||||
}
|
||||
|
||||
// get device characteristics
|
||||
if (ioctl(vol->fd, B_GET_GEOMETRY, &geo) < 0) {
|
||||
struct stat st;
|
||||
if ((fstat(vol->fd, &st) >= 0) &&
|
||||
S_ISREG(st.st_mode)) {
|
||||
/* support mounting disk images */
|
||||
geo.bytes_per_sector = 0x200;
|
||||
geo.sectors_per_track = 1;
|
||||
geo.cylinder_count = st.st_size / 0x200;
|
||||
geo.head_count = 1;
|
||||
geo.read_only = !(st.st_mode & S_IWUSR);
|
||||
geo.removable = true;
|
||||
} else {
|
||||
dprintf("dosfs: error getting device geometry\n");
|
||||
goto error0;
|
||||
}
|
||||
}
|
||||
if ((geo.bytes_per_sector != 0x200) && (geo.bytes_per_sector != 0x400) && (geo.bytes_per_sector != 0x800)) {
|
||||
dprintf("dosfs: unsupported device block size (%lu)\n", geo.bytes_per_sector);
|
||||
goto error0;
|
||||
}
|
||||
if (geo.removable) {
|
||||
DPRINTF(0, ("%s is removable\n", path));
|
||||
vol->flags |= B_FS_IS_REMOVABLE;
|
||||
}
|
||||
if (geo.read_only || (flags & B_MOUNT_READ_ONLY)) {
|
||||
DPRINTF(0, ("%s is read-only\n", path));
|
||||
vol->flags |= B_FS_IS_READONLY;
|
||||
} else {
|
||||
// reopen it with read/write permissions
|
||||
close(vol->fd);
|
||||
if ((err = (vol->fd = open(path, O_RDWR))) < 0) {
|
||||
dprintf("dosfs: unable to open %s (%s)\n", path, strerror(err));
|
||||
goto error0;
|
||||
}
|
||||
if (vol->flags & B_FS_IS_REMOVABLE)
|
||||
lock_removable_device(vol->fd, true);
|
||||
}
|
||||
|
||||
// read in the boot sector
|
||||
if ((err = read_pos(vol->fd, 0, (void*)buf, 512)) != 512) {
|
||||
dprintf("dosfs: error reading boot sector\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// only check boot signature on hard disks to account for broken mtools
|
||||
// behavior
|
||||
if (((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) && (buf[0x15] == 0xf8))
|
||||
goto error;
|
||||
|
||||
if (!memcmp(buf+3, "NTFS ", 8) || !memcmp(buf+3, "HPFS ", 8)) {
|
||||
dprintf("%4.4s, not FAT\n", buf+3);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// first fill in the universal fields from the bpb
|
||||
vol->bytes_per_sector = read16(buf,0xb);
|
||||
if ((vol->bytes_per_sector != 0x200) && (vol->bytes_per_sector != 0x400) && (vol->bytes_per_sector != 0x800)) {
|
||||
dprintf("dosfs error: unsupported bytes per sector (%lu)\n",
|
||||
vol->bytes_per_sector);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vol->sectors_per_cluster = i = buf[0xd];
|
||||
if ((i != 1) && (i != 2) && (i != 4) && (i != 8) &&
|
||||
(i != 0x10) && (i != 0x20) && (i != 0x40) && (i != 0x80)) {
|
||||
dprintf("dosfs: sectors/cluster = %d\n", i);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vol->reserved_sectors = read16(buf,0xe);
|
||||
|
||||
vol->fat_count = buf[0x10];
|
||||
if ((vol->fat_count == 0) || (vol->fat_count > 8)) {
|
||||
dprintf("dosfs: unreasonable fat count (%lu)\n", vol->fat_count);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vol->media_descriptor = buf[0x15];
|
||||
// check media descriptor versus known types
|
||||
if ((buf[0x15] != 0xF0) && (buf[0x15] < 0xf8)) {
|
||||
dprintf("dosfs error: invalid media descriptor byte\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
vol->vol_entry = -2; // for now, assume there is no volume entry
|
||||
memset(vol->vol_label, ' ', 11);
|
||||
|
||||
// now become more discerning citizens
|
||||
vol->sectors_per_fat = read16(buf,0x16);
|
||||
if (vol->sectors_per_fat == 0) {
|
||||
// fat32 land
|
||||
vol->fat_bits = 32;
|
||||
vol->sectors_per_fat = read32(buf,0x24);
|
||||
vol->total_sectors = read32(buf,0x20);
|
||||
|
||||
vol->fsinfo_sector = read16(buf, 0x30);
|
||||
if ((vol->fsinfo_sector != 0xffff) && (vol->fsinfo_sector >= vol->reserved_sectors)) {
|
||||
dprintf("dosfs: fsinfo sector too large (%x)\n", vol->fsinfo_sector);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vol->fat_mirrored = !(buf[0x28] & 0x80);
|
||||
vol->active_fat = (vol->fat_mirrored) ? (buf[0x28] & 0xf) : 0;
|
||||
|
||||
vol->data_start = vol->reserved_sectors + vol->fat_count*vol->sectors_per_fat;
|
||||
vol->total_clusters = (vol->total_sectors - vol->data_start) / vol->sectors_per_cluster;
|
||||
|
||||
vol->root_vnode.cluster = read32(buf,0x2c);
|
||||
if (vol->root_vnode.cluster >= vol->total_clusters) {
|
||||
dprintf("dosfs: root vnode cluster too large (%lx)\n", vol->root_vnode.cluster);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
// fat12 & fat16
|
||||
if (vol->fat_count != 2) {
|
||||
dprintf("dosfs error: claims %ld fat tables\n", vol->fat_count);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vol->root_entries_count = read16(buf,0x11);
|
||||
if (vol->root_entries_count % (vol->bytes_per_sector / 0x20)) {
|
||||
dprintf("dosfs error: invalid number of root entries\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
vol->fsinfo_sector = 0xffff;
|
||||
vol->total_sectors = read16(buf,0x13); // partition size
|
||||
if (vol->total_sectors == 0)
|
||||
vol->total_sectors = read32(buf,0x20);
|
||||
|
||||
{
|
||||
/*
|
||||
Zip disks that were formatted at iomega have an incorrect number
|
||||
of sectors. They say that they have 196576 sectors but they
|
||||
really only have 196192. This check is a work-around for their
|
||||
brain-deadness.
|
||||
*/
|
||||
unsigned char bogus_zip_data[] = {
|
||||
0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,
|
||||
0xf8, 0xc0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00
|
||||
};
|
||||
|
||||
if (memcmp(buf+0x0b, bogus_zip_data, sizeof(bogus_zip_data)) == 0 &&
|
||||
vol->total_sectors == 196576 &&
|
||||
((off_t)geo.sectors_per_track *
|
||||
(off_t)geo.cylinder_count *
|
||||
(off_t)geo.head_count) == 196192) {
|
||||
|
||||
vol->total_sectors = 196192;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (buf[0x26] == 0x29) {
|
||||
// fill in the volume label
|
||||
if (memcmp(buf+0x2b, " ", 11)) {
|
||||
memcpy(vol->vol_label, buf+0x2b, 11);
|
||||
vol->vol_entry = -1;
|
||||
}
|
||||
}
|
||||
|
||||
vol->fat_mirrored = true;
|
||||
vol->active_fat = 0;
|
||||
|
||||
vol->root_start = vol->reserved_sectors + vol->fat_count * vol->sectors_per_fat;
|
||||
vol->root_sectors = vol->root_entries_count * 0x20 / vol->bytes_per_sector;
|
||||
vol->root_vnode.cluster = 1;
|
||||
vol->root_vnode.end_cluster = 1;
|
||||
vol->root_vnode.st_size = vol->root_sectors * vol->bytes_per_sector;
|
||||
|
||||
vol->data_start = vol->root_start + vol->root_sectors;
|
||||
vol->total_clusters = (vol->total_sectors - vol->data_start) / vol->sectors_per_cluster;
|
||||
|
||||
// XXX: uncertain about border cases; win32 sdk says cutoffs are at
|
||||
// at ff6/ff7 (or fff6/fff7), but that doesn't make much sense
|
||||
if (vol->total_clusters > 0xff1)
|
||||
vol->fat_bits = 16;
|
||||
else
|
||||
vol->fat_bits = 12;
|
||||
}
|
||||
|
||||
/* check that the partition is large enough to contain the file system */
|
||||
if (vol->total_sectors > geo.sectors_per_track * geo.cylinder_count *
|
||||
geo.head_count) {
|
||||
dprintf("dosfs: volume extends past end of partition\n");
|
||||
err = B_PARTITION_TOO_SMALL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
// perform sanity checks on the FAT
|
||||
|
||||
// the media descriptor in active FAT should match the one in the BPB
|
||||
if ((err = read_pos(vol->fd, vol->bytes_per_sector*(vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat), (void *)buf, 0x200)) != 0x200) {
|
||||
dprintf("dosfs: error reading FAT\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (buf[0] != vol->media_descriptor) {
|
||||
dprintf("dosfs error: media descriptor mismatch (%x != %x)\n", buf[0], vol->media_descriptor);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (vol->fat_mirrored) {
|
||||
uint32 i;
|
||||
uint8 buf2[512];
|
||||
for (i=0;i<vol->fat_count;i++) {
|
||||
if (i != vol->active_fat) {
|
||||
DPRINTF(1, ("checking fat #%ld\n", i));
|
||||
buf2[0] = ~buf[0];
|
||||
if ((err = read_pos(vol->fd, vol->bytes_per_sector*(vol->reserved_sectors + vol->sectors_per_fat*i), (void *)buf2, 0x200)) != 0x200) {
|
||||
dprintf("dosfs: error reading FAT %ld\n", i);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (buf2[0] != vol->media_descriptor) {
|
||||
dprintf("dosfs error: media descriptor mismatch in fat # %ld (%x != %x)\n", i, buf2[0], vol->media_descriptor);
|
||||
goto error;
|
||||
}
|
||||
#if 0
|
||||
// checking for exact matches of fats is too
|
||||
// restrictive; allow these to go through in
|
||||
// case the fat is corrupted for some reason
|
||||
if (memcmp(buf, buf2, 0x200)) {
|
||||
dprintf("dosfs error: fat %d doesn't match active fat (%d)\n", i, vol->active_fat);
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now we are convinced of the drive's validity
|
||||
|
||||
vol->id = nsid;
|
||||
strncpy(vol->device,path,256);
|
||||
|
||||
// XXX: if fsinfo exists, read from there?
|
||||
vol->last_allocated = 2;
|
||||
|
||||
vol->beos_vnid = INVALID_VNID_BITS_MASK;
|
||||
{
|
||||
void *handle;
|
||||
handle = load_driver_settings("dos");
|
||||
vol->respect_disk_image =
|
||||
get_driver_boolean_parameter(handle, "respect", true, true);
|
||||
unload_driver_settings(handle);
|
||||
}
|
||||
|
||||
// initialize block cache
|
||||
if (init_cache_for_device(vol->fd, (off_t)vol->total_sectors) < 0) {
|
||||
dprintf("error initializing block cache\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// as well as the vnode cache
|
||||
if (init_vcache(vol) != B_OK) {
|
||||
dprintf("error initializing vnode cache\n");
|
||||
goto error1;
|
||||
}
|
||||
|
||||
// and the dlist cache
|
||||
if (dlist_init(vol) != B_OK) {
|
||||
dprintf("error initializing dlist cache\n");
|
||||
goto error2;
|
||||
}
|
||||
|
||||
if (vol->flags & B_FS_IS_READONLY)
|
||||
vol->free_clusters = 0;
|
||||
else {
|
||||
if ((err = count_free_clusters(vol)) < 0) {
|
||||
dprintf("error counting free clusters (%s)\n", strerror(err));
|
||||
goto error3;
|
||||
}
|
||||
vol->free_clusters = err;
|
||||
}
|
||||
|
||||
DPRINTF(0, ("built at %s on %s\n", build_time, build_date));
|
||||
DPRINTF(0, ("mounting %s (id %lx, device %x, media descriptor %x)\n", vol->device, vol->id, vol->fd, vol->media_descriptor));
|
||||
DPRINTF(0, ("%lx bytes/sector, %lx sectors/cluster\n", vol->bytes_per_sector, vol->sectors_per_cluster));
|
||||
DPRINTF(0, ("%lx reserved sectors, %lx total sectors\n", vol->reserved_sectors, vol->total_sectors));
|
||||
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));
|
||||
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));
|
||||
DPRINTF(0, ("%lx total clusters, %lx free\n", vol->total_clusters, vol->free_clusters));
|
||||
DPRINTF(0, ("fat mirroring is %s, fs info sector at sector %x\n", (vol->fat_mirrored) ? "on" : "off", vol->fsinfo_sector));
|
||||
DPRINTF(0, ("last allocated cluster = %lx\n", vol->last_allocated));
|
||||
|
||||
if (vol->fat_bits == 32) {
|
||||
// now that the block cache has been initialised, we can figure
|
||||
// out the length of the root directory with count_clusters()
|
||||
vol->root_vnode.st_size = count_clusters(vol, vol->root_vnode.cluster) * vol->bytes_per_sector * vol->sectors_per_cluster;
|
||||
vol->root_vnode.end_cluster = get_nth_fat_entry(vol, vol->root_vnode.cluster, vol->root_vnode.st_size / vol->bytes_per_sector / vol->sectors_per_cluster - 1);
|
||||
}
|
||||
|
||||
// initialize root vnode
|
||||
vol->root_vnode.magic = VNODE_MAGIC;
|
||||
vol->root_vnode.vnid = vol->root_vnode.dir_vnid = GENERATE_DIR_CLUSTER_VNID(vol->root_vnode.cluster,vol->root_vnode.cluster);
|
||||
vol->root_vnode.sindex = vol->root_vnode.eindex = 0xffffffff;
|
||||
vol->root_vnode.mode = FAT_SUBDIR;
|
||||
time(&(vol->root_vnode.st_time));
|
||||
vol->root_vnode.mime = NULL;
|
||||
dlist_add(vol, vol->root_vnode.vnid);
|
||||
|
||||
// find volume label (supercedes any label in the bpb)
|
||||
{
|
||||
struct diri diri;
|
||||
uint8 *buffer;
|
||||
buffer = diri_init(vol, vol->root_vnode.cluster, 0, &diri);
|
||||
for (;buffer;buffer=diri_next_entry(&diri)) {
|
||||
if ((buffer[0x0b] & FAT_VOLUME) && (buffer[0x0b] != 0xf) && (buffer[0] != 0xe5)) {
|
||||
vol->vol_entry = diri.current_index;
|
||||
memcpy(vol->vol_label, buffer, 11);
|
||||
break;
|
||||
}
|
||||
}
|
||||
diri_free(&diri);
|
||||
}
|
||||
|
||||
DPRINTF(0, ("root vnode id = %Lx\n", vol->root_vnode.vnid));
|
||||
DPRINTF(0, ("volume label [%11.11s] (%lx)\n", vol->vol_label, vol->vol_entry));
|
||||
|
||||
// steal a trick from bfs
|
||||
if (!memcmp(vol->vol_label, "__RO__ ", 11)) {
|
||||
vol->flags |= B_FS_IS_READONLY;
|
||||
}
|
||||
|
||||
*newVol = vol;
|
||||
return B_NO_ERROR;
|
||||
|
||||
error3:
|
||||
dlist_uninit(vol);
|
||||
error2:
|
||||
uninit_vcache(vol);
|
||||
error1:
|
||||
remove_cached_device_blocks(vol->fd, NO_WRITES);
|
||||
error:
|
||||
if (!(vol->flags & B_FS_IS_READONLY) && (vol->flags & B_FS_IS_REMOVABLE))
|
||||
lock_removable_device(vol->fd, false);
|
||||
error0:
|
||||
close(vol->fd);
|
||||
free(vol);
|
||||
return (err >= B_NO_ERROR) ? EINVAL : err;
|
||||
}
|
||||
|
||||
static int dosfs_mount(nspace_id nsid, const char *device, ulong flags, void *parms,
|
||||
size_t len, void **data, vnode_id *vnid)
|
||||
{
|
||||
int result;
|
||||
nspace *vol;
|
||||
void *handle;
|
||||
|
||||
handle = load_driver_settings("dos");
|
||||
debug_attr = strtoul(get_driver_parameter(handle, "debug_attr", "0", "0"), NULL, 0);
|
||||
debug_dir = strtoul(get_driver_parameter(handle, "debug_dir", "0", "0"), NULL, 0);
|
||||
debug_dlist = strtoul(get_driver_parameter(handle, "debug_dlist", "0", "0"), NULL, 0);
|
||||
debug_dosfs = strtoul(get_driver_parameter(handle, "debug_dosfs", "0", "0"), NULL, 0);
|
||||
debug_encodings = strtoul(get_driver_parameter(handle, "debug_encodings", "0", "0"), NULL, 0);
|
||||
debug_fat = strtoul(get_driver_parameter(handle, "debug_fat", "0", "0"), NULL, 0);
|
||||
debug_file = strtoul(get_driver_parameter(handle, "debug_file", "0", "0"), NULL, 0);
|
||||
debug_iter = strtoul(get_driver_parameter(handle, "debug_iter", "0", "0"), NULL, 0);
|
||||
debug_vcache = strtoul(get_driver_parameter(handle, "debug_vcache", "0", "0"), NULL, 0);
|
||||
unload_driver_settings(handle);
|
||||
|
||||
/* parms and len are command line options; dosfs doesn't use any so
|
||||
we can ignore these arguments */
|
||||
TOUCH(parms); TOUCH(len);
|
||||
|
||||
#if __RO__
|
||||
// make it read-only
|
||||
flags |= 1;
|
||||
#endif
|
||||
|
||||
if (data == NULL) {
|
||||
dprintf("dosfs_mount passed NULL data pointer\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
// Try and mount volume as a FAT volume
|
||||
if ((result = mount_fat_disk(device, nsid, flags, &vol)) == B_NO_ERROR) {
|
||||
char name[32];
|
||||
|
||||
if (check_nspace_magic(vol, "dosfs_mount")) return EINVAL;
|
||||
|
||||
*vnid = vol->root_vnode.vnid;
|
||||
*data = (void*)vol;
|
||||
|
||||
// You MUST do this. Create the vnode for the root.
|
||||
result = new_vnode(nsid, *vnid, (void*)&(vol->root_vnode));
|
||||
if (result != B_NO_ERROR) {
|
||||
dprintf("error creating new vnode (%s)\n", strerror(result));
|
||||
goto error;
|
||||
}
|
||||
sprintf(name, "fat lock %lx", vol->id);
|
||||
if ((result = new_lock(&(vol->vlock), name)) != 0) {
|
||||
dprintf("error creating lock (%s)\n", strerror(result));
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
load_driver_symbols("dos");
|
||||
|
||||
if (atomic_add(&instances, 1) == 0) {
|
||||
add_debugger_command("dos", debug_dos, "dump a dos nspace structure");
|
||||
add_debugger_command("dvnode", debug_dvnode, "dump a dos vnode structure");
|
||||
add_debugger_command("dfvnid", debug_dfvnid, "find a vnid in the vnid cache");
|
||||
add_debugger_command("dfloc", debug_dfloc, "find a loc in the vnid cache");
|
||||
add_debugger_command("dc2s", debug_dc2s, "calculate sector for cluster");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
error:
|
||||
remove_cached_device_blocks(vol->fd, NO_WRITES);
|
||||
dlist_uninit(vol);
|
||||
uninit_vcache(vol);
|
||||
free(vol);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void update_fsinfo(nspace *vol)
|
||||
{
|
||||
if ((vol->fat_bits == 32) && (vol->fsinfo_sector != 0xffff) &&
|
||||
((vol->flags & B_FS_IS_READONLY) == false)) {
|
||||
uchar *buffer;
|
||||
if ((buffer = (uchar *)get_block(vol->fd, vol->fsinfo_sector, vol->bytes_per_sector)) != NULL) {
|
||||
if ((read32(buffer,0) == 0x41615252) && (read32(buffer,0x1e4) == 0x61417272) && (read16(buffer,0x1fe) == 0xaa55)) {
|
||||
buffer[0x1e8] = (vol->free_clusters & 0xff);
|
||||
buffer[0x1e9] = ((vol->free_clusters >> 8) & 0xff);
|
||||
buffer[0x1ea] = ((vol->free_clusters >> 16) & 0xff);
|
||||
buffer[0x1eb] = ((vol->free_clusters >> 24) & 0xff);
|
||||
mark_blocks_dirty(vol->fd, vol->fsinfo_sector, 1);
|
||||
|
||||
// should also store next free cluster
|
||||
} else {
|
||||
dprintf("update_fsinfo: fsinfo block has invalid magic number\n");
|
||||
}
|
||||
release_block(vol->fd, vol->fsinfo_sector);
|
||||
} else {
|
||||
dprintf("update_fsinfo: error getting fsinfo sector %x\n", vol->fsinfo_sector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dosfs_unmount(void *_vol)
|
||||
{
|
||||
int result = B_NO_ERROR;
|
||||
|
||||
nspace* vol = (nspace*)_vol;
|
||||
|
||||
LOCK_VOL(vol);
|
||||
|
||||
if (check_nspace_magic(vol, "dosfs_unmount")) {
|
||||
UNLOCK_VOL(vol);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
DPRINTF(0, ("dosfs_unmount volume %lx\n", vol->id));
|
||||
|
||||
update_fsinfo(vol);
|
||||
flush_device(vol->fd, 0);
|
||||
remove_cached_device_blocks(vol->fd, ALLOW_WRITES);
|
||||
|
||||
#if DEBUG
|
||||
if (atomic_add(&instances, -1) == 1) {
|
||||
remove_debugger_command("dos", debug_dos);
|
||||
remove_debugger_command("dvnode", debug_dvnode);
|
||||
remove_debugger_command("dfvnid", debug_dfvnid);
|
||||
remove_debugger_command("dfloc", debug_dfloc);
|
||||
remove_debugger_command("dc2s", debug_dc2s);
|
||||
}
|
||||
#endif
|
||||
|
||||
dlist_uninit(vol);
|
||||
uninit_vcache(vol);
|
||||
|
||||
if (!(vol->flags & B_FS_IS_READONLY) && (vol->flags & B_FS_IS_REMOVABLE))
|
||||
lock_removable_device(vol->fd, false);
|
||||
result = close(vol->fd);
|
||||
free_lock(&(vol->vlock));
|
||||
vol->magic = ~VNODE_MAGIC;
|
||||
free(vol);
|
||||
|
||||
#if USE_DMALLOC
|
||||
check_mem();
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// dosfs_rfsstat - Fill in fs_info struct for device.
|
||||
static int dosfs_rfsstat(void *_vol, struct fs_info * fss)
|
||||
{
|
||||
nspace* vol = (nspace*)_vol;
|
||||
int i;
|
||||
|
||||
LOCK_VOL(vol);
|
||||
|
||||
if (check_nspace_magic(vol, "dosfs_rfsstat")) {
|
||||
UNLOCK_VOL(vol);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
DPRINTF(1, ("dosfs_rfsstat called\n"));
|
||||
|
||||
// fss->dev and fss->root filled in by kernel
|
||||
|
||||
// File system flags.
|
||||
fss->flags = vol->flags;
|
||||
|
||||
// FS block size.
|
||||
fss->block_size = vol->bytes_per_sector * vol->sectors_per_cluster;
|
||||
|
||||
// IO size - specifies buffer size for file copying
|
||||
fss->io_size = 65536;
|
||||
|
||||
// Total blocks
|
||||
fss->total_blocks = vol->total_clusters;
|
||||
|
||||
// Free blocks
|
||||
fss->free_blocks = vol->free_clusters;
|
||||
|
||||
// Device name.
|
||||
strncpy(fss->device_name, vol->device, sizeof(fss->device_name));
|
||||
|
||||
if (vol->vol_entry > -2)
|
||||
strncpy(fss->volume_name, vol->vol_label, sizeof(fss->volume_name));
|
||||
else
|
||||
strcpy(fss->volume_name, "no name ");
|
||||
|
||||
// XXX: should sanitize name as well
|
||||
for (i=10;i>0;i--)
|
||||
if (fss->volume_name[i] != ' ')
|
||||
break;
|
||||
fss->volume_name[i+1] = 0;
|
||||
for (;i>=0;i--) {
|
||||
if ((fss->volume_name[i] >= 'A') && (fss->volume_name[i] <= 'Z'))
|
||||
fss->volume_name[i] += 'a' - 'A';
|
||||
}
|
||||
|
||||
// File system name
|
||||
strcpy(fss->fsh_name, "fat");
|
||||
|
||||
UNLOCK_VOL(vol);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dosfs_wfsstat(void *_vol, struct fs_info * fss, long mask)
|
||||
{
|
||||
int result = B_ERROR;
|
||||
nspace* vol = (nspace*)_vol;
|
||||
|
||||
LOCK_VOL(vol);
|
||||
|
||||
if (check_nspace_magic(vol, "dosfs_wfsstat")) {
|
||||
UNLOCK_VOL(vol);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
DPRINTF(0, ("dosfs_wfsstat called\n"));
|
||||
|
||||
/* if it's a r/o file system and not the special hack, then don't allow
|
||||
* volume renaming */
|
||||
if ((vol->flags & B_FS_IS_READONLY) && memcmp(vol->vol_label, "__RO__ ", 11)) {
|
||||
UNLOCK_VOL(vol);
|
||||
return EROFS;
|
||||
}
|
||||
|
||||
if (mask & WFSSTAT_NAME) {
|
||||
// sanitize name
|
||||
char name[11];
|
||||
int i,j;
|
||||
memset(name, ' ', 11);
|
||||
DPRINTF(1, ("wfsstat: setting name to %s\n", fss->volume_name));
|
||||
for (i=j=0;(i<11)&&(fss->volume_name[j]);j++) {
|
||||
static char acceptable[] = "!#$%&'()-0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`{}~";
|
||||
char c = fss->volume_name[j];
|
||||
if ((c >= 'a') && (c <= 'z')) c += 'A' - 'a';
|
||||
// spaces acceptable in volume names
|
||||
if (strchr(acceptable, c) || (c == ' '))
|
||||
name[i++] = c;
|
||||
}
|
||||
if (i == 0) { // bad name, kiddo
|
||||
result = EINVAL;
|
||||
goto bi;
|
||||
}
|
||||
DPRINTF(1, ("wfsstat: sanitized to [%11.11s]\n", name));
|
||||
|
||||
if (vol->vol_entry == -1) {
|
||||
// stored in the bpb
|
||||
uchar *buffer;
|
||||
if ((buffer = get_block(vol->fd, 0, vol->bytes_per_sector)) == NULL) {
|
||||
result = EIO;
|
||||
goto bi;
|
||||
}
|
||||
if ((buffer[0x26] != 0x29) || memcmp(buffer + 0x2b, vol->vol_label, 11)) {
|
||||
dprintf("dosfs_wfsstat: label mismatch\n");
|
||||
result = B_ERROR;
|
||||
} else {
|
||||
memcpy(buffer + 0x2b, name, 11);
|
||||
mark_blocks_dirty(vol->fd, 0, 1);
|
||||
result = 0;
|
||||
}
|
||||
release_block(vol->fd, 0);
|
||||
} else if (vol->vol_entry >= 0) {
|
||||
struct diri diri;
|
||||
uint8 *buffer;
|
||||
buffer = diri_init(vol, vol->root_vnode.cluster, vol->vol_entry, &diri);
|
||||
|
||||
// check if it is the same as the old volume label
|
||||
if ((buffer == NULL) || (memcmp(buffer, vol->vol_label, 11))) {
|
||||
dprintf("dosfs_wfsstat: label mismatch\n");
|
||||
diri_free(&diri);
|
||||
result = B_ERROR;
|
||||
goto bi;
|
||||
}
|
||||
memcpy(buffer, name, 11);
|
||||
diri_mark_dirty(&diri);
|
||||
diri_free(&diri);
|
||||
result = 0;
|
||||
} else {
|
||||
uint32 index;
|
||||
result = create_volume_label(vol, name, &index);
|
||||
if (result == B_OK) vol->vol_entry = index;
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
memcpy(vol->vol_label, name, 11);
|
||||
}
|
||||
|
||||
bi: UNLOCK_VOL(vol);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int dosfs_ioctl(void *_vol, void *_node, void *cookie, int code,
|
||||
void *buf, size_t len)
|
||||
{
|
||||
status_t result = B_OK;
|
||||
nspace *vol = (nspace *)_vol;
|
||||
vnode *node = (vnode *)_node;
|
||||
|
||||
TOUCH(cookie); TOUCH(buf); TOUCH(len);
|
||||
|
||||
LOCK_VOL(vol);
|
||||
|
||||
if (check_nspace_magic(vol, "dosfs_ioctl") ||
|
||||
check_vnode_magic(node, "dosfs_ioctl")) {
|
||||
UNLOCK_VOL(vol);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case 10002 : /* return real creation time */
|
||||
if (buf) *(bigtime_t *)buf = node->st_time;
|
||||
break;
|
||||
case 10003 : /* return real last modification time */
|
||||
if (buf) *(bigtime_t *)buf = node->st_time;
|
||||
break;
|
||||
|
||||
case 69666 :
|
||||
result = fragment(vol, buf);
|
||||
break;
|
||||
|
||||
case 100000 :
|
||||
dprintf("built at %s on %s\n", build_time, build_date);
|
||||
dprintf("vol info: %s (device %x, media descriptor %x)\n", vol->device, vol->fd, vol->media_descriptor);
|
||||
dprintf("%lx bytes/sector, %lx sectors/cluster\n", vol->bytes_per_sector, vol->sectors_per_cluster);
|
||||
dprintf("%lx reserved sectors, %lx total sectors\n", vol->reserved_sectors, vol->total_sectors);
|
||||
dprintf("%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);
|
||||
dprintf("root directory starts at sector %lx (cluster %lx), data at sector %lx\n", vol->root_start, vol->root_vnode.cluster, vol->data_start);
|
||||
dprintf("%lx total clusters, %lx free\n", vol->total_clusters, vol->free_clusters);
|
||||
dprintf("fat mirroring is %s, fs info sector at sector %x\n", (vol->fat_mirrored) ? "on" : "off", vol->fsinfo_sector);
|
||||
dprintf("last allocated cluster = %lx\n", vol->last_allocated);
|
||||
dprintf("root vnode id = %Lx\n", vol->root_vnode.vnid);
|
||||
dprintf("volume label [%11.11s]\n", vol->vol_label);
|
||||
break;
|
||||
|
||||
case 100001 :
|
||||
dprintf("vnode id %Lx, dir vnid = %Lx\n", node->vnid, node->dir_vnid);
|
||||
dprintf("si = %lx, ei = %lx\n", node->sindex, node->eindex);
|
||||
dprintf("cluster = %lx (%lx), mode = %lx, size = %Lx\n", node->cluster, vol->data_start + vol->sectors_per_cluster * (node->cluster - 2), node->mode, node->st_size);
|
||||
dprintf("mime = %s\n", node->mime ? node->mime : "(null)");
|
||||
dump_fat_chain(vol, node->cluster);
|
||||
break;
|
||||
|
||||
case 100002 :
|
||||
{struct diri diri;
|
||||
uint8 *buffer;
|
||||
uint32 i;
|
||||
for (i=0,buffer=diri_init(vol,node->cluster, 0, &diri);buffer;buffer=diri_next_entry(&diri),i++) {
|
||||
if (buffer[0] == 0) break;
|
||||
dprintf("entry %lx:\n", i);
|
||||
dump_directory(buffer);
|
||||
}
|
||||
diri_free(&diri);}
|
||||
break;
|
||||
|
||||
case 100003 :
|
||||
dprintf("vcache validation not yet implemented\n");
|
||||
#if 0
|
||||
dprintf("validating vcache for %lx\n", vol->id);
|
||||
validate_vcache(vol);
|
||||
dprintf("validation complete for %lx\n", vol->id);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 100004 :
|
||||
dprintf("dumping vcache for %lx\n", vol->id);
|
||||
dump_vcache(vol);
|
||||
break;
|
||||
|
||||
case 100005 :
|
||||
dprintf("dumping dlist for %lx\n", vol->id);
|
||||
dlist_dump(vol);
|
||||
break;
|
||||
|
||||
default :
|
||||
dprintf("dosfs_ioctl: vol %lx, vnode %Lx code = %d\n", vol->id, node->vnid, code);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
UNLOCK_VOL(vol);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int dosfs_sync(void *_vol)
|
||||
{
|
||||
nspace *vol = (nspace *)_vol;
|
||||
|
||||
LOCK_VOL(vol);
|
||||
|
||||
if (check_nspace_magic((nspace*)_vol, "dosfs_sync")) {
|
||||
UNLOCK_VOL(vol);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
DPRINTF(0, ("dosfs_sync called on volume %lx\n", vol->id));
|
||||
|
||||
update_fsinfo(vol);
|
||||
flush_device(vol->fd, 0);
|
||||
|
||||
UNLOCK_VOL(vol);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dosfs_fsync(void *vol, void *node)
|
||||
{
|
||||
TOUCH(node);
|
||||
|
||||
return dosfs_sync(vol);
|
||||
}
|
||||
|
||||
vnode_ops fs_entry = {
|
||||
&dosfs_read_vnode,
|
||||
&dosfs_write_vnode,
|
||||
&dosfs_remove_vnode,
|
||||
NULL,
|
||||
&dosfs_walk,
|
||||
&dosfs_access,
|
||||
&dosfs_create,
|
||||
&dosfs_mkdir,
|
||||
NULL,
|
||||
NULL,
|
||||
&dosfs_rename,
|
||||
&dosfs_unlink,
|
||||
&dosfs_rmdir,
|
||||
&dosfs_readlink,
|
||||
&dosfs_opendir,
|
||||
&dosfs_closedir,
|
||||
&dosfs_free_dircookie,
|
||||
&dosfs_rewinddir,
|
||||
&dosfs_readdir,
|
||||
&dosfs_open,
|
||||
&dosfs_close,
|
||||
&dosfs_free_cookie,
|
||||
&dosfs_read,
|
||||
&dosfs_write,
|
||||
NULL,
|
||||
NULL,
|
||||
&dosfs_ioctl,
|
||||
NULL,
|
||||
&dosfs_rstat,
|
||||
&dosfs_wstat,
|
||||
&dosfs_fsync,
|
||||
NULL,
|
||||
&dosfs_mount,
|
||||
&dosfs_unmount,
|
||||
&dosfs_sync,
|
||||
&dosfs_rfsstat,
|
||||
&dosfs_wfsstat,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&dosfs_open_attrdir,
|
||||
&dosfs_close_attrdir,
|
||||
&dosfs_free_attrcookie,
|
||||
&dosfs_rewind_attrdir,
|
||||
&dosfs_read_attrdir,
|
||||
&dosfs_write_attr,
|
||||
&dosfs_read_attr,
|
||||
NULL,
|
||||
NULL,
|
||||
&dosfs_stat_attr,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
int32 api_version = B_CUR_FS_API_VERSION;
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
#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;
|
||||
|
||||
lock vlock;
|
||||
|
||||
// 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 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;
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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_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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
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
@@ -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_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
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
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 void notify_select_event(selectsync *sync, uint32 ref);
|
||||
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
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
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));
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
|
||||
struct ext_mime {
|
||||
char *extension;
|
||||
char *mime;
|
||||
};
|
||||
|
||||
struct ext_mime mimes[] = {
|
||||
{ "gz", "application/x-gzip" },
|
||||
{ "hqx", "application/x-binhex40" },
|
||||
{ "lha", "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" },
|
||||
|
||||
{ "aif", "audio/x-aiff" },
|
||||
{ "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" },
|
||||
{ "mp3", "audio/x-mpeg" },
|
||||
|
||||
{ "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" },
|
||||
{ "doc", "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" },
|
||||
{ "hh", "text/x-source-code" },
|
||||
{ "cxx", "text/x-source-code" },
|
||||
{ "cpp", "text/x-source-code" },
|
||||
{ "S", "text/x-source-code" },
|
||||
{ "java", "text/x-source-code" },
|
||||
|
||||
{ "avi", "video/x-msvideo" },
|
||||
{ "mov", "video/quicktime" },
|
||||
{ "mpg", "video/mpeg" },
|
||||
{ "mpeg", "video/mpeg" },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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
|
||||
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
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
|
||||
@@ -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_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
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
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__;
|
||||
Reference in New Issue
Block a user