2002-07-09 16:24:59 +04:00
|
|
|
/*
|
2005-01-12 02:26:00 +03:00
|
|
|
* Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*
|
|
|
|
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
|
|
|
* Distributed under the terms of the NewOS License.
|
|
|
|
*/
|
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
#include "IOScheduler.h"
|
|
|
|
|
2004-06-07 05:18:56 +04:00
|
|
|
#include <KernelExport.h>
|
|
|
|
#include <Drivers.h>
|
|
|
|
#include <pnp_devfs.h>
|
2004-09-15 04:44:57 +04:00
|
|
|
#include <NodeMonitor.h>
|
2004-06-07 05:18:56 +04:00
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
#include <kdevice_manager.h>
|
2004-10-29 05:58:08 +04:00
|
|
|
#include <KPath.h>
|
2002-07-09 16:24:59 +04:00
|
|
|
#include <vfs.h>
|
|
|
|
#include <debug.h>
|
|
|
|
#include <khash.h>
|
2002-10-30 02:07:06 +03:00
|
|
|
#include <malloc.h>
|
2002-07-09 16:24:59 +04:00
|
|
|
#include <lock.h>
|
|
|
|
#include <vm.h>
|
|
|
|
|
|
|
|
#include <arch/cpu.h>
|
|
|
|
#include <devfs.h>
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
2004-06-07 05:18:56 +04:00
|
|
|
#include <sys/stat.h>
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
|
2004-06-07 05:18:56 +04:00
|
|
|
//#define TRACE_DEVFS
|
|
|
|
#ifdef TRACE_DEVFS
|
2002-07-17 11:55:51 +04:00
|
|
|
# define TRACE(x) dprintf x
|
2002-07-09 16:24:59 +04:00
|
|
|
#else
|
2002-07-17 11:55:51 +04:00
|
|
|
# define TRACE(x)
|
2002-07-09 16:24:59 +04:00
|
|
|
#endif
|
|
|
|
|
2002-07-28 22:41:07 +04:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
struct devfs_part_map {
|
2004-10-27 19:21:11 +04:00
|
|
|
struct devfs_vnode *raw_device;
|
|
|
|
partition_info info;
|
2002-07-09 16:24:59 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct devfs_stream {
|
2004-09-15 04:44:57 +04:00
|
|
|
mode_t type;
|
2002-07-09 16:24:59 +04:00
|
|
|
union {
|
|
|
|
struct stream_dir {
|
|
|
|
struct devfs_vnode *dir_head;
|
|
|
|
struct devfs_cookie *jar_head;
|
|
|
|
} dir;
|
|
|
|
struct stream_dev {
|
2005-01-12 02:26:00 +03:00
|
|
|
device_node_info *node;
|
2004-09-04 21:46:10 +04:00
|
|
|
pnp_devfs_driver_info *info;
|
2002-09-25 20:34:33 +04:00
|
|
|
device_hooks *ops;
|
2002-07-09 16:24:59 +04:00
|
|
|
struct devfs_part_map *part_map;
|
2004-08-28 17:33:31 +04:00
|
|
|
IOScheduler *scheduler;
|
2002-07-09 16:24:59 +04:00
|
|
|
} dev;
|
2002-07-28 22:41:07 +04:00
|
|
|
struct stream_symlink {
|
2004-10-29 05:58:08 +04:00
|
|
|
const char *path;
|
|
|
|
size_t length;
|
2002-07-28 22:41:07 +04:00
|
|
|
} symlink;
|
2002-07-09 16:24:59 +04:00
|
|
|
} u;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct devfs_vnode {
|
|
|
|
struct devfs_vnode *all_next;
|
2004-09-15 04:44:57 +04:00
|
|
|
vnode_id id;
|
|
|
|
char *name;
|
|
|
|
time_t modification_time;
|
|
|
|
time_t creation_time;
|
|
|
|
uid_t uid;
|
|
|
|
gid_t gid;
|
2002-07-09 16:24:59 +04:00
|
|
|
struct devfs_vnode *parent;
|
|
|
|
struct devfs_vnode *dir_next;
|
|
|
|
struct devfs_stream stream;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct devfs {
|
2005-01-18 19:51:21 +03:00
|
|
|
mount_id id;
|
|
|
|
mutex lock;
|
|
|
|
int32 next_vnode_id;
|
2004-08-27 21:44:08 +04:00
|
|
|
hash_table *vnode_list_hash;
|
2002-07-09 16:24:59 +04:00
|
|
|
struct devfs_vnode *root_vnode;
|
2005-01-18 19:51:21 +03:00
|
|
|
bool root_scanned;
|
2002-07-09 16:24:59 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct devfs_cookie {
|
|
|
|
int oflags;
|
|
|
|
union {
|
|
|
|
struct cookie_dir {
|
|
|
|
struct devfs_cookie *next;
|
|
|
|
struct devfs_cookie *prev;
|
|
|
|
struct devfs_vnode *ptr;
|
2004-09-12 04:00:29 +04:00
|
|
|
int state; // iteration state
|
2002-07-09 16:24:59 +04:00
|
|
|
} dir;
|
|
|
|
struct cookie_dev {
|
|
|
|
void *dcookie;
|
|
|
|
} dev;
|
|
|
|
} u;
|
|
|
|
};
|
|
|
|
|
2004-09-12 04:00:29 +04:00
|
|
|
// directory iteration states
|
|
|
|
enum {
|
|
|
|
ITERATION_STATE_DOT = 0,
|
|
|
|
ITERATION_STATE_DOT_DOT = 1,
|
|
|
|
ITERATION_STATE_OTHERS = 2,
|
|
|
|
ITERATION_STATE_BEGIN = ITERATION_STATE_DOT,
|
|
|
|
};
|
|
|
|
|
2005-01-18 19:51:21 +03:00
|
|
|
// The boot device, if already present
|
|
|
|
extern dev_t gBootDevice;
|
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
/* the one and only allowed devfs instance */
|
2004-06-07 21:22:32 +04:00
|
|
|
static struct devfs *sDeviceFileSystem = NULL;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-06-07 05:18:56 +04:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
#define BOOTFS_HASH_SIZE 16
|
2002-07-11 01:55:33 +04:00
|
|
|
|
|
|
|
|
2002-11-29 11:36:05 +03:00
|
|
|
static uint32
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_vnode_hash_func(void *_vnode, const void *_key, uint32 range)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
const vnode_id *key = (const vnode_id *)_key;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
if (vnode != NULL)
|
|
|
|
return vnode->id % range;
|
2002-07-17 11:55:51 +04:00
|
|
|
|
|
|
|
return (*key) % range;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
|
|
|
static int
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_vnode_compare_func(void *_vnode, const void *_key)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
const vnode_id *key = (const vnode_id *)_key;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
if (vnode->id == *key)
|
2002-07-09 16:24:59 +04:00
|
|
|
return 0;
|
2002-07-17 11:55:51 +04:00
|
|
|
|
|
|
|
return -1;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
|
|
|
static struct devfs_vnode *
|
2004-09-15 04:44:57 +04:00
|
|
|
devfs_create_vnode(struct devfs *fs, devfs_vnode *parent, const char *name)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
vnode = (struct devfs_vnode *)malloc(sizeof(struct devfs_vnode));
|
|
|
|
if (vnode == NULL)
|
2002-07-09 16:24:59 +04:00
|
|
|
return NULL;
|
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
memset(vnode, 0, sizeof(struct devfs_vnode));
|
|
|
|
vnode->id = fs->next_vnode_id++;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
vnode->name = strdup(name);
|
|
|
|
if (vnode->name == NULL) {
|
|
|
|
free(vnode);
|
2002-07-09 16:24:59 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
vnode->creation_time = vnode->modification_time = time(NULL);
|
|
|
|
vnode->uid = geteuid();
|
|
|
|
vnode->gid = parent ? parent->gid : getegid();
|
|
|
|
// inherit group from parent if possible
|
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
return vnode;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_delete_vnode(struct devfs *fs, struct devfs_vnode *vnode, bool force_delete)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
// cant delete it if it's in a directory or is a directory
|
|
|
|
// and has children
|
2002-07-17 11:55:51 +04:00
|
|
|
if (!force_delete
|
2004-09-15 04:44:57 +04:00
|
|
|
&& ((S_ISDIR(vnode->stream.type) && vnode->stream.u.dir.dir_head != NULL)
|
2004-08-27 21:44:08 +04:00
|
|
|
|| vnode->dir_next != NULL))
|
2002-07-09 16:24:59 +04:00
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
// remove it from the global hash table
|
2004-08-27 21:44:08 +04:00
|
|
|
hash_remove(fs->vnode_list_hash, vnode);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (S_ISCHR(vnode->stream.type)) {
|
2004-09-04 21:46:10 +04:00
|
|
|
// for partitions, we have to release the raw device
|
|
|
|
if (vnode->stream.u.dev.part_map)
|
2004-10-27 19:21:11 +04:00
|
|
|
put_vnode(fs->id, vnode->stream.u.dev.part_map->raw_device->id);
|
2004-09-04 21:46:10 +04:00
|
|
|
|
|
|
|
// remove API conversion from old to new drivers
|
|
|
|
if (vnode->stream.u.dev.node == NULL)
|
|
|
|
free(vnode->stream.u.dev.info);
|
|
|
|
}
|
2002-07-17 11:55:51 +04:00
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
free(vnode->name);
|
|
|
|
free(vnode);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2003-10-17 18:05:05 +04:00
|
|
|
#if 0
|
2002-07-11 01:55:33 +04:00
|
|
|
static void
|
|
|
|
insert_cookie_in_jar(struct devfs_vnode *dir, struct devfs_cookie *cookie)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
cookie->u.dir.next = dir->stream.u.dir.jar_head;
|
|
|
|
dir->stream.u.dir.jar_head = cookie;
|
|
|
|
cookie->u.dir.prev = NULL;
|
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
|
|
|
static void
|
|
|
|
remove_cookie_from_jar(struct devfs_vnode *dir, struct devfs_cookie *cookie)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-07-19 08:20:07 +04:00
|
|
|
if (cookie->u.dir.next)
|
2002-07-09 16:24:59 +04:00
|
|
|
cookie->u.dir.next->u.dir.prev = cookie->u.dir.prev;
|
2002-07-19 08:20:07 +04:00
|
|
|
if (cookie->u.dir.prev)
|
2002-07-09 16:24:59 +04:00
|
|
|
cookie->u.dir.prev->u.dir.next = cookie->u.dir.next;
|
2002-07-19 08:20:07 +04:00
|
|
|
if (dir->stream.u.dir.jar_head == cookie)
|
2002-07-09 16:24:59 +04:00
|
|
|
dir->stream.u.dir.jar_head = cookie->u.dir.next;
|
|
|
|
|
|
|
|
cookie->u.dir.prev = cookie->u.dir.next = NULL;
|
|
|
|
}
|
2003-10-17 18:05:05 +04:00
|
|
|
#endif
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
/* makes sure none of the dircookies point to the vnode passed in */
|
2002-07-11 01:55:33 +04:00
|
|
|
static void
|
|
|
|
update_dircookies(struct devfs_vnode *dir, struct devfs_vnode *v)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
struct devfs_cookie *cookie;
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
for (cookie = dir->stream.u.dir.jar_head; cookie; cookie = cookie->u.dir.next) {
|
|
|
|
if (cookie->u.dir.ptr == v)
|
2002-07-09 16:24:59 +04:00
|
|
|
cookie->u.dir.ptr = v->dir_next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
static struct devfs_vnode *
|
|
|
|
devfs_find_in_dir(struct devfs_vnode *dir, const char *path)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
struct devfs_vnode *v;
|
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISDIR(dir->stream.type))
|
2002-07-09 16:24:59 +04:00
|
|
|
return NULL;
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
if (!strcmp(path, "."))
|
2002-07-09 16:24:59 +04:00
|
|
|
return dir;
|
2002-07-17 11:55:51 +04:00
|
|
|
if (!strcmp(path, ".."))
|
2002-07-09 16:24:59 +04:00
|
|
|
return dir->parent;
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
for (v = dir->stream.u.dir.dir_head; v; v = v->dir_next) {
|
2004-06-07 05:18:56 +04:00
|
|
|
TRACE(("devfs_find_in_dir: looking at entry '%s'\n", v->name));
|
2002-07-17 11:55:51 +04:00
|
|
|
if (strcmp(v->name, path) == 0) {
|
2004-06-07 05:18:56 +04:00
|
|
|
TRACE(("devfs_find_in_dir: found it at %p\n", v));
|
2002-07-09 16:24:59 +04:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2005-05-12 07:27:44 +04:00
|
|
|
devfs_insert_in_dir(struct devfs_vnode *dir, struct devfs_vnode *vnode)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISDIR(dir->stream.type))
|
|
|
|
return B_BAD_VALUE;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
vnode->dir_next = dir->stream.u.dir.dir_head;
|
|
|
|
dir->stream.u.dir.dir_head = vnode;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
vnode->parent = dir;
|
2004-09-15 04:44:57 +04:00
|
|
|
dir->modification_time = time(NULL);
|
2005-05-12 07:27:44 +04:00
|
|
|
|
|
|
|
notify_entry_created(sDeviceFileSystem->id, dir->id, vnode->name, vnode->id);
|
|
|
|
notify_stat_changed(sDeviceFileSystem->id, dir->id, B_STAT_MODIFICATION_TIME);
|
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2005-05-12 07:27:44 +04:00
|
|
|
devfs_remove_from_dir(struct devfs_vnode *dir, struct devfs_vnode *removeNode)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2005-05-12 07:27:44 +04:00
|
|
|
struct devfs_vnode *vnode = dir->stream.u.dir.dir_head;
|
|
|
|
struct devfs_vnode *lastNode = NULL;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
for (; vnode != NULL; lastNode = vnode, vnode = vnode->dir_next) {
|
|
|
|
if (vnode == removeNode) {
|
|
|
|
// make sure no dircookies point to this vnode
|
|
|
|
update_dircookies(dir, vnode);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
if (lastNode)
|
|
|
|
lastNode->dir_next = vnode->dir_next;
|
2002-07-09 16:24:59 +04:00
|
|
|
else
|
2005-05-12 07:27:44 +04:00
|
|
|
dir->stream.u.dir.dir_head = vnode->dir_next;
|
|
|
|
vnode->dir_next = NULL;
|
2004-09-15 04:44:57 +04:00
|
|
|
dir->modification_time = time(NULL);
|
2005-05-12 07:27:44 +04:00
|
|
|
|
|
|
|
notify_entry_removed(sDeviceFileSystem->id, dir->id, vnode->name, vnode->id);
|
|
|
|
notify_stat_changed(sDeviceFileSystem->id, dir->id, B_STAT_MODIFICATION_TIME);
|
2004-09-15 04:44:57 +04:00
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
}
|
2004-09-15 04:44:57 +04:00
|
|
|
return B_ENTRY_NOT_FOUND;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
/* XXX seems to be unused
|
|
|
|
static bool
|
2002-07-11 01:55:33 +04:00
|
|
|
devfs_is_dir_empty(struct devfs_vnode *dir)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-07-17 11:55:51 +04:00
|
|
|
if (dir->stream.type != STREAM_TYPE_DIR)
|
2002-07-09 16:24:59 +04:00
|
|
|
return false;
|
2002-07-17 11:55:51 +04:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
return !dir->stream.u.dir.dir_head;
|
|
|
|
}
|
2002-09-03 06:19:22 +04:00
|
|
|
*/
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-10-27 19:21:11 +04:00
|
|
|
devfs_get_partition_info(struct devfs *fs, struct devfs_vnode *v,
|
|
|
|
struct devfs_cookie *cookie, void *buffer, size_t length)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-10-27 19:21:11 +04:00
|
|
|
struct devfs_part_map *map = v->stream.u.dev.part_map;
|
2002-07-17 11:55:51 +04:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
if (!S_ISCHR(v->stream.type) || map == NULL || length != sizeof(partition_info))
|
|
|
|
return B_BAD_VALUE;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
return user_memcpy(buffer, &map->info, sizeof(partition_info));
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-19 08:20:07 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-10-27 19:21:11 +04:00
|
|
|
add_partition(struct devfs *fs, struct devfs_vnode *device,
|
|
|
|
const char *name, const partition_info &info)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-10-27 19:21:11 +04:00
|
|
|
struct devfs_vnode *partition;
|
2004-08-28 17:33:31 +04:00
|
|
|
status_t status;
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
if (!S_ISCHR(device->stream.type))
|
2004-08-28 17:33:31 +04:00
|
|
|
return B_BAD_VALUE;
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
// we don't support nested partitions
|
2004-10-27 19:21:11 +04:00
|
|
|
if (device->stream.u.dev.part_map)
|
2004-08-28 17:33:31 +04:00
|
|
|
return B_BAD_VALUE;
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
// reduce checks to a minimum - things like negative offsets could be useful
|
2002-07-17 11:55:51 +04:00
|
|
|
if (info.size < 0)
|
2004-08-28 17:33:31 +04:00
|
|
|
return B_BAD_VALUE;
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
// create partition map
|
2004-10-27 19:21:11 +04:00
|
|
|
struct devfs_part_map *map = (struct devfs_part_map *)malloc(sizeof(struct devfs_part_map));
|
|
|
|
if (map == NULL)
|
2004-08-28 17:33:31 +04:00
|
|
|
return B_NO_MEMORY;
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
memcpy(&map->info, &info, sizeof(partition_info));
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
mutex_lock(&fs->lock);
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
// you cannot change a partition once set
|
2004-10-27 19:21:11 +04:00
|
|
|
if (devfs_find_in_dir(device->parent, name)) {
|
2004-08-28 17:33:31 +04:00
|
|
|
status = B_BAD_VALUE;
|
2002-07-09 16:24:59 +04:00
|
|
|
goto err1;
|
|
|
|
}
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
// increase reference count of raw device -
|
|
|
|
// the partition device really needs it
|
|
|
|
// (at least to resolve its name on GET_PARTITION_INFO)
|
2004-10-27 19:21:11 +04:00
|
|
|
status = get_vnode(fs->id, device->id, (fs_vnode *)&map->raw_device);
|
2004-08-28 17:33:31 +04:00
|
|
|
if (status < B_OK)
|
2002-07-09 16:24:59 +04:00
|
|
|
goto err1;
|
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
// now create the partition vnode
|
2004-10-27 19:21:11 +04:00
|
|
|
partition = devfs_create_vnode(fs, device->parent, name);
|
|
|
|
if (partition == NULL) {
|
2004-08-28 17:33:31 +04:00
|
|
|
status = B_NO_MEMORY;
|
2002-07-09 16:24:59 +04:00
|
|
|
goto err2;
|
|
|
|
}
|
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
partition->stream.type = device->stream.type;
|
|
|
|
partition->stream.u.dev.node = device->stream.u.dev.node;
|
|
|
|
partition->stream.u.dev.info = device->stream.u.dev.info;
|
|
|
|
partition->stream.u.dev.ops = device->stream.u.dev.ops;
|
|
|
|
partition->stream.u.dev.part_map = map;
|
|
|
|
partition->stream.u.dev.scheduler = device->stream.u.dev.scheduler;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
hash_insert(fs->vnode_list_hash, partition);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
devfs_insert_in_dir(device->parent, partition);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
mutex_unlock(&fs->lock);
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("SET_PARTITION: Added partition\n"));
|
2004-08-28 17:33:31 +04:00
|
|
|
return B_OK;
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
err1:
|
2004-10-27 19:21:11 +04:00
|
|
|
mutex_unlock(&fs->lock);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
free(map);
|
2004-08-28 17:33:31 +04:00
|
|
|
return status;
|
2004-10-27 19:21:11 +04:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
err2:
|
2004-10-27 19:21:11 +04:00
|
|
|
mutex_unlock(&fs->lock);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
put_vnode(fs->id, device->id);
|
|
|
|
free(map);
|
2004-08-28 17:33:31 +04:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
translate_partition_access(devfs_part_map *map, off_t &offset, size_t &size)
|
|
|
|
{
|
|
|
|
if (offset < 0)
|
|
|
|
offset = 0;
|
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
if (offset > map->info.size) {
|
2004-08-28 17:33:31 +04:00
|
|
|
size = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
size = min_c(size, map->info.size - offset);
|
|
|
|
offset += map->info.offset;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
static pnp_devfs_driver_info *
|
|
|
|
create_new_driver_info(device_hooks *ops)
|
|
|
|
{
|
|
|
|
pnp_devfs_driver_info *info = (pnp_devfs_driver_info *)malloc(sizeof(pnp_devfs_driver_info));
|
|
|
|
if (info == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2005-01-12 02:26:00 +03:00
|
|
|
memset(info, 0, sizeof(driver_module_info));
|
2004-09-04 21:46:10 +04:00
|
|
|
|
|
|
|
info->open = NULL;
|
|
|
|
// ops->open is used directly for old devices
|
|
|
|
info->close = ops->close;
|
|
|
|
info->free = ops->free;
|
|
|
|
info->control = ops->control;
|
|
|
|
info->select = ops->select;
|
|
|
|
info->deselect = ops->deselect;
|
|
|
|
info->read = ops->read;
|
|
|
|
info->write = ops->write;
|
|
|
|
|
|
|
|
info->read_pages = NULL;
|
|
|
|
info->write_pages = NULL;
|
|
|
|
// old devices can't know to do physical page access
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
static status_t
|
2004-10-29 06:24:30 +04:00
|
|
|
get_node_for_path(struct devfs *fs, const char *path, struct devfs_vnode **_node)
|
2004-10-27 19:21:11 +04:00
|
|
|
{
|
2004-10-29 06:24:30 +04:00
|
|
|
return vfs_get_fs_node_from_path(fs->id, path, true, (void **)_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static status_t
|
|
|
|
unpublish_node(struct devfs *fs, const char *path, int type)
|
|
|
|
{
|
|
|
|
devfs_vnode *node;
|
|
|
|
status_t status = get_node_for_path(fs, path, &node);
|
|
|
|
if (status != B_OK)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
if ((type & S_IFMT) != type) {
|
|
|
|
status = B_BAD_TYPE;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
|
|
|
status = devfs_remove_from_dir(node->parent, node);
|
|
|
|
if (status < B_OK)
|
|
|
|
goto err2;
|
|
|
|
|
|
|
|
status = remove_vnode(fs->id, node->id);
|
|
|
|
|
|
|
|
err2:
|
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
err1:
|
|
|
|
put_vnode(fs->id, node->id);
|
|
|
|
return status;
|
2004-10-27 19:21:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-25 05:17:02 +03:00
|
|
|
static status_t
|
|
|
|
publish_directory(struct devfs *fs, const char *path)
|
|
|
|
{
|
|
|
|
ASSERT_LOCKED_MUTEX(&fs->lock);
|
|
|
|
|
|
|
|
// copy the path over to a temp buffer so we can munge it
|
|
|
|
KPath tempPath(path);
|
2005-05-13 03:31:04 +04:00
|
|
|
if (tempPath.InitCheck() != B_OK)
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
2005-01-25 05:17:02 +03:00
|
|
|
char *temp = tempPath.LockBuffer();
|
|
|
|
|
|
|
|
// create the path leading to the device
|
|
|
|
// parse the path passed in, stripping out '/'
|
|
|
|
|
|
|
|
struct devfs_vnode *dir = fs->root_vnode;
|
|
|
|
struct devfs_vnode *vnode = NULL;
|
|
|
|
status_t status = B_OK;
|
|
|
|
int32 i = 0, last = 0;
|
|
|
|
|
|
|
|
for (;temp[last] ;) {
|
|
|
|
if (temp[i] == '/') {
|
|
|
|
temp[i] = '\0';
|
|
|
|
i++;
|
|
|
|
} else if (temp[i] != '\0') {
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE(("\tpath component '%s'\n", &temp[last]));
|
|
|
|
|
|
|
|
// we have a path component
|
|
|
|
vnode = devfs_find_in_dir(dir, &temp[last]);
|
|
|
|
if (vnode) {
|
|
|
|
if (S_ISDIR(vnode->stream.type)) {
|
|
|
|
last = i;
|
|
|
|
dir = vnode;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we hit something on our path that's not a directory
|
|
|
|
status = B_FILE_EXISTS;
|
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
vnode = devfs_create_vnode(fs, dir, &temp[last]);
|
|
|
|
if (!vnode) {
|
|
|
|
status = B_NO_MEMORY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set up the new directory
|
|
|
|
vnode->stream.type = S_IFDIR | 0755;
|
|
|
|
vnode->stream.u.dir.dir_head = NULL;
|
|
|
|
vnode->stream.u.dir.jar_head = NULL;
|
|
|
|
|
|
|
|
hash_insert(sDeviceFileSystem->vnode_list_hash, vnode);
|
|
|
|
devfs_insert_in_dir(dir, vnode);
|
|
|
|
|
|
|
|
last = i;
|
|
|
|
dir = vnode;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
static status_t
|
2004-10-29 05:41:16 +04:00
|
|
|
publish_node(struct devfs *fs, const char *path, struct devfs_vnode **_node)
|
2004-09-04 21:46:10 +04:00
|
|
|
{
|
2004-10-29 05:58:08 +04:00
|
|
|
ASSERT_LOCKED_MUTEX(&fs->lock);
|
2004-09-04 21:46:10 +04:00
|
|
|
|
|
|
|
// copy the path over to a temp buffer so we can munge it
|
2004-10-29 05:58:08 +04:00
|
|
|
KPath tempPath(path);
|
2005-05-13 03:31:04 +04:00
|
|
|
if (tempPath.InitCheck() != B_OK)
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
2004-10-29 05:58:08 +04:00
|
|
|
char *temp = tempPath.LockBuffer();
|
2004-09-04 21:46:10 +04:00
|
|
|
|
|
|
|
// create the path leading to the device
|
|
|
|
// parse the path passed in, stripping out '/'
|
2004-10-29 05:58:08 +04:00
|
|
|
|
2004-10-29 05:41:16 +04:00
|
|
|
struct devfs_vnode *dir = fs->root_vnode;
|
2004-09-04 21:46:10 +04:00
|
|
|
struct devfs_vnode *vnode = NULL;
|
2004-10-29 05:58:08 +04:00
|
|
|
status_t status = B_OK;
|
2004-09-04 21:46:10 +04:00
|
|
|
int32 i = 0, last = 0;
|
|
|
|
bool atLeaf = false;
|
|
|
|
|
|
|
|
for (;;) {
|
2005-01-25 05:17:02 +03:00
|
|
|
if (temp[i] == '\0') {
|
2004-09-04 21:46:10 +04:00
|
|
|
atLeaf = true; // we'll be done after this one
|
|
|
|
} else if (temp[i] == '/') {
|
2005-01-25 05:17:02 +03:00
|
|
|
temp[i] = '\0';
|
2004-09-04 21:46:10 +04:00
|
|
|
i++;
|
|
|
|
} else {
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE(("\tpath component '%s'\n", &temp[last]));
|
|
|
|
|
|
|
|
// we have a path component
|
|
|
|
vnode = devfs_find_in_dir(dir, &temp[last]);
|
|
|
|
if (vnode) {
|
|
|
|
if (!atLeaf) {
|
|
|
|
// we are not at the leaf of the path, so as long as
|
|
|
|
// this is a dir we're okay
|
2004-09-15 04:44:57 +04:00
|
|
|
if (S_ISDIR(vnode->stream.type)) {
|
2004-09-04 21:46:10 +04:00
|
|
|
last = i;
|
|
|
|
dir = vnode;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// we are at the leaf and hit another node
|
|
|
|
// or we aren't but hit a non-dir node.
|
|
|
|
// we're screwed
|
|
|
|
status = B_FILE_EXISTS;
|
|
|
|
goto out;
|
|
|
|
} else {
|
2004-10-29 05:41:16 +04:00
|
|
|
vnode = devfs_create_vnode(fs, dir, &temp[last]);
|
2004-09-04 21:46:10 +04:00
|
|
|
if (!vnode) {
|
|
|
|
status = B_NO_MEMORY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set up the new vnode
|
2004-10-29 05:41:16 +04:00
|
|
|
if (!atLeaf) {
|
2004-09-04 21:46:10 +04:00
|
|
|
// this is a dir
|
2004-09-15 04:44:57 +04:00
|
|
|
vnode->stream.type = S_IFDIR | 0755;
|
2004-09-04 21:46:10 +04:00
|
|
|
vnode->stream.u.dir.dir_head = NULL;
|
|
|
|
vnode->stream.u.dir.jar_head = NULL;
|
2004-10-29 05:41:16 +04:00
|
|
|
} else {
|
|
|
|
// this is the last component
|
|
|
|
*_node = vnode;
|
2004-09-04 21:46:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
hash_insert(sDeviceFileSystem->vnode_list_hash, vnode);
|
|
|
|
devfs_insert_in_dir(dir, vnode);
|
|
|
|
|
|
|
|
if (atLeaf)
|
|
|
|
break;
|
2004-10-29 05:41:16 +04:00
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
last = i;
|
|
|
|
dir = vnode;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2004-10-29 05:41:16 +04:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static status_t
|
2005-01-12 02:26:00 +03:00
|
|
|
publish_device(struct devfs *fs, const char *path, device_node_info *deviceNode,
|
2004-10-29 05:41:16 +04:00
|
|
|
pnp_devfs_driver_info *info, device_hooks *ops)
|
|
|
|
{
|
|
|
|
TRACE(("publish_device(path = \"%s\", node = %p, info = %p, hooks = %p)\n",
|
2005-01-12 02:26:00 +03:00
|
|
|
path, deviceNode, info, ops));
|
2004-10-29 05:41:16 +04:00
|
|
|
|
|
|
|
if (sDeviceFileSystem == NULL) {
|
|
|
|
panic("publish_device() called before devfs mounted\n");
|
|
|
|
return B_ERROR;
|
|
|
|
}
|
|
|
|
|
2005-01-12 02:26:00 +03:00
|
|
|
if ((ops == NULL && (deviceNode == NULL || info == NULL))
|
2004-10-29 05:41:16 +04:00
|
|
|
|| path == NULL || path[0] == '/')
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
// are the provided device hooks okay?
|
|
|
|
if ((ops != NULL && (ops->open == NULL || ops->close == NULL
|
|
|
|
|| ops->read == NULL || ops->write == NULL))
|
|
|
|
|| info != NULL && (info->open == NULL || info->close == NULL
|
|
|
|
|| info->read == NULL || info->write == NULL))
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
// mark disk devices - they might get an I/O scheduler
|
|
|
|
bool isDisk = false;
|
|
|
|
if (!strncmp(path, "disk/", 5))
|
|
|
|
isDisk = true;
|
|
|
|
|
|
|
|
struct devfs_vnode *node;
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
|
|
|
status = publish_node(fs, path, &node);
|
|
|
|
if (status != B_OK)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
// all went fine, let's initialize the node
|
|
|
|
node->stream.type = S_IFCHR | 0644;
|
|
|
|
|
2005-01-12 02:26:00 +03:00
|
|
|
if (deviceNode != NULL)
|
2004-10-29 05:41:16 +04:00
|
|
|
node->stream.u.dev.info = info;
|
|
|
|
else
|
|
|
|
node->stream.u.dev.info = create_new_driver_info(ops);
|
|
|
|
|
2005-01-12 02:26:00 +03:00
|
|
|
node->stream.u.dev.node = deviceNode;
|
2004-10-29 05:41:16 +04:00
|
|
|
node->stream.u.dev.ops = ops;
|
|
|
|
|
|
|
|
// every raw disk gets an I/O scheduler object attached
|
|
|
|
// ToDo: the driver should ask for a scheduler (ie. using its devfs node attributes)
|
|
|
|
if (isDisk && !strcmp(node->name, "raw"))
|
|
|
|
node->stream.u.dev.scheduler = new IOScheduler(path, info);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&fs->lock);
|
2004-09-04 21:46:10 +04:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-28 07:22:56 +04:00
|
|
|
/** Construct complete device name (as used for device_open()).
|
|
|
|
* This is safe to use only when the device is in use (and therefore
|
|
|
|
* cannot be unpublished during the iteration).
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_device_name(struct devfs_vnode *vnode, char *buffer, size_t size)
|
|
|
|
{
|
|
|
|
struct devfs_vnode *leaf = vnode;
|
|
|
|
size_t offset = 0;
|
|
|
|
|
|
|
|
// count levels
|
|
|
|
|
|
|
|
for (; vnode->parent && vnode->parent != vnode; vnode = vnode->parent) {
|
|
|
|
offset += strlen(vnode->name) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// construct full path name
|
|
|
|
|
|
|
|
for (vnode = leaf; vnode->parent && vnode->parent != vnode; vnode = vnode->parent) {
|
|
|
|
size_t length = strlen(vnode->name);
|
|
|
|
size_t start = offset - length - 1;
|
|
|
|
|
|
|
|
if (size >= offset) {
|
|
|
|
strcpy(buffer + start, vnode->name);
|
|
|
|
if (vnode != leaf)
|
|
|
|
buffer[offset - 1] = '/';
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = start;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
// #pragma mark -
|
|
|
|
|
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-11-15 19:43:30 +03:00
|
|
|
devfs_mount(mount_id id, const char *devfs, uint32 flags, const char *args,
|
|
|
|
fs_volume *_fs, vnode_id *root_vnid)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
struct devfs *fs;
|
|
|
|
struct devfs_vnode *v;
|
2002-09-03 06:19:22 +04:00
|
|
|
status_t err;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
TRACE(("devfs_mount: entry\n"));
|
|
|
|
|
2004-06-07 21:22:32 +04:00
|
|
|
if (sDeviceFileSystem) {
|
2002-07-09 16:24:59 +04:00
|
|
|
dprintf("double mount of devfs attempted\n");
|
2004-02-23 06:21:59 +03:00
|
|
|
err = B_ERROR;
|
2002-07-09 16:24:59 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
fs = (struct devfs *)malloc(sizeof(struct devfs));
|
2002-07-14 09:13:20 +04:00
|
|
|
if (fs == NULL) {
|
2004-08-27 21:44:08 +04:00
|
|
|
err = B_NO_MEMORY;
|
2002-07-09 16:24:59 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
fs->id = id;
|
|
|
|
fs->next_vnode_id = 0;
|
2005-01-18 19:51:21 +03:00
|
|
|
fs->root_scanned = false;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
err = mutex_init(&fs->lock, "devfs_mutex");
|
2004-08-27 21:44:08 +04:00
|
|
|
if (err < B_OK)
|
2002-07-09 16:24:59 +04:00
|
|
|
goto err1;
|
|
|
|
|
2004-10-20 04:19:38 +04:00
|
|
|
fs->vnode_list_hash = hash_init(BOOTFS_HASH_SIZE, (addr_t)&v->all_next - (addr_t)v,
|
2002-07-09 16:24:59 +04:00
|
|
|
&devfs_vnode_compare_func, &devfs_vnode_hash_func);
|
2002-07-14 09:13:20 +04:00
|
|
|
if (fs->vnode_list_hash == NULL) {
|
2004-08-27 21:44:08 +04:00
|
|
|
err = B_NO_MEMORY;
|
2002-07-09 16:24:59 +04:00
|
|
|
goto err2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a vnode
|
2004-09-15 04:44:57 +04:00
|
|
|
v = devfs_create_vnode(fs, NULL, "");
|
2002-07-14 09:13:20 +04:00
|
|
|
if (v == NULL) {
|
2004-08-27 21:44:08 +04:00
|
|
|
err = B_NO_MEMORY;
|
2002-07-09 16:24:59 +04:00
|
|
|
goto err3;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set it up
|
|
|
|
v->parent = v;
|
|
|
|
|
|
|
|
// create a dir stream for it to hold
|
2004-09-15 04:44:57 +04:00
|
|
|
v->stream.type = S_IFDIR | 0755;
|
2002-07-09 16:24:59 +04:00
|
|
|
v->stream.u.dir.dir_head = NULL;
|
|
|
|
v->stream.u.dir.jar_head = NULL;
|
|
|
|
fs->root_vnode = v;
|
|
|
|
|
|
|
|
hash_insert(fs->vnode_list_hash, v);
|
|
|
|
|
|
|
|
*root_vnid = v->id;
|
|
|
|
*_fs = fs;
|
2004-06-07 21:22:32 +04:00
|
|
|
sDeviceFileSystem = fs;
|
2004-08-27 21:44:08 +04:00
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
devfs_delete_vnode(fs, v, true);
|
|
|
|
err3:
|
|
|
|
hash_uninit(fs->vnode_list_hash);
|
|
|
|
err2:
|
|
|
|
mutex_destroy(&fs->lock);
|
|
|
|
err1:
|
2002-10-30 02:07:06 +03:00
|
|
|
free(fs);
|
2002-07-09 16:24:59 +04:00
|
|
|
err:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_unmount(fs_volume _fs)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
2002-07-09 16:24:59 +04:00
|
|
|
struct devfs_vnode *v;
|
|
|
|
struct hash_iterator i;
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_unmount: entry fs = %p\n", fs));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
// delete all of the vnodes
|
|
|
|
hash_open(fs->vnode_list_hash, &i);
|
2005-01-18 19:51:21 +03:00
|
|
|
while ((v = (struct devfs_vnode *)hash_next(fs->vnode_list_hash, &i)) != NULL) {
|
2002-07-09 16:24:59 +04:00
|
|
|
devfs_delete_vnode(fs, v, true);
|
|
|
|
}
|
|
|
|
hash_close(fs->vnode_list_hash, &i, false);
|
|
|
|
|
|
|
|
hash_uninit(fs->vnode_list_hash);
|
|
|
|
mutex_destroy(&fs->lock);
|
2002-10-30 02:07:06 +03:00
|
|
|
free(fs);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_sync(fs_volume fs)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
TRACE(("devfs_sync: entry\n"));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_lookup(fs_volume _fs, fs_vnode _dir, const char *name, vnode_id *_id, int *_type)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
|
|
|
struct devfs_vnode *dir = (struct devfs_vnode *)_dir;
|
2002-07-28 22:41:07 +04:00
|
|
|
struct devfs_vnode *vnode, *vdummy;
|
2002-09-03 06:19:22 +04:00
|
|
|
status_t err;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_lookup: entry dir %p, name '%s'\n", dir, name));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISDIR(dir->stream.type))
|
2002-07-28 22:41:07 +04:00
|
|
|
return B_NOT_A_DIRECTORY;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
2005-01-18 19:51:21 +03:00
|
|
|
if (!fs->root_scanned && gBootDevice >= 0) {
|
|
|
|
fs->root_scanned = true;
|
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
|
|
|
|
// scan for drivers at root level on first contact
|
|
|
|
probe_for_device_type("");
|
|
|
|
|
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
}
|
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
// look it up
|
2002-07-17 11:55:51 +04:00
|
|
|
vnode = devfs_find_in_dir(dir, name);
|
2005-01-18 19:51:21 +03:00
|
|
|
if (vnode == NULL) {
|
|
|
|
// scan for drivers in the given directory (that indicates the type of the driver)
|
|
|
|
KPath path;
|
2005-05-13 03:31:04 +04:00
|
|
|
if (path.InitCheck() != B_OK) {
|
|
|
|
err = B_NO_MEMORY;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2005-01-18 19:51:21 +03:00
|
|
|
get_device_name(dir, path.LockBuffer(), path.BufferSize());
|
|
|
|
path.UnlockBuffer();
|
|
|
|
path.Append(name);
|
|
|
|
dprintf("lookup: \"%s\"\n", path.Path());
|
|
|
|
|
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
|
|
|
|
// scan for drivers of this type
|
|
|
|
probe_for_device_type(path.Path());
|
|
|
|
|
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
|
|
|
vnode = devfs_find_in_dir(dir, name);
|
|
|
|
if (vnode == NULL) {
|
|
|
|
err = B_ENTRY_NOT_FOUND;
|
|
|
|
goto err;
|
|
|
|
}
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2004-06-07 18:30:56 +04:00
|
|
|
err = get_vnode(fs->id, vnode->id, (fs_vnode *)&vdummy);
|
2002-07-17 11:55:51 +04:00
|
|
|
if (err < 0)
|
2002-07-09 16:24:59 +04:00
|
|
|
goto err;
|
|
|
|
|
2002-07-28 22:41:07 +04:00
|
|
|
*_id = vnode->id;
|
|
|
|
*_type = vnode->stream.type;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
err:
|
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_get_vnode_name(fs_volume _fs, fs_vnode _vnode, char *buffer, size_t bufferSize)
|
2002-07-19 01:06:02 +04:00
|
|
|
{
|
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
TRACE(("devfs_get_vnode_name: vnode = %p\n", vnode));
|
2002-07-19 01:06:02 +04:00
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
strlcpy(buffer, vnode->name, bufferSize);
|
2002-07-19 01:06:02 +04:00
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_get_vnode(fs_volume _fs, vnode_id id, fs_vnode *_vnode, bool reenter)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_get_vnode: asking for vnode id = %Ld, vnode = %p, r %d\n", id, _vnode, reenter));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
if (!reenter)
|
2002-07-09 16:24:59 +04:00
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
*_vnode = hash_lookup(fs->vnode_list_hash, &id);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
if (!reenter)
|
2002-07-09 16:24:59 +04:00
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_get_vnode: looked it up at %p\n", *_vnode));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
if (*_vnode)
|
2005-05-12 07:27:44 +04:00
|
|
|
return B_OK;
|
2002-07-17 11:55:51 +04:00
|
|
|
|
2004-02-23 06:21:59 +03:00
|
|
|
return B_ENTRY_NOT_FOUND;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_put_vnode(fs_volume _fs, fs_vnode _v, bool reenter)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-06-07 05:18:56 +04:00
|
|
|
#ifdef TRACE_DEVFS
|
2002-07-17 11:55:51 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_v;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_put_vnode: entry on vnode %p, id = %Ld, reenter %d\n", vnode, vnode->id, reenter));
|
2002-07-09 16:24:59 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0; // whatever
|
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_remove_vnode(fs_volume _fs, fs_vnode _v, bool reenter)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
2002-07-17 11:55:51 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_v;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_removevnode: remove %p (%Ld), reenter %d\n", vnode, vnode->id, reenter));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
if (!reenter)
|
2002-07-09 16:24:59 +04:00
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
if (vnode->dir_next) {
|
2002-07-09 16:24:59 +04:00
|
|
|
// can't remove node if it's linked to the dir
|
2002-07-17 11:55:51 +04:00
|
|
|
panic("devfs_removevnode: vnode %p asked to be removed is present in dir\n", vnode);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
devfs_delete_vnode(fs, vnode, false);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
if (!reenter)
|
2002-07-09 16:24:59 +04:00
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-11-01 23:20:37 +03:00
|
|
|
devfs_create(fs_volume _fs, fs_vnode _dir, const char *name, int openMode, int perms,
|
|
|
|
fs_cookie *_cookie, vnode_id *_newVnodeID)
|
2002-07-14 09:13:20 +04:00
|
|
|
{
|
2004-11-01 23:20:37 +03:00
|
|
|
struct devfs_vnode *dir = (struct devfs_vnode *)_dir;
|
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
|
|
|
struct devfs_cookie *cookie;
|
|
|
|
struct devfs_vnode *vnode, *vdummy;
|
|
|
|
status_t status = B_OK;
|
|
|
|
|
2005-01-18 19:51:21 +03:00
|
|
|
TRACE(("devfs_create: dir %p, name \"%s\", oflags 0x%x, fs_cookie %p \n", dir, name, openMode, _cookie));
|
2004-11-01 23:20:37 +03:00
|
|
|
|
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
|
|
|
// look it up
|
|
|
|
vnode = devfs_find_in_dir(dir, name);
|
|
|
|
if (!vnode) {
|
|
|
|
status = EROFS;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (openMode & O_EXCL)
|
|
|
|
return B_FILE_EXISTS;
|
|
|
|
|
|
|
|
status = get_vnode(fs->id, vnode->id, (fs_vnode *)&vdummy);
|
|
|
|
if (status < B_OK)
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
*_newVnodeID = vnode->id;
|
|
|
|
|
|
|
|
cookie = (struct devfs_cookie *)malloc(sizeof(struct devfs_cookie));
|
|
|
|
if (cookie == NULL) {
|
|
|
|
status = B_NO_MEMORY;
|
|
|
|
goto err2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISCHR(vnode->stream.type)) {
|
|
|
|
if (vnode->stream.u.dev.node != NULL) {
|
|
|
|
status = vnode->stream.u.dev.info->open(
|
|
|
|
vnode->stream.u.dev.node->parent->cookie, openMode,
|
|
|
|
&cookie->u.dev.dcookie);
|
|
|
|
} else {
|
|
|
|
char buffer[B_FILE_NAME_LENGTH];
|
|
|
|
get_device_name(vnode, buffer, sizeof(buffer));
|
|
|
|
|
|
|
|
status = vnode->stream.u.dev.ops->open(buffer, openMode,
|
|
|
|
&cookie->u.dev.dcookie);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (status < B_OK)
|
|
|
|
goto err3;
|
|
|
|
|
|
|
|
*_cookie = cookie;
|
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
return B_OK;
|
|
|
|
|
|
|
|
err3:
|
|
|
|
free(cookie);
|
|
|
|
err2:
|
|
|
|
put_vnode(fs->id, vnode->id);
|
|
|
|
err1:
|
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
return status;
|
2002-07-14 09:13:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-09-04 21:46:10 +04:00
|
|
|
devfs_open(fs_volume _fs, fs_vnode _vnode, int openMode, fs_cookie *_cookie)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
2002-07-09 16:24:59 +04:00
|
|
|
struct devfs_cookie *cookie;
|
2004-09-15 04:44:57 +04:00
|
|
|
status_t status = B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-07 02:48:58 +04:00
|
|
|
TRACE(("devfs_open: vnode %p, oflags 0x%x, fs_cookie %p \n", vnode, openMode, _cookie));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
cookie = (struct devfs_cookie *)malloc(sizeof(struct devfs_cookie));
|
2002-07-14 09:13:20 +04:00
|
|
|
if (cookie == NULL)
|
2004-08-27 21:44:08 +04:00
|
|
|
return B_NO_MEMORY;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (S_ISCHR(vnode->stream.type)) {
|
2004-09-10 03:06:04 +04:00
|
|
|
if (vnode->stream.u.dev.node != NULL) {
|
|
|
|
status = vnode->stream.u.dev.info->open(
|
|
|
|
vnode->stream.u.dev.node->parent->cookie, openMode,
|
|
|
|
&cookie->u.dev.dcookie);
|
|
|
|
} else {
|
2004-09-28 07:22:56 +04:00
|
|
|
char buffer[B_FILE_NAME_LENGTH];
|
|
|
|
get_device_name(vnode, buffer, sizeof(buffer));
|
|
|
|
|
|
|
|
status = vnode->stream.u.dev.ops->open(buffer, openMode,
|
2004-09-10 03:06:04 +04:00
|
|
|
&cookie->u.dev.dcookie);
|
|
|
|
}
|
|
|
|
}
|
2004-11-01 23:20:37 +03:00
|
|
|
if (status < B_OK)
|
|
|
|
free(cookie);
|
|
|
|
else
|
|
|
|
*_cookie = cookie;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
return status;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_close(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_close: entry vnode %p, cookie %p\n", vnode, cookie));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (S_ISCHR(vnode->stream.type)) {
|
2002-07-09 16:24:59 +04:00
|
|
|
// pass the call through to the underlying device
|
2004-09-04 21:46:10 +04:00
|
|
|
return vnode->stream.u.dev.info->close(cookie->u.dev.dcookie);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_free_cookie(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_freecookie: entry vnode %p, cookie %p\n", vnode, cookie));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (S_ISCHR(vnode->stream.type)) {
|
2002-07-09 16:24:59 +04:00
|
|
|
// pass the call through to the underlying device
|
2004-09-04 21:46:10 +04:00
|
|
|
vnode->stream.u.dev.info->free(cookie->u.dev.dcookie);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
free(cookie);
|
2002-07-09 16:24:59 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_fsync(fs_volume _fs, fs_vnode _v)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-10-29 05:58:08 +04:00
|
|
|
static status_t
|
2005-04-06 20:07:10 +04:00
|
|
|
devfs_read_link(fs_volume _fs, fs_vnode _link, char *buffer, size_t *_bufferSize)
|
2004-10-29 05:58:08 +04:00
|
|
|
{
|
|
|
|
struct devfs_vnode *link = (struct devfs_vnode *)_link;
|
|
|
|
|
|
|
|
if (!S_ISLNK(link->stream.type))
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
2005-04-06 20:07:10 +04:00
|
|
|
if (*_bufferSize <= link->stream.u.symlink.length) {
|
|
|
|
*_bufferSize = link->stream.u.symlink.length + 1;
|
|
|
|
return B_BUFFER_OVERFLOW;
|
|
|
|
}
|
2004-10-29 05:58:08 +04:00
|
|
|
|
|
|
|
memcpy(buffer, link->stream.u.symlink.path, link->stream.u.symlink.length + 1);
|
2004-10-29 17:11:46 +04:00
|
|
|
return B_OK;
|
2004-10-29 05:58:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
static status_t
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_read(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos,
|
|
|
|
void *buffer, size_t *_length)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
TRACE(("devfs_read: vnode %p, cookie %p, pos %Ld, len %p\n", vnode, cookie, pos, _length));
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISCHR(vnode->stream.type))
|
2004-09-10 03:06:04 +04:00
|
|
|
return B_BAD_VALUE;
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
if (vnode->stream.u.dev.part_map)
|
|
|
|
translate_partition_access(vnode->stream.u.dev.part_map, pos, *_length);
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
if (*_length == 0)
|
|
|
|
return B_OK;
|
|
|
|
|
|
|
|
// if this device has an I/O scheduler attached, the request must go through it
|
|
|
|
if (IOScheduler *scheduler = vnode->stream.u.dev.scheduler) {
|
|
|
|
IORequest request(cookie->u.dev.dcookie, pos, buffer, *_length);
|
|
|
|
|
|
|
|
status_t status = scheduler->Process(request);
|
|
|
|
if (status == B_OK)
|
|
|
|
*_length = request.Size();
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
return status;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
// pass the call through to the device
|
2004-09-04 21:46:10 +04:00
|
|
|
return vnode->stream.u.dev.info->read(cookie->u.dev.dcookie, pos, buffer, _length);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
static status_t
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_write(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos,
|
|
|
|
const void *buffer, size_t *_length)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
|
|
|
|
|
|
|
TRACE(("devfs_write: vnode %p, cookie %p, pos %Ld, len %p\n", vnode, cookie, pos, _length));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISCHR(vnode->stream.type))
|
2004-08-28 17:33:31 +04:00
|
|
|
return B_BAD_VALUE;
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
if (vnode->stream.u.dev.part_map)
|
|
|
|
translate_partition_access(vnode->stream.u.dev.part_map, pos, *_length);
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
if (*_length == 0)
|
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
if (IOScheduler *scheduler = vnode->stream.u.dev.scheduler) {
|
|
|
|
IORequest request(cookie->u.dev.dcookie, pos, buffer, *_length);
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-08-28 17:33:31 +04:00
|
|
|
status_t status = scheduler->Process(request);
|
|
|
|
if (status == B_OK)
|
|
|
|
*_length = request.Size();
|
|
|
|
|
|
|
|
return status;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
2004-08-27 21:44:08 +04:00
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
return vnode->stream.u.dev.info->write(cookie->u.dev.dcookie, pos, buffer, _length);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_create_dir(fs_volume _fs, fs_vnode _dir, const char *name, int perms, vnode_id *new_vnid)
|
2002-07-14 09:13:20 +04:00
|
|
|
{
|
|
|
|
return EROFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_open_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie *_cookie)
|
2002-07-14 09:13:20 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
2002-07-14 09:13:20 +04:00
|
|
|
struct devfs_cookie *cookie;
|
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_open_dir: vnode %p\n", vnode));
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISDIR(vnode->stream.type))
|
2004-08-27 21:44:08 +04:00
|
|
|
return B_BAD_VALUE;
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2004-08-27 21:44:08 +04:00
|
|
|
cookie = (struct devfs_cookie *)malloc(sizeof(struct devfs_cookie));
|
2002-07-14 09:13:20 +04:00
|
|
|
if (cookie == NULL)
|
2004-08-27 21:44:08 +04:00
|
|
|
return B_NO_MEMORY;
|
2002-07-14 09:13:20 +04:00
|
|
|
|
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
|
|
|
cookie->u.dir.ptr = vnode->stream.u.dir.dir_head;
|
2004-09-12 04:00:29 +04:00
|
|
|
cookie->u.dir.state = ITERATION_STATE_BEGIN;
|
2002-07-14 09:13:20 +04:00
|
|
|
*_cookie = cookie;
|
|
|
|
|
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_read_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-09-10 03:06:04 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
2004-02-23 08:03:04 +03:00
|
|
|
status_t status = B_OK;
|
2004-09-12 04:00:29 +04:00
|
|
|
struct devfs_vnode *childNode = NULL;
|
|
|
|
const char *name = NULL;
|
|
|
|
struct devfs_vnode *nextChildNode = NULL;
|
|
|
|
int nextState = cookie->u.dir.state;
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
TRACE(("devfs_read_dir: vnode %p, cookie %p, buffer %p, size %ld\n", _vnode, cookie, dirent, bufferSize));
|
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISDIR(vnode->stream.type))
|
|
|
|
return B_BAD_VALUE;
|
2002-07-11 01:55:33 +04:00
|
|
|
|
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
2004-09-12 04:00:29 +04:00
|
|
|
switch (cookie->u.dir.state) {
|
|
|
|
case ITERATION_STATE_DOT:
|
|
|
|
childNode = vnode;
|
|
|
|
name = ".";
|
|
|
|
nextChildNode = vnode->stream.u.dir.dir_head;
|
|
|
|
nextState = cookie->u.dir.state + 1;
|
|
|
|
break;
|
|
|
|
case ITERATION_STATE_DOT_DOT:
|
|
|
|
childNode = vnode->parent;
|
|
|
|
name = "..";
|
|
|
|
nextChildNode = vnode->stream.u.dir.dir_head;
|
|
|
|
nextState = cookie->u.dir.state + 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
childNode = cookie->u.dir.ptr;
|
|
|
|
if (childNode) {
|
|
|
|
name = childNode->name;
|
|
|
|
nextChildNode = childNode->dir_next;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!childNode) {
|
2002-07-11 01:55:33 +04:00
|
|
|
*_num = 0;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
dirent->d_dev = fs->id;
|
2004-09-12 04:00:29 +04:00
|
|
|
dirent->d_ino = childNode->id;
|
|
|
|
dirent->d_reclen = strlen(name) + sizeof(struct dirent);
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-11-29 11:36:05 +03:00
|
|
|
if (dirent->d_reclen > bufferSize) {
|
2002-07-11 01:55:33 +04:00
|
|
|
status = ENOBUFS;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2004-09-12 04:00:29 +04:00
|
|
|
status = user_strlcpy(dirent->d_name, name,
|
|
|
|
bufferSize - sizeof(struct dirent));
|
2004-02-23 08:03:04 +03:00
|
|
|
if (status < B_OK)
|
2002-07-11 01:55:33 +04:00
|
|
|
goto err;
|
|
|
|
|
2004-09-12 04:00:29 +04:00
|
|
|
cookie->u.dir.ptr = nextChildNode;
|
|
|
|
cookie->u.dir.state = nextState;
|
2004-02-23 08:03:04 +03:00
|
|
|
status = B_OK;
|
2002-07-11 01:55:33 +04:00
|
|
|
|
|
|
|
err:
|
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
|
|
|
|
return status;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2002-09-24 03:24:12 +04:00
|
|
|
devfs_rewind_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-09-10 03:06:04 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
2002-07-17 11:55:51 +04:00
|
|
|
|
|
|
|
TRACE(("devfs_rewind_dir: vnode %p, cookie %p\n", _vnode, _cookie));
|
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISDIR(vnode->stream.type))
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
2004-09-10 03:06:04 +04:00
|
|
|
cookie->u.dir.ptr = vnode->stream.u.dir.dir_head;
|
2004-09-12 04:00:29 +04:00
|
|
|
cookie->u.dir.state = ITERATION_STATE_BEGIN;
|
2002-07-11 01:55:33 +04:00
|
|
|
|
|
|
|
mutex_unlock(&fs->lock);
|
2002-07-09 16:24:59 +04:00
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_ioctl(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, ulong op, void *buffer, size_t length)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
|
|
|
|
|
|
|
TRACE(("devfs_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n", _vnode, _cookie, op, buffer, length));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (S_ISCHR(vnode->stream.type)) {
|
2002-07-11 01:55:33 +04:00
|
|
|
switch (op) {
|
2004-02-23 06:21:59 +03:00
|
|
|
case B_GET_PARTITION_INFO:
|
2004-08-28 17:33:31 +04:00
|
|
|
return devfs_get_partition_info(fs, vnode, cookie, (partition_info *)buffer, length);
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-02-23 06:21:59 +03:00
|
|
|
case B_SET_PARTITION:
|
2004-10-27 19:21:11 +04:00
|
|
|
return B_NOT_ALLOWED;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
return vnode->stream.u.dev.info->control(cookie->u.dev.dcookie, op, buffer, length);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
2004-02-23 06:21:59 +03:00
|
|
|
|
|
|
|
return B_BAD_VALUE;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-10-05 17:24:12 +04:00
|
|
|
static status_t
|
|
|
|
devfs_set_flags(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, int flags)
|
|
|
|
{
|
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
|
|
|
|
|
|
|
// we need to pass the O_NONBLOCK flag to the underlying device
|
|
|
|
|
|
|
|
if (!S_ISCHR(vnode->stream.type))
|
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
|
|
|
|
return vnode->stream.u.dev.info->control(cookie->u.dev.dcookie, flags & O_NONBLOCK ?
|
|
|
|
B_SET_NONBLOCKING_IO : B_SET_BLOCKING_IO, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-18 00:04:25 +03:00
|
|
|
static status_t
|
|
|
|
devfs_select(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, uint8 event,
|
|
|
|
uint32 ref, selectsync *sync)
|
|
|
|
{
|
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
|
|
|
|
|
|
|
if (!S_ISCHR(vnode->stream.type))
|
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
|
|
|
|
// If the device has no select() hook, notify select() now.
|
|
|
|
if (!vnode->stream.u.dev.info->select)
|
|
|
|
return notify_select_event((selectsync*)sync, ref, event);
|
|
|
|
|
|
|
|
return vnode->stream.u.dev.info->select(cookie->u.dev.dcookie, event, ref,
|
|
|
|
(selectsync*)sync);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static status_t
|
|
|
|
devfs_deselect(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, uint8 event,
|
|
|
|
selectsync *sync)
|
|
|
|
{
|
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
|
|
|
|
|
|
|
if (!S_ISCHR(vnode->stream.type))
|
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
|
|
|
|
// If the device has no select() hook, notify select() now.
|
|
|
|
if (!vnode->stream.u.dev.info->deselect)
|
|
|
|
return B_OK;
|
|
|
|
|
|
|
|
return vnode->stream.u.dev.info->deselect(cookie->u.dev.dcookie, event,
|
|
|
|
(selectsync*)sync);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
static bool
|
2004-09-07 02:48:58 +04:00
|
|
|
devfs_can_page(fs_volume _fs, fs_vnode _vnode, fs_cookie cookie)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-09-04 21:46:10 +04:00
|
|
|
struct devfs_vnode *vnode = (devfs_vnode *)_vnode;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-07 02:48:58 +04:00
|
|
|
TRACE(("devfs_canpage: vnode %p\n", vnode));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISCHR(vnode->stream.type)
|
2004-09-07 19:39:03 +04:00
|
|
|
|| vnode->stream.u.dev.node == NULL
|
|
|
|
|| cookie == NULL)
|
2004-09-04 21:46:10 +04:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return vnode->stream.u.dev.info->read_pages != NULL;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
|
|
|
|
static status_t
|
2004-09-07 02:48:58 +04:00
|
|
|
devfs_read_pages(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos, const iovec *vecs, size_t count, size_t *_numBytes)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-09-04 21:46:10 +04:00
|
|
|
struct devfs_vnode *vnode = (devfs_vnode *)_vnode;
|
2004-09-07 02:48:58 +04:00
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-07 02:48:58 +04:00
|
|
|
TRACE(("devfs_read_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISCHR(vnode->stream.type)
|
2004-09-07 19:39:03 +04:00
|
|
|
|| vnode->stream.u.dev.info->read_pages == NULL
|
|
|
|
|| cookie == NULL)
|
2004-09-04 21:46:10 +04:00
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
|
|
|
|
if (vnode->stream.u.dev.part_map)
|
|
|
|
translate_partition_access(vnode->stream.u.dev.part_map, pos, *_numBytes);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-07 02:48:58 +04:00
|
|
|
return vnode->stream.u.dev.info->read_pages(cookie->u.dev.dcookie, pos, vecs, count, _numBytes);
|
2004-09-04 21:46:10 +04:00
|
|
|
}
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
static status_t
|
2004-09-07 02:48:58 +04:00
|
|
|
devfs_write_pages(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos, const iovec *vecs, size_t count, size_t *_numBytes)
|
2004-09-04 21:46:10 +04:00
|
|
|
{
|
|
|
|
struct devfs_vnode *vnode = (devfs_vnode *)_vnode;
|
2004-09-07 02:48:58 +04:00
|
|
|
struct devfs_cookie *cookie = (struct devfs_cookie *)_cookie;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-07 02:48:58 +04:00
|
|
|
TRACE(("devfs_write_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
if (!S_ISCHR(vnode->stream.type)
|
2004-09-07 19:39:03 +04:00
|
|
|
|| vnode->stream.u.dev.info->write_pages == NULL
|
|
|
|
|| cookie == NULL)
|
2004-09-04 21:46:10 +04:00
|
|
|
return B_NOT_ALLOWED;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
if (vnode->stream.u.dev.part_map)
|
|
|
|
translate_partition_access(vnode->stream.u.dev.part_map, pos, *_numBytes);
|
|
|
|
|
2004-09-07 02:48:58 +04:00
|
|
|
return vnode->stream.u.dev.info->write_pages(cookie->u.dev.dcookie, pos, vecs, count, _numBytes);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
2004-02-23 06:21:59 +03:00
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_read_stat(fs_volume _fs, fs_vnode _vnode, struct stat *stat)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
TRACE(("devfs_read_stat: vnode %p (%Ld), stat %p\n", vnode, vnode->id, stat));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-17 11:55:51 +04:00
|
|
|
stat->st_ino = vnode->id;
|
2002-07-09 16:24:59 +04:00
|
|
|
stat->st_size = 0;
|
2004-09-15 04:44:57 +04:00
|
|
|
stat->st_mode = vnode->stream.type;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
stat->st_nlink = 1;
|
|
|
|
stat->st_blksize = 65536;
|
|
|
|
|
|
|
|
stat->st_uid = vnode->uid;
|
|
|
|
stat->st_gid = vnode->gid;
|
|
|
|
|
|
|
|
stat->st_atime = time(NULL);
|
|
|
|
stat->st_mtime = stat->st_ctime = vnode->modification_time;
|
|
|
|
stat->st_crtime = vnode->creation_time;
|
|
|
|
|
2004-09-28 07:22:56 +04:00
|
|
|
// ToDo: this only works for partitions right now - if we should decide
|
|
|
|
// to keep this feature, we should have a better solution
|
|
|
|
if (S_ISCHR(vnode->stream.type)) {
|
|
|
|
//device_geometry geometry;
|
|
|
|
|
|
|
|
// if it's a real block device, then let's report a useful size
|
|
|
|
if (vnode->stream.u.dev.part_map != NULL) {
|
2004-10-27 19:21:11 +04:00
|
|
|
stat->st_size = vnode->stream.u.dev.part_map->info.size;
|
2004-09-28 07:22:56 +04:00
|
|
|
#if 0
|
|
|
|
} else if (vnode->stream.u.dev.info->control(cookie->u.dev.dcookie,
|
|
|
|
B_GET_GEOMETRY, &geometry, sizeof(struct device_geometry)) >= B_OK) {
|
|
|
|
stat->st_size = 1LL * geometry.head_count * geometry.cylinder_count
|
|
|
|
* geometry.sectors_per_track * geometry.bytes_per_sector;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// is this a real block device? then let's have it reported like that
|
|
|
|
if (stat->st_size != 0)
|
|
|
|
stat->st_mode = S_IFBLK | (vnode->stream.type & S_IUMSK);
|
|
|
|
}
|
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-03 06:19:22 +04:00
|
|
|
static status_t
|
2004-08-27 21:44:08 +04:00
|
|
|
devfs_write_stat(fs_volume _fs, fs_vnode _vnode, const struct stat *stat, uint32 statMask)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-09-15 04:44:57 +04:00
|
|
|
struct devfs *fs = (struct devfs *)_fs;
|
2004-08-27 21:44:08 +04:00
|
|
|
struct devfs_vnode *vnode = (struct devfs_vnode *)_vnode;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-15 04:44:57 +04:00
|
|
|
TRACE(("devfs_write_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id, stat));
|
|
|
|
|
|
|
|
// we cannot change the size of anything
|
|
|
|
if (statMask & FS_WRITE_STAT_SIZE)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
mutex_lock(&fs->lock);
|
|
|
|
|
|
|
|
if (statMask & FS_WRITE_STAT_MODE)
|
|
|
|
vnode->stream.type = (vnode->stream.type & ~S_IUMSK) | (stat->st_mode & S_IUMSK);
|
|
|
|
|
|
|
|
if (statMask & FS_WRITE_STAT_UID)
|
|
|
|
vnode->uid = stat->st_uid;
|
|
|
|
if (statMask & FS_WRITE_STAT_GID)
|
|
|
|
vnode->gid = stat->st_gid;
|
|
|
|
|
|
|
|
if (statMask & FS_WRITE_STAT_MTIME)
|
|
|
|
vnode->modification_time = stat->st_mtime;
|
|
|
|
if (statMask & FS_WRITE_STAT_CRTIME)
|
|
|
|
vnode->creation_time = stat->st_crtime;
|
|
|
|
|
|
|
|
mutex_unlock(&fs->lock);
|
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
notify_stat_changed(fs->id, vnode->id, statMask);
|
2004-09-15 04:44:57 +04:00
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-06-07 21:22:32 +04:00
|
|
|
static status_t
|
|
|
|
devfs_std_ops(int32 op, ...)
|
|
|
|
{
|
|
|
|
switch (op) {
|
|
|
|
case B_MODULE_INIT:
|
|
|
|
return B_OK;
|
|
|
|
|
|
|
|
case B_MODULE_UNINIT:
|
|
|
|
return B_OK;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return B_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
file_system_info gDeviceFileSystem = {
|
|
|
|
{
|
|
|
|
"file_systems/devfs" B_CURRENT_FS_API_VERSION,
|
|
|
|
0,
|
|
|
|
devfs_std_ops,
|
|
|
|
},
|
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
&devfs_mount,
|
|
|
|
&devfs_unmount,
|
2002-07-23 18:10:12 +04:00
|
|
|
NULL,
|
|
|
|
NULL,
|
2002-07-09 16:24:59 +04:00
|
|
|
&devfs_sync,
|
|
|
|
|
|
|
|
&devfs_lookup,
|
2002-07-19 01:06:02 +04:00
|
|
|
&devfs_get_vnode_name,
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-14 09:13:20 +04:00
|
|
|
&devfs_get_vnode,
|
|
|
|
&devfs_put_vnode,
|
|
|
|
&devfs_remove_vnode,
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
&devfs_can_page,
|
|
|
|
&devfs_read_pages,
|
|
|
|
&devfs_write_pages,
|
|
|
|
|
|
|
|
NULL, // get_file_map
|
2002-07-14 09:13:20 +04:00
|
|
|
|
|
|
|
/* common */
|
|
|
|
&devfs_ioctl,
|
2004-10-05 17:24:12 +04:00
|
|
|
&devfs_set_flags,
|
2005-03-18 00:04:25 +03:00
|
|
|
&devfs_select,
|
|
|
|
&devfs_deselect,
|
2002-07-09 16:24:59 +04:00
|
|
|
&devfs_fsync,
|
|
|
|
|
2004-10-29 05:58:08 +04:00
|
|
|
&devfs_read_link,
|
2002-08-05 09:37:17 +04:00
|
|
|
NULL, // write_link
|
2002-07-28 22:41:07 +04:00
|
|
|
NULL, // symlink
|
2002-08-14 00:39:25 +04:00
|
|
|
NULL, // link
|
2004-10-29 06:24:30 +04:00
|
|
|
NULL, // unlink
|
|
|
|
NULL, // rename
|
2002-07-14 09:13:20 +04:00
|
|
|
|
2002-08-13 17:51:36 +04:00
|
|
|
NULL, // access
|
2002-07-14 09:13:20 +04:00
|
|
|
&devfs_read_stat,
|
|
|
|
&devfs_write_stat,
|
|
|
|
|
|
|
|
/* file */
|
|
|
|
&devfs_create,
|
|
|
|
&devfs_open,
|
|
|
|
&devfs_close,
|
|
|
|
&devfs_free_cookie,
|
2002-07-09 16:24:59 +04:00
|
|
|
&devfs_read,
|
|
|
|
&devfs_write,
|
2002-07-14 09:13:20 +04:00
|
|
|
|
|
|
|
/* directory */
|
|
|
|
&devfs_create_dir,
|
2002-08-14 00:39:25 +04:00
|
|
|
NULL, // remove_dir
|
2002-07-14 09:13:20 +04:00
|
|
|
&devfs_open_dir,
|
|
|
|
&devfs_close, // we are using the same operations for directories
|
|
|
|
&devfs_free_cookie, // and files here - that's intended, not by accident
|
2002-07-09 16:24:59 +04:00
|
|
|
&devfs_read_dir,
|
|
|
|
&devfs_rewind_dir,
|
2002-09-24 03:24:12 +04:00
|
|
|
|
|
|
|
// the other operations are not supported (attributes, indices, queries)
|
|
|
|
NULL,
|
2002-07-09 16:24:59 +04:00
|
|
|
};
|
|
|
|
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2004-06-07 05:18:56 +04:00
|
|
|
// #pragma mark -
|
|
|
|
// temporary hack to get it to work with the current device manager
|
|
|
|
|
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
static device_manager_info *sDeviceManager;
|
2004-06-07 05:18:56 +04:00
|
|
|
|
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
static const device_attr pnp_devfs_attrs[] = {
|
|
|
|
{ B_DRIVER_MODULE, B_STRING_TYPE, { string: PNP_DEVFS_MODULE_NAME }},
|
2004-06-07 05:18:56 +04:00
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** someone registered a device */
|
|
|
|
|
|
|
|
static status_t
|
2005-01-18 19:51:21 +03:00
|
|
|
pnp_devfs_register_device(device_node_handle parent)
|
2004-06-07 05:18:56 +04:00
|
|
|
{
|
2005-05-12 07:27:44 +04:00
|
|
|
char *filename = NULL;
|
2005-01-12 02:26:00 +03:00
|
|
|
device_node_handle node;
|
2004-09-04 21:46:10 +04:00
|
|
|
status_t status;
|
2004-06-07 05:18:56 +04:00
|
|
|
|
|
|
|
TRACE(("pnp_devfs_probe()\n"));
|
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
if (sDeviceManager->get_attr_string(parent, PNP_DEVFS_FILENAME, &filename, true) != B_OK) {
|
2004-09-04 21:46:10 +04:00
|
|
|
dprintf("devfs: Item containing file name is missing\n");
|
|
|
|
status = B_ERROR;
|
|
|
|
goto err1;
|
2004-06-07 05:18:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
TRACE(("Adding %s\n", filename));
|
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
status = sDeviceManager->register_device(parent, pnp_devfs_attrs, NULL, &node);
|
|
|
|
if (status != B_OK || node == NULL)
|
2004-09-04 21:46:10 +04:00
|
|
|
goto err1;
|
2004-06-07 05:18:56 +04:00
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
// ToDo: this is a hack to get things working (init_driver() only works for registered nodes)
|
|
|
|
parent->registered = true;
|
|
|
|
|
|
|
|
pnp_devfs_driver_info *info;
|
|
|
|
status = sDeviceManager->init_driver(parent, NULL, (driver_module_info **)&info, NULL);
|
|
|
|
if (status != B_OK)
|
2004-06-07 05:18:56 +04:00
|
|
|
goto err2;
|
|
|
|
|
|
|
|
//add_device(device);
|
2004-10-29 05:41:16 +04:00
|
|
|
status = publish_device(sDeviceFileSystem, filename, node, info, NULL);
|
|
|
|
if (status != B_OK)
|
|
|
|
goto err3;
|
2004-06-07 05:18:56 +04:00
|
|
|
//nudge();
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
|
2004-10-29 05:41:16 +04:00
|
|
|
err3:
|
2005-05-12 07:27:44 +04:00
|
|
|
sDeviceManager->uninit_driver(parent);
|
2004-06-07 05:18:56 +04:00
|
|
|
err2:
|
2005-05-12 07:27:44 +04:00
|
|
|
sDeviceManager->unregister_device(node);
|
2004-09-04 21:46:10 +04:00
|
|
|
err1:
|
2004-06-07 05:18:56 +04:00
|
|
|
free(filename);
|
|
|
|
|
2004-09-04 21:46:10 +04:00
|
|
|
return status;
|
2004-06-07 05:18:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// remove device from public list and add it to unpublish list
|
|
|
|
// (devices_lock must be hold)
|
|
|
|
static void
|
|
|
|
pnp_devfs_remove_device(device_info *device)
|
|
|
|
{
|
|
|
|
TRACE(("removing device %s from public list\n", device->name));
|
|
|
|
|
|
|
|
--num_devices;
|
|
|
|
REMOVE_DL_LIST( device, devices, );
|
|
|
|
|
|
|
|
++num_unpublished_devices;
|
|
|
|
ADD_DL_LIST_HEAD( device, devices_to_unpublish, );
|
|
|
|
|
|
|
|
// (don't free it even if no handle is open - the device
|
|
|
|
// info block contains the hook list which may just got passed
|
|
|
|
// to the devfs layer; we better wait until next
|
|
|
|
// publish_devices, so we are sure that devfs won't access
|
|
|
|
// the hook list anymore)
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// device got removed
|
|
|
|
static void
|
2005-01-12 02:26:00 +03:00
|
|
|
pnp_devfs_device_removed(device_node_handle node, void *cookie)
|
2004-06-07 05:18:56 +04:00
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
device_info *device;
|
2005-01-12 02:26:00 +03:00
|
|
|
device_node_handle parent;
|
2004-06-07 05:18:56 +04:00
|
|
|
status_t res = B_OK;
|
|
|
|
#endif
|
|
|
|
TRACE(("pnp_devfs_device_removed()\n"));
|
|
|
|
#if 0
|
2005-05-12 07:27:44 +04:00
|
|
|
parent = sDeviceManager->get_parent(node);
|
2004-06-07 05:18:56 +04:00
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
// don't use cookie - we don't use sDeviceManager loading scheme but
|
2004-06-07 05:18:56 +04:00
|
|
|
// global data and keep care of everything ourself!
|
|
|
|
ACQUIRE_BEN( &device_list_lock );
|
|
|
|
|
|
|
|
for( device = devices; device; device = device->next ) {
|
|
|
|
if( device->parent == parent )
|
|
|
|
break;
|
|
|
|
}
|
2005-05-12 07:27:44 +04:00
|
|
|
|
2004-06-07 05:18:56 +04:00
|
|
|
if( device != NULL ) {
|
2005-05-12 07:27:44 +04:00
|
|
|
pnp_devfs_remove_device(device);
|
2004-06-07 05:18:56 +04:00
|
|
|
} else {
|
|
|
|
SHOW_ERROR( 0, "bug: node %p couldn't been found", node );
|
|
|
|
res = B_NAME_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE_BEN( &device_list_lock );
|
|
|
|
|
|
|
|
//nudge();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static status_t
|
2004-06-07 21:22:32 +04:00
|
|
|
pnp_devfs_std_ops(int32 op, ...)
|
2004-06-07 05:18:56 +04:00
|
|
|
{
|
|
|
|
switch (op) {
|
|
|
|
case B_MODULE_INIT:
|
2005-05-12 07:27:44 +04:00
|
|
|
return get_module(B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager);
|
2004-06-07 05:18:56 +04:00
|
|
|
|
|
|
|
case B_MODULE_UNINIT:
|
2005-05-12 07:27:44 +04:00
|
|
|
put_module(B_DEVICE_MANAGER_MODULE_NAME);
|
2004-06-07 05:18:56 +04:00
|
|
|
return B_OK;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return B_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-12 02:26:00 +03:00
|
|
|
driver_module_info gDeviceForDriversModule = {
|
2004-06-07 05:18:56 +04:00
|
|
|
{
|
|
|
|
PNP_DEVFS_MODULE_NAME,
|
|
|
|
0 /*B_KEEP_LOADED*/,
|
2004-06-07 21:22:32 +04:00
|
|
|
pnp_devfs_std_ops
|
2004-06-07 05:18:56 +04:00
|
|
|
},
|
|
|
|
|
2005-05-12 07:27:44 +04:00
|
|
|
NULL, // supports device
|
2005-01-18 19:51:21 +03:00
|
|
|
pnp_devfs_register_device,
|
2005-05-12 07:27:44 +04:00
|
|
|
NULL, // init driver
|
|
|
|
NULL, // uninit driver
|
|
|
|
pnp_devfs_device_removed,
|
|
|
|
NULL, // cleanup
|
|
|
|
NULL, // get paths
|
2004-06-07 05:18:56 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
|
|
|
2004-10-29 05:41:16 +04:00
|
|
|
extern "C" status_t
|
|
|
|
devfs_unpublish_file_device(const char *path)
|
|
|
|
{
|
2004-10-29 06:24:30 +04:00
|
|
|
return unpublish_node(sDeviceFileSystem, path, S_IFLNK);
|
2004-10-29 05:41:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern "C" status_t
|
|
|
|
devfs_publish_file_device(const char *path, const char *filePath)
|
|
|
|
{
|
2004-10-29 05:58:08 +04:00
|
|
|
struct devfs_vnode *node;
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
filePath = strdup(filePath);
|
|
|
|
if (filePath == NULL)
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
mutex_lock(&sDeviceFileSystem->lock);
|
|
|
|
|
|
|
|
status = publish_node(sDeviceFileSystem, path, &node);
|
|
|
|
if (status != B_OK)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
// all went fine, let's initialize the node
|
|
|
|
node->stream.type = S_IFLNK | 0644;
|
|
|
|
node->stream.u.symlink.path = filePath;
|
|
|
|
node->stream.u.symlink.length = strlen(filePath);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&sDeviceFileSystem->lock);
|
|
|
|
return status;
|
2004-10-29 05:41:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-27 21:57:43 +04:00
|
|
|
extern "C" status_t
|
2004-06-15 19:32:22 +04:00
|
|
|
devfs_unpublish_partition(const char *path)
|
|
|
|
{
|
2004-10-29 06:24:30 +04:00
|
|
|
return unpublish_node(sDeviceFileSystem, path, S_IFCHR);
|
2004-06-15 19:32:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-27 21:57:43 +04:00
|
|
|
extern "C" status_t
|
2004-06-15 19:32:22 +04:00
|
|
|
devfs_publish_partition(const char *path, const partition_info *info)
|
|
|
|
{
|
2004-10-27 19:21:11 +04:00
|
|
|
if (path == NULL || info == NULL)
|
2004-06-15 19:32:22 +04:00
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
2004-10-27 19:21:11 +04:00
|
|
|
TRACE(("publish partition: %s (device \"%s\", offset %Ld, size %Ld)\n",
|
|
|
|
path, info->device, info->offset, info->size));
|
|
|
|
|
|
|
|
// the partition and device paths must be the same until the leaves
|
|
|
|
const char *lastPath = strrchr(path, '/');
|
|
|
|
const char *lastDevice = strrchr(info->device, '/');
|
|
|
|
if (lastPath == NULL || lastDevice == NULL
|
|
|
|
|| lastPath - path != lastDevice - info->device
|
|
|
|
|| strncmp(path, info->device, lastPath - path))
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
devfs_vnode *device;
|
2004-10-29 06:24:30 +04:00
|
|
|
status_t status = get_node_for_path(sDeviceFileSystem, info->device, &device);
|
2004-10-27 19:21:11 +04:00
|
|
|
if (status != B_OK)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
status = add_partition(sDeviceFileSystem, device, lastPath + 1, *info);
|
|
|
|
|
|
|
|
put_vnode(sDeviceFileSystem->id, device->id);
|
|
|
|
return status;
|
2004-06-15 19:32:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-27 21:57:43 +04:00
|
|
|
extern "C" status_t
|
2004-09-04 21:46:10 +04:00
|
|
|
devfs_publish_device(const char *path, void *obsolete, device_hooks *ops)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-10-29 05:41:16 +04:00
|
|
|
return publish_device(sDeviceFileSystem, path, NULL, NULL, ops);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
2002-07-11 01:55:33 +04:00
|
|
|
|
2005-01-25 05:17:02 +03:00
|
|
|
|
|
|
|
extern "C" status_t
|
|
|
|
devfs_publish_directory(const char *path)
|
|
|
|
{
|
|
|
|
mutex_lock(&sDeviceFileSystem->lock);
|
|
|
|
|
|
|
|
status_t status = publish_directory(sDeviceFileSystem, path);
|
|
|
|
|
|
|
|
mutex_unlock(&sDeviceFileSystem->lock);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|