1 /*
2 * Copyright 2002-2016, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 *
5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
8
9
10 #include <fs/devfs.h>
11
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/stat.h>
17
18 #include <Drivers.h>
19 #include <KernelExport.h>
20 #include <NodeMonitor.h>
21
22 #include <arch/cpu.h>
23 #include <AutoDeleter.h>
24 #include <boot/kernel_args.h>
25 #include <boot_device.h>
26 #include <debug.h>
27 #include <elf.h>
28 #include <FindDirectory.h>
29 #include <fs/devfs.h>
30 #include <fs/KPath.h>
31 #include <fs/node_monitor.h>
32 #include <kdevice_manager.h>
33 #include <lock.h>
34 #include <Notifications.h>
35 #include <util/AutoLock.h>
36 #include <util/fs_trim_support.h>
37 #include <vfs.h>
38 #include <vm/vm.h>
39 #include <wait_for_objects.h>
40
41 #include "BaseDevice.h"
42 #include "FileDevice.h"
43 #include "IORequest.h"
44 #include "legacy_drivers.h"
45
46
47 //#define TRACE_DEVFS
48 #ifdef TRACE_DEVFS
49 # define TRACE(x) dprintf x
50 #else
51 # define TRACE(x)
52 #endif
53
54
55 namespace {
56
57 struct devfs_partition {
58 struct devfs_vnode* raw_device;
59 partition_info info;
60 };
61
62 struct driver_entry;
63
64 enum {
65 kNotScanned = 0,
66 kBootScan,
67 kNormalScan,
68 };
69
70 struct devfs_stream {
71 mode_t type;
72 union {
73 struct stream_dir {
74 struct devfs_vnode* dir_head;
75 struct list cookies;
76 mutex scan_lock;
77 int32 scanned;
78 } dir;
79 struct stream_dev {
80 BaseDevice* device;
81 struct devfs_partition* partition;
82 } dev;
83 struct stream_symlink {
84 const char* path;
85 size_t length;
86 } symlink;
87 } u;
88 };
89
90 struct devfs_vnode {
91 struct devfs_vnode* all_next;
92 ino_t id;
93 char* name;
94 timespec modification_time;
95 timespec creation_time;
96 uid_t uid;
97 gid_t gid;
98 struct devfs_vnode* parent;
99 struct devfs_vnode* dir_next;
100 struct devfs_stream stream;
101 };
102
103 #define DEVFS_HASH_SIZE 16
104
105
106 struct NodeHash {
107 typedef ino_t KeyType;
108 typedef devfs_vnode ValueType;
109
HashKey__anonee31bddd0111::NodeHash110 size_t HashKey(KeyType key) const
111 {
112 return key ^ (key >> 32);
113 }
114
Hash__anonee31bddd0111::NodeHash115 size_t Hash(ValueType* value) const
116 {
117 return HashKey(value->id);
118 }
119
Compare__anonee31bddd0111::NodeHash120 bool Compare(KeyType key, ValueType* value) const
121 {
122 return value->id == key;
123 }
124
GetLink__anonee31bddd0111::NodeHash125 ValueType*& GetLink(ValueType* value) const
126 {
127 return value->all_next;
128 }
129 };
130
131 typedef BOpenHashTable<NodeHash> NodeTable;
132
133 struct devfs {
134 dev_t id;
135 fs_volume* volume;
136 recursive_lock lock;
137 int32 next_vnode_id;
138 NodeTable* vnode_hash;
139 struct devfs_vnode* root_vnode;
140 };
141
142 struct devfs_dir_cookie {
143 struct list_link link;
144 struct devfs_vnode* current;
145 int32 state; // iteration state
146 };
147
148 struct devfs_cookie {
149 void* device_cookie;
150 };
151
152 // directory iteration states
153 enum {
154 ITERATION_STATE_DOT = 0,
155 ITERATION_STATE_DOT_DOT = 1,
156 ITERATION_STATE_OTHERS = 2,
157 ITERATION_STATE_BEGIN = ITERATION_STATE_DOT,
158 };
159
160 // extern only to make forward declaration possible
161 extern fs_volume_ops kVolumeOps;
162 extern fs_vnode_ops kVnodeOps;
163
164 } // namespace
165
166
167 static status_t get_node_for_path(struct devfs* fs, const char* path,
168 struct devfs_vnode** _node);
169 static void get_device_name(struct devfs_vnode* vnode, char* buffer,
170 size_t size);
171 static status_t unpublish_node(struct devfs* fs, devfs_vnode* node,
172 mode_t type);
173 static status_t publish_device(struct devfs* fs, const char* path,
174 BaseDevice* device);
175
176
177 // The one and only allowed devfs instance
178 static struct devfs* sDeviceFileSystem = NULL;
179
180
181 // #pragma mark - devfs private
182
183
184 static timespec
current_timespec()185 current_timespec()
186 {
187 bigtime_t time = real_time_clock_usecs();
188
189 timespec tv;
190 tv.tv_sec = time / 1000000;
191 tv.tv_nsec = (time % 1000000) * 1000;
192 return tv;
193 }
194
195
196 static ino_t
get_parent_id(struct devfs_vnode * vnode)197 get_parent_id(struct devfs_vnode* vnode)
198 {
199 if (vnode->parent != NULL)
200 return vnode->parent->id;
201 return -1;
202 }
203
204
205 static int32
scan_mode(void)206 scan_mode(void)
207 {
208 // We may scan every device twice:
209 // - once before there is a boot device,
210 // - and once when there is one
211
212 return gBootDevice >= 0 ? kNormalScan : kBootScan;
213 }
214
215
216 static status_t
scan_for_drivers_if_needed(devfs_vnode * dir)217 scan_for_drivers_if_needed(devfs_vnode* dir)
218 {
219 ASSERT(S_ISDIR(dir->stream.type));
220
221 MutexLocker _(dir->stream.u.dir.scan_lock);
222
223 if (dir->stream.u.dir.scanned >= scan_mode())
224 return B_OK;
225
226 KPath path;
227 if (path.InitCheck() != B_OK)
228 return B_NO_MEMORY;
229
230 get_device_name(dir, path.LockBuffer(), path.BufferSize());
231 path.UnlockBuffer();
232
233 TRACE(("scan_for_drivers_if_needed: mode %" B_PRId32 ": %s\n",
234 scan_mode(), path.Path()));
235
236 // scan for drivers at this path
237 static int32 updateCycle = 1;
238 device_manager_probe(path.Path(), updateCycle++);
239 legacy_driver_probe(path.Path());
240
241 dir->stream.u.dir.scanned = scan_mode();
242 return B_OK;
243 }
244
245
246 static void
init_directory_vnode(struct devfs_vnode * vnode,int permissions)247 init_directory_vnode(struct devfs_vnode* vnode, int permissions)
248 {
249 vnode->stream.type = S_IFDIR | permissions;
250 mutex_init(&vnode->stream.u.dir.scan_lock, "devfs scan");
251 vnode->stream.u.dir.dir_head = NULL;
252 list_init(&vnode->stream.u.dir.cookies);
253 }
254
255
256 static struct devfs_vnode*
devfs_create_vnode(struct devfs * fs,devfs_vnode * parent,const char * name)257 devfs_create_vnode(struct devfs* fs, devfs_vnode* parent, const char* name)
258 {
259 struct devfs_vnode* vnode;
260
261 vnode = (struct devfs_vnode*)malloc(sizeof(struct devfs_vnode));
262 if (vnode == NULL)
263 return NULL;
264
265 memset(vnode, 0, sizeof(struct devfs_vnode));
266 vnode->id = fs->next_vnode_id++;
267
268 vnode->name = strdup(name);
269 if (vnode->name == NULL) {
270 free(vnode);
271 return NULL;
272 }
273
274 vnode->creation_time = vnode->modification_time = current_timespec();
275 vnode->uid = geteuid();
276 vnode->gid = parent ? parent->gid : getegid();
277 // inherit group from parent if possible
278
279 return vnode;
280 }
281
282
283 static status_t
devfs_delete_vnode(struct devfs * fs,struct devfs_vnode * vnode,bool forceDelete)284 devfs_delete_vnode(struct devfs* fs, struct devfs_vnode* vnode,
285 bool forceDelete)
286 {
287 // Can't delete it if it's in a directory or is a directory
288 // and has children
289 if (!forceDelete && ((S_ISDIR(vnode->stream.type)
290 && vnode->stream.u.dir.dir_head != NULL)
291 || vnode->dir_next != NULL))
292 return B_NOT_ALLOWED;
293
294 // remove it from the global hash table
295 fs->vnode_hash->Remove(vnode);
296
297 if (S_ISCHR(vnode->stream.type)) {
298 if (vnode->stream.u.dev.partition == NULL) {
299 // pass the call through to the underlying device
300 vnode->stream.u.dev.device->Removed();
301 } else {
302 // for partitions, we have to release the raw device but must
303 // not free the device info as it was inherited from the raw
304 // device and is still in use there
305 put_vnode(fs->volume, vnode->stream.u.dev.partition->raw_device->id);
306 }
307 } else if (S_ISDIR(vnode->stream.type)) {
308 mutex_destroy(&vnode->stream.u.dir.scan_lock);
309 }
310
311 free(vnode->name);
312 free(vnode);
313
314 return B_OK;
315 }
316
317
318 /*! Makes sure none of the dircookies point to the vnode passed in */
319 static void
update_dir_cookies(struct devfs_vnode * dir,struct devfs_vnode * vnode)320 update_dir_cookies(struct devfs_vnode* dir, struct devfs_vnode* vnode)
321 {
322 struct devfs_dir_cookie* cookie = NULL;
323
324 while ((cookie = (devfs_dir_cookie*)list_get_next_item(
325 &dir->stream.u.dir.cookies, cookie)) != NULL) {
326 if (cookie->current == vnode)
327 cookie->current = vnode->dir_next;
328 }
329 }
330
331
332 static struct devfs_vnode*
devfs_find_in_dir(struct devfs_vnode * dir,const char * path)333 devfs_find_in_dir(struct devfs_vnode* dir, const char* path)
334 {
335 struct devfs_vnode* vnode;
336
337 if (!S_ISDIR(dir->stream.type))
338 return NULL;
339
340 if (!strcmp(path, "."))
341 return dir;
342 if (!strcmp(path, ".."))
343 return dir->parent;
344
345 for (vnode = dir->stream.u.dir.dir_head; vnode; vnode = vnode->dir_next) {
346 //TRACE(("devfs_find_in_dir: looking at entry '%s'\n", vnode->name));
347 if (strcmp(vnode->name, path) == 0) {
348 //TRACE(("devfs_find_in_dir: found it at %p\n", vnode));
349 return vnode;
350 }
351 }
352 return NULL;
353 }
354
355
356 static status_t
devfs_insert_in_dir(struct devfs_vnode * dir,struct devfs_vnode * vnode,bool notify=true)357 devfs_insert_in_dir(struct devfs_vnode* dir, struct devfs_vnode* vnode,
358 bool notify = true)
359 {
360 if (!S_ISDIR(dir->stream.type))
361 return B_BAD_VALUE;
362
363 // make sure the directory stays sorted alphabetically
364
365 devfs_vnode* node = dir->stream.u.dir.dir_head;
366 devfs_vnode* last = NULL;
367 while (node && strcmp(node->name, vnode->name) < 0) {
368 last = node;
369 node = node->dir_next;
370 }
371 if (last == NULL) {
372 // the new vnode is the first entry in the list
373 vnode->dir_next = dir->stream.u.dir.dir_head;
374 dir->stream.u.dir.dir_head = vnode;
375 } else {
376 // insert after that node
377 vnode->dir_next = last->dir_next;
378 last->dir_next = vnode;
379 }
380
381 vnode->parent = dir;
382 dir->modification_time = current_timespec();
383
384 if (notify) {
385 notify_entry_created(sDeviceFileSystem->id, dir->id, vnode->name,
386 vnode->id);
387 notify_stat_changed(sDeviceFileSystem->id, get_parent_id(dir), dir->id,
388 B_STAT_MODIFICATION_TIME);
389 }
390 return B_OK;
391 }
392
393
394 static status_t
devfs_remove_from_dir(struct devfs_vnode * dir,struct devfs_vnode * removeNode,bool notify=true)395 devfs_remove_from_dir(struct devfs_vnode* dir, struct devfs_vnode* removeNode,
396 bool notify = true)
397 {
398 struct devfs_vnode* vnode = dir->stream.u.dir.dir_head;
399 struct devfs_vnode* lastNode = NULL;
400
401 for (; vnode != NULL; lastNode = vnode, vnode = vnode->dir_next) {
402 if (vnode == removeNode) {
403 // make sure no dircookies point to this vnode
404 update_dir_cookies(dir, vnode);
405
406 if (lastNode)
407 lastNode->dir_next = vnode->dir_next;
408 else
409 dir->stream.u.dir.dir_head = vnode->dir_next;
410 vnode->dir_next = NULL;
411 dir->modification_time = current_timespec();
412
413 if (notify) {
414 notify_entry_removed(sDeviceFileSystem->id, dir->id, vnode->name,
415 vnode->id);
416 notify_stat_changed(sDeviceFileSystem->id, get_parent_id(dir),
417 dir->id, B_STAT_MODIFICATION_TIME);
418 }
419 return B_OK;
420 }
421 }
422 return B_ENTRY_NOT_FOUND;
423 }
424
425
426 static status_t
add_partition(struct devfs * fs,struct devfs_vnode * device,const char * name,const partition_info & info)427 add_partition(struct devfs* fs, struct devfs_vnode* device, const char* name,
428 const partition_info& info)
429 {
430 struct devfs_vnode* partitionNode;
431 status_t status;
432
433 if (!S_ISCHR(device->stream.type))
434 return B_BAD_VALUE;
435
436 // we don't support nested partitions
437 if (device->stream.u.dev.partition != NULL)
438 return B_BAD_VALUE;
439
440 // reduce checks to a minimum - things like negative offsets could be useful
441 if (info.size < 0)
442 return B_BAD_VALUE;
443
444 // create partition
445 struct devfs_partition* partition = (struct devfs_partition*)malloc(
446 sizeof(struct devfs_partition));
447 if (partition == NULL)
448 return B_NO_MEMORY;
449
450 memcpy(&partition->info, &info, sizeof(partition_info));
451
452 RecursiveLocker locker(fs->lock);
453
454 // you cannot change a partition once set
455 if (devfs_find_in_dir(device->parent, name)) {
456 status = B_BAD_VALUE;
457 goto err1;
458 }
459
460 // increase reference count of raw device -
461 // the partition device really needs it
462 status = get_vnode(fs->volume, device->id, (void**)&partition->raw_device);
463 if (status < B_OK)
464 goto err1;
465
466 // now create the partition vnode
467 partitionNode = devfs_create_vnode(fs, device->parent, name);
468 if (partitionNode == NULL) {
469 status = B_NO_MEMORY;
470 goto err2;
471 }
472
473 partitionNode->stream.type = device->stream.type;
474 partitionNode->stream.u.dev.device = device->stream.u.dev.device;
475 partitionNode->stream.u.dev.partition = partition;
476
477 fs->vnode_hash->Insert(partitionNode);
478 devfs_insert_in_dir(device->parent, partitionNode);
479
480 TRACE(("add_partition(name = %s, offset = %" B_PRIdOFF
481 ", size = %" B_PRIdOFF ")\n",
482 name, info.offset, info.size));
483 return B_OK;
484
485 err2:
486 put_vnode(fs->volume, device->id);
487 err1:
488 free(partition);
489 return status;
490 }
491
492
493 static inline void
translate_partition_access(devfs_partition * partition,off_t & offset,size_t & size)494 translate_partition_access(devfs_partition* partition, off_t& offset,
495 size_t& size)
496 {
497 ASSERT(offset >= 0);
498 ASSERT(offset < partition->info.size);
499
500 size = (size_t)min_c((off_t)size, partition->info.size - offset);
501 offset += partition->info.offset;
502 }
503
504
505 static bool
translate_partition_access(devfs_partition * partition,uint64 & offset,uint64 & size)506 translate_partition_access(devfs_partition* partition, uint64& offset,
507 uint64& size)
508 {
509 const off_t partitionSize = partition->info.size;
510 const off_t partitionOffset = partition->info.offset;
511
512 // Check that off_t values can be cast to uint64,
513 // partition offset can theoretically be negative
514 ASSERT(partitionSize >= 0);
515 STATIC_ASSERT(sizeof(partitionSize) <= sizeof(uint64));
516 STATIC_ASSERT(sizeof(partitionOffset) <= sizeof(uint64));
517
518 // Check that calculations give expected results
519 if (offset >= (uint64)partitionSize)
520 return false;
521 if (partitionOffset >= 0 && offset > UINT64_MAX - (uint64)partitionOffset)
522 return false;
523 if (partitionOffset < 0 && offset < (uint64)-partitionOffset)
524 return false;
525
526 size = min_c(size, (uint64)partitionSize - offset);
527 if (partitionOffset >= 0)
528 offset += (uint64)partitionOffset;
529 else
530 offset -= (uint64)-partitionOffset;
531
532 return true;
533 }
534
535
536 static inline void
translate_partition_access(devfs_partition * partition,io_request * request)537 translate_partition_access(devfs_partition* partition, io_request* request)
538 {
539 off_t offset = request->Offset();
540
541 ASSERT(offset >= 0);
542 ASSERT(offset + (off_t)request->Length() <= partition->info.size);
543
544 request->SetOffset(offset + partition->info.offset);
545 }
546
547
548 static status_t
get_node_for_path(struct devfs * fs,const char * path,struct devfs_vnode ** _node)549 get_node_for_path(struct devfs* fs, const char* path,
550 struct devfs_vnode** _node)
551 {
552 return vfs_get_fs_node_from_path(fs->volume, path, false, true,
553 (void**)_node);
554 }
555
556
557 static status_t
unpublish_node(struct devfs * fs,devfs_vnode * node,mode_t type)558 unpublish_node(struct devfs* fs, devfs_vnode* node, mode_t type)
559 {
560 if ((node->stream.type & S_IFMT) != type)
561 return B_BAD_TYPE;
562
563 recursive_lock_lock(&fs->lock);
564
565 status_t status = devfs_remove_from_dir(node->parent, node);
566 if (status < B_OK)
567 goto out;
568
569 status = remove_vnode(fs->volume, node->id);
570
571 out:
572 recursive_lock_unlock(&fs->lock);
573 return status;
574 }
575
576
577 static void
publish_node(devfs * fs,devfs_vnode * dirNode,struct devfs_vnode * node)578 publish_node(devfs* fs, devfs_vnode* dirNode, struct devfs_vnode* node)
579 {
580 fs->vnode_hash->Insert(node);
581 devfs_insert_in_dir(dirNode, node);
582 }
583
584
585 static status_t
publish_directory(struct devfs * fs,const char * path)586 publish_directory(struct devfs* fs, const char* path)
587 {
588 ASSERT_LOCKED_RECURSIVE(&fs->lock);
589
590 // copy the path over to a temp buffer so we can munge it
591 KPath tempPath(path);
592 if (tempPath.InitCheck() != B_OK)
593 return B_NO_MEMORY;
594
595 TRACE(("devfs: publish directory \"%s\"\n", path));
596 char* temp = tempPath.LockBuffer();
597
598 // create the path leading to the device
599 // parse the path passed in, stripping out '/'
600
601 struct devfs_vnode* dir = fs->root_vnode;
602 struct devfs_vnode* vnode = NULL;
603 status_t status = B_OK;
604 int32 i = 0, last = 0;
605
606 while (temp[last]) {
607 if (temp[i] == '/') {
608 temp[i] = '\0';
609 i++;
610 } else if (temp[i] != '\0') {
611 i++;
612 continue;
613 }
614
615 //TRACE(("\tpath component '%s'\n", &temp[last]));
616
617 // we have a path component
618 vnode = devfs_find_in_dir(dir, &temp[last]);
619 if (vnode) {
620 if (S_ISDIR(vnode->stream.type)) {
621 last = i;
622 dir = vnode;
623 continue;
624 }
625
626 // we hit something on our path that's not a directory
627 status = B_FILE_EXISTS;
628 goto out;
629 } else {
630 vnode = devfs_create_vnode(fs, dir, &temp[last]);
631 if (!vnode) {
632 status = B_NO_MEMORY;
633 goto out;
634 }
635 }
636
637 // set up the new directory
638 init_directory_vnode(vnode, 0755);
639 publish_node(sDeviceFileSystem, dir, vnode);
640
641 last = i;
642 dir = vnode;
643 }
644
645 out:
646 return status;
647 }
648
649
650 static status_t
new_node(struct devfs * fs,const char * path,struct devfs_vnode ** _node,struct devfs_vnode ** _dir)651 new_node(struct devfs* fs, const char* path, struct devfs_vnode** _node,
652 struct devfs_vnode** _dir)
653 {
654 ASSERT_LOCKED_RECURSIVE(&fs->lock);
655
656 // copy the path over to a temp buffer so we can munge it
657 KPath tempPath(path);
658 if (tempPath.InitCheck() != B_OK)
659 return B_NO_MEMORY;
660
661 char* temp = tempPath.LockBuffer();
662
663 // create the path leading to the device
664 // parse the path passed in, stripping out '/'
665
666 struct devfs_vnode* dir = fs->root_vnode;
667 struct devfs_vnode* vnode = NULL;
668 status_t status = B_OK;
669 int32 i = 0, last = 0;
670 bool atLeaf = false;
671
672 for (;;) {
673 if (temp[i] == '\0') {
674 atLeaf = true; // we'll be done after this one
675 } else if (temp[i] == '/') {
676 temp[i] = '\0';
677 i++;
678 } else {
679 i++;
680 continue;
681 }
682
683 //TRACE(("\tpath component '%s'\n", &temp[last]));
684
685 // we have a path component
686 vnode = devfs_find_in_dir(dir, &temp[last]);
687 if (vnode) {
688 if (!atLeaf) {
689 // we are not at the leaf of the path, so as long as
690 // this is a dir we're okay
691 if (S_ISDIR(vnode->stream.type)) {
692 last = i;
693 dir = vnode;
694 continue;
695 }
696 }
697 // we are at the leaf and hit another node
698 // or we aren't but hit a non-dir node.
699 // we're screwed
700 status = B_FILE_EXISTS;
701 goto out;
702 } else {
703 vnode = devfs_create_vnode(fs, dir, &temp[last]);
704 if (!vnode) {
705 status = B_NO_MEMORY;
706 goto out;
707 }
708 }
709
710 // set up the new vnode
711 if (!atLeaf) {
712 // this is a dir
713 init_directory_vnode(vnode, 0755);
714 publish_node(fs, dir, vnode);
715 } else {
716 // this is the last component
717 // Note: We do not yet insert the node into the directory, as it
718 // is not yet fully initialized. Instead we return the directory
719 // vnode so that the calling function can insert it after all
720 // initialization is done. This ensures that no create notification
721 // is sent out for a vnode that is not yet fully valid.
722 *_node = vnode;
723 *_dir = dir;
724 break;
725 }
726
727 last = i;
728 dir = vnode;
729 }
730
731 out:
732 return status;
733 }
734
735
736 static status_t
publish_device(struct devfs * fs,const char * path,BaseDevice * device)737 publish_device(struct devfs* fs, const char* path, BaseDevice* device)
738 {
739 TRACE(("publish_device(path = \"%s\", device = %p)\n", path, device));
740
741 if (sDeviceFileSystem == NULL) {
742 panic("publish_device() called before devfs mounted\n");
743 return B_ERROR;
744 }
745
746 if (device == NULL || path == NULL || path[0] == '\0' || path[0] == '/')
747 return B_BAD_VALUE;
748
749 // TODO: this has to be done in the BaseDevice sub classes!
750 #if 0
751 // are the provided device hooks okay?
752 if (info->device_open == NULL || info->device_close == NULL
753 || info->device_free == NULL
754 || ((info->device_read == NULL || info->device_write == NULL)
755 && info->device_io == NULL))
756 return B_BAD_VALUE;
757 #endif
758
759 struct devfs_vnode* node;
760 struct devfs_vnode* dirNode;
761 status_t status;
762
763 RecursiveLocker locker(&fs->lock);
764
765 status = new_node(fs, path, &node, &dirNode);
766 if (status != B_OK)
767 return status;
768
769 // all went fine, let's initialize the node
770 node->stream.type = S_IFCHR | 0644;
771 node->stream.u.dev.device = device;
772 device->SetID(node->id);
773
774 // the node is now fully valid and we may insert it into the dir
775 publish_node(fs, dirNode, node);
776 return B_OK;
777 }
778
779
780 /*! Construct complete device name (as used for device_open()).
781 This is safe to use only when the device is in use (and therefore
782 cannot be unpublished during the iteration).
783 */
784 static void
get_device_name(struct devfs_vnode * vnode,char * buffer,size_t size)785 get_device_name(struct devfs_vnode* vnode, char* buffer, size_t size)
786 {
787 RecursiveLocker _(sDeviceFileSystem->lock);
788
789 struct devfs_vnode* leaf = vnode;
790 size_t offset = 0;
791
792 // count levels
793
794 for (; vnode->parent && vnode->parent != vnode; vnode = vnode->parent) {
795 offset += strlen(vnode->name) + 1;
796 }
797
798 // construct full path name
799
800 for (vnode = leaf; vnode->parent && vnode->parent != vnode;
801 vnode = vnode->parent) {
802 size_t length = strlen(vnode->name);
803 size_t start = offset - length - 1;
804
805 if (size >= offset) {
806 strcpy(buffer + start, vnode->name);
807 if (vnode != leaf)
808 buffer[offset - 1] = '/';
809 }
810
811 offset = start;
812 }
813 }
814
815
816 static int
dump_node(int argc,char ** argv)817 dump_node(int argc, char** argv)
818 {
819 if (argc != 2) {
820 print_debugger_command_usage(argv[0]);
821 return 0;
822 }
823
824 struct devfs_vnode* vnode = (struct devfs_vnode*)parse_expression(argv[1]);
825 if (vnode == NULL) {
826 kprintf("invalid node address\n");
827 return 0;
828 }
829
830 kprintf("DEVFS NODE: %p\n", vnode);
831 kprintf(" id: %" B_PRIdINO "\n", vnode->id);
832 kprintf(" name: \"%s\"\n", vnode->name);
833 kprintf(" type: %x\n", vnode->stream.type);
834 kprintf(" parent: %p\n", vnode->parent);
835 kprintf(" dir next: %p\n", vnode->dir_next);
836
837 if (S_ISDIR(vnode->stream.type)) {
838 kprintf(" dir scanned: %" B_PRId32 "\n", vnode->stream.u.dir.scanned);
839 kprintf(" contents:\n");
840
841 devfs_vnode* children = vnode->stream.u.dir.dir_head;
842 while (children != NULL) {
843 kprintf(" %p, id %" B_PRIdINO "\n", children, children->id);
844 children = children->dir_next;
845 }
846 } else if (S_ISLNK(vnode->stream.type)) {
847 kprintf(" symlink to: %s\n", vnode->stream.u.symlink.path);
848 } else {
849 kprintf(" device: %p\n", vnode->stream.u.dev.device);
850 kprintf(" partition: %p\n", vnode->stream.u.dev.partition);
851 if (vnode->stream.u.dev.partition != NULL) {
852 partition_info& info = vnode->stream.u.dev.partition->info;
853 kprintf(" raw device node: %p\n",
854 vnode->stream.u.dev.partition->raw_device);
855 kprintf(" offset: %" B_PRIdOFF "\n", info.offset);
856 kprintf(" size: %" B_PRIdOFF "\n", info.size);
857 kprintf(" block size: %" B_PRId32 "\n", info.logical_block_size);
858 kprintf(" session: %" B_PRId32 "\n", info.session);
859 kprintf(" partition: %" B_PRId32 "\n", info.partition);
860 kprintf(" device: %s\n", info.device);
861 set_debug_variable("_raw",
862 (addr_t)vnode->stream.u.dev.partition->raw_device);
863 }
864 }
865
866 return 0;
867 }
868
869
870 static int
dump_cookie(int argc,char ** argv)871 dump_cookie(int argc, char** argv)
872 {
873 if (argc != 2) {
874 print_debugger_command_usage(argv[0]);
875 return 0;
876 }
877
878 uint64 address;
879 if (!evaluate_debug_expression(argv[1], &address, false))
880 return 0;
881
882 struct devfs_cookie* cookie = (devfs_cookie*)(addr_t)address;
883
884 kprintf("DEVFS COOKIE: %p\n", cookie);
885 kprintf(" device_cookie: %p\n", cookie->device_cookie);
886
887 return 0;
888 }
889
890
891 // #pragma mark - file system interface
892
893
894 static status_t
devfs_mount(fs_volume * volume,const char * devfs,uint32 flags,const char * args,ino_t * _rootNodeID)895 devfs_mount(fs_volume* volume, const char* devfs, uint32 flags,
896 const char* args, ino_t* _rootNodeID)
897 {
898 struct devfs_vnode* vnode;
899 struct devfs* fs;
900 status_t err;
901
902 TRACE(("devfs_mount: entry\n"));
903
904 if (sDeviceFileSystem) {
905 TRACE(("double mount of devfs attempted\n"));
906 err = B_ERROR;
907 goto err;
908 }
909
910 fs = (struct devfs*)malloc(sizeof(struct devfs));
911 if (fs == NULL) {
912 err = B_NO_MEMORY;
913 goto err;
914 }
915
916 volume->private_volume = fs;
917 volume->ops = &kVolumeOps;
918 fs->volume = volume;
919 fs->id = volume->id;
920 fs->next_vnode_id = 0;
921
922 recursive_lock_init(&fs->lock, "devfs lock");
923
924 fs->vnode_hash = new(std::nothrow) NodeTable();
925 if (fs->vnode_hash == NULL || fs->vnode_hash->Init(DEVFS_HASH_SIZE) != B_OK) {
926 err = B_NO_MEMORY;
927 goto err2;
928 }
929
930 // create a vnode
931 vnode = devfs_create_vnode(fs, NULL, "");
932 if (vnode == NULL) {
933 err = B_NO_MEMORY;
934 goto err3;
935 }
936
937 // set it up
938 vnode->parent = vnode;
939
940 // create a dir stream for it to hold
941 init_directory_vnode(vnode, 0755);
942 fs->root_vnode = vnode;
943
944 fs->vnode_hash->Insert(vnode);
945 publish_vnode(volume, vnode->id, vnode, &kVnodeOps, vnode->stream.type, 0);
946
947 *_rootNodeID = vnode->id;
948 sDeviceFileSystem = fs;
949 return B_OK;
950
951 err3:
952 delete fs->vnode_hash;
953 err2:
954 recursive_lock_destroy(&fs->lock);
955 free(fs);
956 err:
957 return err;
958 }
959
960
961 static status_t
devfs_unmount(fs_volume * _volume)962 devfs_unmount(fs_volume* _volume)
963 {
964 struct devfs* fs = (struct devfs*)_volume->private_volume;
965 struct devfs_vnode* vnode;
966
967 TRACE(("devfs_unmount: entry fs = %p\n", fs));
968
969 recursive_lock_lock(&fs->lock);
970
971 // release the reference to the root
972 put_vnode(fs->volume, fs->root_vnode->id);
973
974 // delete all of the vnodes
975 NodeTable::Iterator i(fs->vnode_hash);
976 while (i.HasNext()) {
977 vnode = i.Next();
978 devfs_delete_vnode(fs, vnode, true);
979 }
980 delete fs->vnode_hash;
981
982 recursive_lock_destroy(&fs->lock);
983 free(fs);
984
985 return B_OK;
986 }
987
988
989 static status_t
devfs_sync(fs_volume * _volume)990 devfs_sync(fs_volume* _volume)
991 {
992 TRACE(("devfs_sync: entry\n"));
993
994 return B_OK;
995 }
996
997
998 static status_t
devfs_lookup(fs_volume * _volume,fs_vnode * _dir,const char * name,ino_t * _id)999 devfs_lookup(fs_volume* _volume, fs_vnode* _dir, const char* name, ino_t* _id)
1000 {
1001 struct devfs* fs = (struct devfs*)_volume->private_volume;
1002 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node;
1003 struct devfs_vnode* vnode;
1004 status_t status;
1005
1006 TRACE(("devfs_lookup: entry dir %p, name '%s'\n", dir, name));
1007
1008 if (!S_ISDIR(dir->stream.type))
1009 return B_NOT_A_DIRECTORY;
1010
1011 // Make sure the directory contents are up to date
1012 scan_for_drivers_if_needed(dir);
1013
1014 RecursiveLocker locker(&fs->lock);
1015
1016 // look it up
1017 vnode = devfs_find_in_dir(dir, name);
1018 if (vnode == NULL) {
1019 // We don't have to rescan here, because thanks to node monitoring
1020 // we already know it does not exist
1021 return B_ENTRY_NOT_FOUND;
1022 }
1023
1024 status = get_vnode(fs->volume, vnode->id, NULL);
1025 if (status < B_OK)
1026 return status;
1027
1028 *_id = vnode->id;
1029
1030 return B_OK;
1031 }
1032
1033
1034 static status_t
devfs_get_vnode_name(fs_volume * _volume,fs_vnode * _vnode,char * buffer,size_t bufferSize)1035 devfs_get_vnode_name(fs_volume* _volume, fs_vnode* _vnode, char* buffer,
1036 size_t bufferSize)
1037 {
1038 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1039
1040 TRACE(("devfs_get_vnode_name: vnode = %p\n", vnode));
1041
1042 strlcpy(buffer, vnode->name, bufferSize);
1043 return B_OK;
1044 }
1045
1046
1047 static status_t
devfs_get_vnode(fs_volume * _volume,ino_t id,fs_vnode * _vnode,int * _type,uint32 * _flags,bool reenter)1048 devfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _vnode, int* _type,
1049 uint32* _flags, bool reenter)
1050 {
1051 struct devfs* fs = (struct devfs*)_volume->private_volume;
1052
1053 TRACE(("devfs_get_vnode: asking for vnode id = %" B_PRIdINO
1054 ", vnode = %p, r %d\n", id, _vnode, reenter));
1055
1056 RecursiveLocker _(fs->lock);
1057
1058 struct devfs_vnode* vnode = fs->vnode_hash->Lookup(id);
1059 if (vnode == NULL)
1060 return B_ENTRY_NOT_FOUND;
1061
1062 TRACE(("devfs_get_vnode: looked it up at %p\n", vnode));
1063
1064 _vnode->private_node = vnode;
1065 _vnode->ops = &kVnodeOps;
1066 *_type = vnode->stream.type;
1067 *_flags = 0;
1068 return B_OK;
1069 }
1070
1071
1072 static status_t
devfs_put_vnode(fs_volume * _volume,fs_vnode * _vnode,bool reenter)1073 devfs_put_vnode(fs_volume* _volume, fs_vnode* _vnode, bool reenter)
1074 {
1075 #ifdef TRACE_DEVFS
1076 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1077
1078 TRACE(("devfs_put_vnode: entry on vnode %p, id = %" B_PRIdINO
1079 ", reenter %d\n", vnode, vnode->id, reenter));
1080 #endif
1081
1082 return B_OK;
1083 }
1084
1085
1086 static status_t
devfs_remove_vnode(fs_volume * _volume,fs_vnode * _v,bool reenter)1087 devfs_remove_vnode(fs_volume* _volume, fs_vnode* _v, bool reenter)
1088 {
1089 struct devfs* fs = (struct devfs*)_volume->private_volume;
1090 struct devfs_vnode* vnode = (struct devfs_vnode*)_v->private_node;
1091
1092 TRACE(("devfs_removevnode: remove %p (%" B_PRIdINO "), reenter %d\n",
1093 vnode, vnode->id, reenter));
1094
1095 RecursiveLocker locker(&fs->lock);
1096
1097 if (vnode->dir_next) {
1098 // can't remove node if it's linked to the dir
1099 panic("devfs_removevnode: vnode %p asked to be removed is present in dir\n", vnode);
1100 }
1101
1102 devfs_delete_vnode(fs, vnode, false);
1103
1104 return B_OK;
1105 }
1106
1107
1108 static status_t
devfs_open(fs_volume * _volume,fs_vnode * _vnode,int openMode,void ** _cookie)1109 devfs_open(fs_volume* _volume, fs_vnode* _vnode, int openMode,
1110 void** _cookie)
1111 {
1112 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1113 struct devfs_cookie* cookie;
1114 status_t status = B_OK;
1115
1116 cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie));
1117 if (cookie == NULL)
1118 return B_NO_MEMORY;
1119
1120 TRACE(("devfs_open: vnode %p, openMode 0x%x, cookie %p\n", vnode, openMode,
1121 cookie));
1122
1123 cookie->device_cookie = NULL;
1124
1125 if (S_ISCHR(vnode->stream.type)) {
1126 BaseDevice* device = vnode->stream.u.dev.device;
1127 status = device->InitDevice();
1128 if (status != B_OK) {
1129 free(cookie);
1130 return status;
1131 }
1132
1133 char path[B_FILE_NAME_LENGTH];
1134 get_device_name(vnode, path, sizeof(path));
1135
1136 status = device->Open(path, openMode, &cookie->device_cookie);
1137 if (status != B_OK)
1138 device->UninitDevice();
1139 }
1140
1141 if (status != B_OK)
1142 free(cookie);
1143 else
1144 *_cookie = cookie;
1145
1146 return status;
1147 }
1148
1149
1150 static status_t
devfs_close(fs_volume * _volume,fs_vnode * _vnode,void * _cookie)1151 devfs_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1152 {
1153 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1154 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1155
1156 TRACE(("devfs_close: entry vnode %p, cookie %p\n", vnode, cookie));
1157
1158 if (S_ISCHR(vnode->stream.type)) {
1159 // pass the call through to the underlying device
1160 return vnode->stream.u.dev.device->Close(cookie->device_cookie);
1161 }
1162
1163 return B_OK;
1164 }
1165
1166
1167 static status_t
devfs_free_cookie(fs_volume * _volume,fs_vnode * _vnode,void * _cookie)1168 devfs_free_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1169 {
1170 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1171 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1172
1173 TRACE(("devfs_freecookie: entry vnode %p, cookie %p\n", vnode, cookie));
1174
1175 if (S_ISCHR(vnode->stream.type)) {
1176 // pass the call through to the underlying device
1177 vnode->stream.u.dev.device->Free(cookie->device_cookie);
1178 vnode->stream.u.dev.device->UninitDevice();
1179 }
1180
1181 free(cookie);
1182 return B_OK;
1183 }
1184
1185
1186 static status_t
devfs_fsync(fs_volume * _volume,fs_vnode * _v)1187 devfs_fsync(fs_volume* _volume, fs_vnode* _v)
1188 {
1189 return B_OK;
1190 }
1191
1192
1193 static status_t
devfs_read_link(fs_volume * _volume,fs_vnode * _link,char * buffer,size_t * _bufferSize)1194 devfs_read_link(fs_volume* _volume, fs_vnode* _link, char* buffer,
1195 size_t* _bufferSize)
1196 {
1197 struct devfs_vnode* link = (struct devfs_vnode*)_link->private_node;
1198
1199 if (!S_ISLNK(link->stream.type))
1200 return B_BAD_VALUE;
1201
1202 memcpy(buffer, link->stream.u.symlink.path, min_c(*_bufferSize,
1203 link->stream.u.symlink.length));
1204
1205 *_bufferSize = link->stream.u.symlink.length;
1206
1207 return B_OK;
1208 }
1209
1210
1211 static status_t
devfs_read(fs_volume * _volume,fs_vnode * _vnode,void * _cookie,off_t pos,void * buffer,size_t * _length)1212 devfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos,
1213 void* buffer, size_t* _length)
1214 {
1215 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1216 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1217
1218 //TRACE(("devfs_read: vnode %p, cookie %p, pos %lld, len %p\n",
1219 // vnode, cookie, pos, _length));
1220
1221 if (!S_ISCHR(vnode->stream.type))
1222 return B_BAD_VALUE;
1223
1224 if (pos < 0)
1225 return B_BAD_VALUE;
1226
1227 if (vnode->stream.u.dev.partition != NULL) {
1228 if (pos >= vnode->stream.u.dev.partition->info.size)
1229 return B_BAD_VALUE;
1230
1231 translate_partition_access(vnode->stream.u.dev.partition, pos,
1232 *_length);
1233 }
1234
1235 if (*_length == 0)
1236 return B_OK;
1237
1238 // pass the call through to the device
1239 return vnode->stream.u.dev.device->Read(cookie->device_cookie, pos, buffer,
1240 _length);
1241 }
1242
1243
1244 static status_t
devfs_write(fs_volume * _volume,fs_vnode * _vnode,void * _cookie,off_t pos,const void * buffer,size_t * _length)1245 devfs_write(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos,
1246 const void* buffer, size_t* _length)
1247 {
1248 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1249 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1250
1251 //TRACE(("devfs_write: vnode %p, cookie %p, pos %lld, len %p\n",
1252 // vnode, cookie, pos, _length));
1253
1254 if (!S_ISCHR(vnode->stream.type))
1255 return B_BAD_VALUE;
1256
1257 if (pos < 0)
1258 return B_BAD_VALUE;
1259
1260 if (vnode->stream.u.dev.partition != NULL) {
1261 if (pos >= vnode->stream.u.dev.partition->info.size)
1262 return B_BAD_VALUE;
1263
1264 translate_partition_access(vnode->stream.u.dev.partition, pos,
1265 *_length);
1266 }
1267
1268 if (*_length == 0)
1269 return B_OK;
1270
1271 return vnode->stream.u.dev.device->Write(cookie->device_cookie, pos, buffer,
1272 _length);
1273 }
1274
1275
1276 static status_t
devfs_create_dir(fs_volume * _volume,fs_vnode * _dir,const char * name,int perms)1277 devfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name,
1278 int perms)
1279 {
1280 struct devfs* fs = (struct devfs*)_volume->private_volume;
1281 struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node;
1282
1283 struct devfs_vnode* vnode = devfs_find_in_dir(dir, name);
1284 if (vnode != NULL) {
1285 return EEXIST;
1286 }
1287
1288 vnode = devfs_create_vnode(fs, dir, name);
1289 if (vnode == NULL) {
1290 return B_NO_MEMORY;
1291 }
1292
1293 // set up the new directory
1294 init_directory_vnode(vnode, perms);
1295 publish_node(sDeviceFileSystem, dir, vnode);
1296
1297 return B_OK;
1298 }
1299
1300
1301 static status_t
devfs_open_dir(fs_volume * _volume,fs_vnode * _vnode,void ** _cookie)1302 devfs_open_dir(fs_volume* _volume, fs_vnode* _vnode, void** _cookie)
1303 {
1304 struct devfs* fs = (struct devfs*)_volume->private_volume;
1305 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1306 struct devfs_dir_cookie* cookie;
1307
1308 TRACE(("devfs_open_dir: vnode %p\n", vnode));
1309
1310 if (!S_ISDIR(vnode->stream.type))
1311 return B_BAD_VALUE;
1312
1313 cookie = (devfs_dir_cookie*)malloc(sizeof(devfs_dir_cookie));
1314 if (cookie == NULL)
1315 return B_NO_MEMORY;
1316
1317 // make sure the directory has up-to-date contents
1318 scan_for_drivers_if_needed(vnode);
1319
1320 RecursiveLocker locker(&fs->lock);
1321
1322 cookie->current = vnode->stream.u.dir.dir_head;
1323 cookie->state = ITERATION_STATE_BEGIN;
1324
1325 list_add_item(&vnode->stream.u.dir.cookies, cookie);
1326 *_cookie = cookie;
1327
1328 return B_OK;
1329 }
1330
1331
1332 static status_t
devfs_free_dir_cookie(fs_volume * _volume,fs_vnode * _vnode,void * _cookie)1333 devfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1334 {
1335 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1336 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie;
1337 struct devfs* fs = (struct devfs*)_volume->private_volume;
1338
1339 TRACE(("devfs_free_dir_cookie: entry vnode %p, cookie %p\n", vnode, cookie));
1340
1341 RecursiveLocker locker(&fs->lock);
1342
1343 list_remove_item(&vnode->stream.u.dir.cookies, cookie);
1344 free(cookie);
1345 return B_OK;
1346 }
1347
1348
1349 static status_t
devfs_read_dir(fs_volume * _volume,fs_vnode * _vnode,void * _cookie,struct dirent * dirent,size_t bufferSize,uint32 * _num)1350 devfs_read_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1351 struct dirent* dirent, size_t bufferSize, uint32* _num)
1352 {
1353 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1354 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie;
1355 struct devfs* fs = (struct devfs*)_volume->private_volume;
1356 status_t status = B_OK;
1357 struct devfs_vnode* childNode = NULL;
1358 const char* name = NULL;
1359 struct devfs_vnode* nextChildNode = NULL;
1360 int32 nextState = cookie->state;
1361
1362 TRACE(("devfs_read_dir: vnode %p, cookie %p, buffer %p, size %ld\n",
1363 _vnode, cookie, dirent, bufferSize));
1364
1365 if (!S_ISDIR(vnode->stream.type))
1366 return B_BAD_VALUE;
1367
1368 RecursiveLocker locker(&fs->lock);
1369
1370 switch (cookie->state) {
1371 case ITERATION_STATE_DOT:
1372 childNode = vnode;
1373 name = ".";
1374 nextChildNode = vnode->stream.u.dir.dir_head;
1375 nextState = cookie->state + 1;
1376 break;
1377 case ITERATION_STATE_DOT_DOT:
1378 childNode = vnode->parent;
1379 name = "..";
1380 nextChildNode = vnode->stream.u.dir.dir_head;
1381 nextState = cookie->state + 1;
1382 break;
1383 default:
1384 childNode = cookie->current;
1385 if (childNode) {
1386 name = childNode->name;
1387 nextChildNode = childNode->dir_next;
1388 }
1389 break;
1390 }
1391
1392 if (!childNode) {
1393 *_num = 0;
1394 return B_OK;
1395 }
1396
1397 dirent->d_dev = fs->id;
1398 dirent->d_ino = childNode->id;
1399 dirent->d_reclen = offsetof(struct dirent, d_name) + strlen(name) + 1;
1400
1401 if (dirent->d_reclen > bufferSize)
1402 return ENOBUFS;
1403
1404 status = user_strlcpy(dirent->d_name, name,
1405 bufferSize - offsetof(struct dirent, d_name));
1406 if (status < B_OK)
1407 return status;
1408
1409 cookie->current = nextChildNode;
1410 cookie->state = nextState;
1411 *_num = 1;
1412
1413 return B_OK;
1414 }
1415
1416
1417 static status_t
devfs_rewind_dir(fs_volume * _volume,fs_vnode * _vnode,void * _cookie)1418 devfs_rewind_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
1419 {
1420 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1421 struct devfs_dir_cookie* cookie = (devfs_dir_cookie*)_cookie;
1422 struct devfs* fs = (struct devfs*)_volume->private_volume;
1423
1424 TRACE(("devfs_rewind_dir: vnode %p, cookie %p\n", vnode, cookie));
1425
1426 if (!S_ISDIR(vnode->stream.type))
1427 return B_BAD_VALUE;
1428
1429 RecursiveLocker locker(&fs->lock);
1430
1431 cookie->current = vnode->stream.u.dir.dir_head;
1432 cookie->state = ITERATION_STATE_BEGIN;
1433
1434 return B_OK;
1435 }
1436
1437
1438 /*! Forwards the opcode to the device driver, but also handles some devfs
1439 specific functionality, like partitions.
1440 */
1441 static status_t
devfs_ioctl(fs_volume * _volume,fs_vnode * _vnode,void * _cookie,uint32 op,void * buffer,size_t length)1442 devfs_ioctl(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, uint32 op,
1443 void* buffer, size_t length)
1444 {
1445 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1446 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1447
1448 TRACE(("devfs_ioctl: vnode %p, cookie %p, op %" B_PRIu32
1449 ", buf %p, len %" B_PRIuSIZE "\n",
1450 vnode, cookie, op, buffer, length));
1451
1452 // we are actually checking for a *device* here, we don't make the
1453 // distinction between char and block devices
1454 if (S_ISCHR(vnode->stream.type)) {
1455 switch (op) {
1456 case B_GET_GEOMETRY:
1457 {
1458 struct devfs_partition* partition
1459 = vnode->stream.u.dev.partition;
1460 if (partition == NULL)
1461 break;
1462
1463 device_geometry geometry;
1464 status_t status = vnode->stream.u.dev.device->Control(
1465 cookie->device_cookie, op, &geometry, length);
1466 if (status != B_OK)
1467 return status;
1468
1469 // patch values to match partition size
1470 if (geometry.bytes_per_sector == 0)
1471 geometry.bytes_per_sector = 512;
1472
1473 devfs_compute_geometry_size(&geometry,
1474 partition->info.size / geometry.bytes_per_sector,
1475 geometry.bytes_per_sector);
1476
1477 return user_memcpy(buffer, &geometry, sizeof(device_geometry));
1478 }
1479
1480 case B_TRIM_DEVICE:
1481 {
1482 struct devfs_partition* partition
1483 = vnode->stream.u.dev.partition;
1484
1485 fs_trim_data* trimData;
1486 MemoryDeleter deleter;
1487 status_t status = get_trim_data_from_user(buffer, length,
1488 deleter, trimData);
1489 if (status != B_OK)
1490 return status;
1491
1492 #ifdef DEBUG_TRIM
1493 dprintf("TRIM: devfs: received TRIM ranges (bytes):\n");
1494 for (uint32 i = 0; i < trimData->range_count; i++) {
1495 dprintf("[%3" B_PRIu32 "] %" B_PRIu64 " : %"
1496 B_PRIu64 "\n", i,
1497 trimData->ranges[i].offset,
1498 trimData->ranges[i].size);
1499 }
1500 #endif
1501
1502 if (partition != NULL) {
1503 // If there is a partition, offset all ranges according
1504 // to the partition start.
1505 // Range size may be reduced to fit the partition size.
1506 for (uint32 i = 0; i < trimData->range_count; i++) {
1507 if (!translate_partition_access(partition,
1508 trimData->ranges[i].offset,
1509 trimData->ranges[i].size)) {
1510 return B_BAD_VALUE;
1511 }
1512 }
1513
1514 #ifdef DEBUG_TRIM
1515 dprintf("TRIM: devfs: TRIM ranges after partition"
1516 " translation (bytes):\n");
1517 for (uint32 i = 0; i < trimData->range_count; i++) {
1518 dprintf("[%3" B_PRIu32 "] %" B_PRIu64 " : %"
1519 B_PRIu64 "\n", i,
1520 trimData->ranges[i].offset,
1521 trimData->ranges[i].size);
1522 }
1523 #endif
1524 }
1525
1526 status = vnode->stream.u.dev.device->Control(
1527 cookie->device_cookie, op, trimData, length);
1528
1529 // Copy the data back to userland (it contains the number of
1530 // trimmed bytes)
1531 if (status == B_OK)
1532 status = copy_trim_data_to_user(buffer, trimData);
1533
1534 return status;
1535 }
1536
1537 case B_GET_PARTITION_INFO:
1538 {
1539 struct devfs_partition* partition
1540 = vnode->stream.u.dev.partition;
1541 if (!S_ISCHR(vnode->stream.type)
1542 || partition == NULL
1543 || length != sizeof(partition_info))
1544 return B_BAD_VALUE;
1545
1546 return user_memcpy(buffer, &partition->info,
1547 sizeof(partition_info));
1548 }
1549
1550 case B_SET_PARTITION:
1551 return B_NOT_ALLOWED;
1552
1553 case B_GET_PATH_FOR_DEVICE:
1554 {
1555 char path[256];
1556 // TODO: we might want to actually find the mountpoint
1557 // of that instance of devfs...
1558 // but for now we assume it's mounted on /dev
1559 strcpy(path, "/dev/");
1560 get_device_name(vnode, path + 5, sizeof(path) - 5);
1561 if (length && (length <= strlen(path)))
1562 return ERANGE;
1563 return user_strlcpy((char*)buffer, path, sizeof(path));
1564 }
1565
1566 // old unsupported R5 private stuff
1567
1568 case B_GET_NEXT_OPEN_DEVICE:
1569 dprintf("devfs: unsupported legacy ioctl B_GET_NEXT_OPEN_DEVICE\n");
1570 return B_UNSUPPORTED;
1571 case B_ADD_FIXED_DRIVER:
1572 dprintf("devfs: unsupported legacy ioctl B_ADD_FIXED_DRIVER\n");
1573 return B_UNSUPPORTED;
1574 case B_REMOVE_FIXED_DRIVER:
1575 dprintf("devfs: unsupported legacy ioctl B_REMOVE_FIXED_DRIVER\n");
1576 return B_UNSUPPORTED;
1577
1578 }
1579
1580 return vnode->stream.u.dev.device->Control(cookie->device_cookie,
1581 op, buffer, length);
1582 }
1583
1584 return B_BAD_VALUE;
1585 }
1586
1587
1588 static status_t
devfs_set_flags(fs_volume * _volume,fs_vnode * _vnode,void * _cookie,int flags)1589 devfs_set_flags(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1590 int flags)
1591 {
1592 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1593 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1594
1595 // we need to pass the O_NONBLOCK flag to the underlying device
1596
1597 if (!S_ISCHR(vnode->stream.type))
1598 return B_NOT_ALLOWED;
1599
1600 return vnode->stream.u.dev.device->Control(cookie->device_cookie,
1601 flags & O_NONBLOCK ? B_SET_NONBLOCKING_IO : B_SET_BLOCKING_IO, NULL, 0);
1602 }
1603
1604
1605 static status_t
devfs_select(fs_volume * _volume,fs_vnode * _vnode,void * _cookie,uint8 event,selectsync * sync)1606 devfs_select(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1607 uint8 event, selectsync* sync)
1608 {
1609 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1610 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1611
1612 if (!S_ISCHR(vnode->stream.type))
1613 return B_NOT_ALLOWED;
1614
1615 // If the device has no select() hook, notify select() now.
1616 if (!vnode->stream.u.dev.device->HasSelect()) {
1617 if (!SELECT_TYPE_IS_OUTPUT_ONLY(event))
1618 notify_select_event((selectsync*)sync, event);
1619 return B_UNSUPPORTED;
1620 }
1621
1622 return vnode->stream.u.dev.device->Select(cookie->device_cookie, event,
1623 (selectsync*)sync);
1624 }
1625
1626
1627 static status_t
devfs_deselect(fs_volume * _volume,fs_vnode * _vnode,void * _cookie,uint8 event,selectsync * sync)1628 devfs_deselect(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1629 uint8 event, selectsync* sync)
1630 {
1631 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1632 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1633
1634 if (!S_ISCHR(vnode->stream.type))
1635 return B_NOT_ALLOWED;
1636
1637 if (!vnode->stream.u.dev.device->HasDeselect())
1638 return B_OK;
1639
1640 return vnode->stream.u.dev.device->Deselect(cookie->device_cookie, event,
1641 (selectsync*)sync);
1642 }
1643
1644
1645 static bool
devfs_can_page(fs_volume * _volume,fs_vnode * _vnode,void * cookie)1646 devfs_can_page(fs_volume* _volume, fs_vnode* _vnode, void* cookie)
1647 {
1648 #if 0
1649 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1650
1651 //TRACE(("devfs_canpage: vnode %p\n", vnode));
1652
1653 if (!S_ISCHR(vnode->stream.type)
1654 || vnode->stream.u.dev.device->Node() == NULL
1655 || cookie == NULL)
1656 return false;
1657
1658 return vnode->stream.u.dev.device->HasRead()
1659 || vnode->stream.u.dev.device->HasIO();
1660 #endif
1661 // TODO: Obsolete hook!
1662 return false;
1663 }
1664
1665
1666 static status_t
devfs_read_pages(fs_volume * _volume,fs_vnode * _vnode,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)1667 devfs_read_pages(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1668 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
1669 {
1670 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1671 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1672
1673 //TRACE(("devfs_read_pages: vnode %p, vecs %p, count = %lu, pos = %lld, size = %lu\n", vnode, vecs, count, pos, *_numBytes));
1674
1675 if (!S_ISCHR(vnode->stream.type)
1676 || (!vnode->stream.u.dev.device->HasRead()
1677 && !vnode->stream.u.dev.device->HasIO())
1678 || cookie == NULL)
1679 return B_NOT_ALLOWED;
1680
1681 if (pos < 0)
1682 return B_BAD_VALUE;
1683
1684 if (vnode->stream.u.dev.partition != NULL) {
1685 if (pos >= vnode->stream.u.dev.partition->info.size)
1686 return B_BAD_VALUE;
1687
1688 translate_partition_access(vnode->stream.u.dev.partition, pos,
1689 *_numBytes);
1690 }
1691
1692 if (vnode->stream.u.dev.device->HasIO()) {
1693 // TODO: use io_requests for this!
1694 }
1695
1696 // emulate read_pages() using read()
1697
1698 status_t error = B_OK;
1699 size_t bytesTransferred = 0;
1700
1701 size_t remainingBytes = *_numBytes;
1702 for (size_t i = 0; i < count && remainingBytes > 0; i++) {
1703 size_t toRead = min_c(vecs[i].iov_len, remainingBytes);
1704 size_t length = toRead;
1705
1706 error = vnode->stream.u.dev.device->Read(cookie->device_cookie, pos,
1707 vecs[i].iov_base, &length);
1708 if (error != B_OK)
1709 break;
1710
1711 pos += length;
1712 bytesTransferred += length;
1713 remainingBytes -= length;
1714
1715 if (length < toRead)
1716 break;
1717 }
1718
1719 *_numBytes = bytesTransferred;
1720
1721 return bytesTransferred > 0 ? B_OK : error;
1722 }
1723
1724
1725 static status_t
devfs_write_pages(fs_volume * _volume,fs_vnode * _vnode,void * _cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)1726 devfs_write_pages(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
1727 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
1728 {
1729 struct devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1730 struct devfs_cookie* cookie = (struct devfs_cookie*)_cookie;
1731
1732 //TRACE(("devfs_write_pages: vnode %p, vecs %p, count = %lu, pos = %lld, size = %lu\n", vnode, vecs, count, pos, *_numBytes));
1733
1734 if (!S_ISCHR(vnode->stream.type)
1735 || (!vnode->stream.u.dev.device->HasWrite()
1736 && !vnode->stream.u.dev.device->HasIO())
1737 || cookie == NULL)
1738 return B_NOT_ALLOWED;
1739
1740 if (pos < 0)
1741 return B_BAD_VALUE;
1742
1743 if (vnode->stream.u.dev.partition != NULL) {
1744 if (pos >= vnode->stream.u.dev.partition->info.size)
1745 return B_BAD_VALUE;
1746
1747 translate_partition_access(vnode->stream.u.dev.partition, pos,
1748 *_numBytes);
1749 }
1750
1751 if (vnode->stream.u.dev.device->HasIO()) {
1752 // TODO: use io_requests for this!
1753 }
1754
1755 // emulate write_pages() using write()
1756
1757 status_t error = B_OK;
1758 size_t bytesTransferred = 0;
1759
1760 size_t remainingBytes = *_numBytes;
1761 for (size_t i = 0; i < count && remainingBytes > 0; i++) {
1762 size_t toWrite = min_c(vecs[i].iov_len, remainingBytes);
1763 size_t length = toWrite;
1764
1765 error = vnode->stream.u.dev.device->Write(cookie->device_cookie, pos,
1766 vecs[i].iov_base, &length);
1767 if (error != B_OK)
1768 break;
1769
1770 pos += length;
1771 bytesTransferred += length;
1772 remainingBytes -= length;
1773
1774 if (length < toWrite)
1775 break;
1776 }
1777
1778 *_numBytes = bytesTransferred;
1779
1780 return bytesTransferred > 0 ? B_OK : error;
1781 }
1782
1783
1784 static status_t
devfs_io(fs_volume * volume,fs_vnode * _vnode,void * _cookie,io_request * request)1785 devfs_io(fs_volume* volume, fs_vnode* _vnode, void* _cookie,
1786 io_request* request)
1787 {
1788 TRACE(("[%d] devfs_io(request: %p)\n", find_thread(NULL), request));
1789
1790 devfs_vnode* vnode = (devfs_vnode*)_vnode->private_node;
1791 devfs_cookie* cookie = (devfs_cookie*)_cookie;
1792
1793 if (!S_ISCHR(vnode->stream.type) || cookie == NULL) {
1794 request->SetStatusAndNotify(B_NOT_ALLOWED);
1795 return B_NOT_ALLOWED;
1796 }
1797
1798 if (!vnode->stream.u.dev.device->HasIO())
1799 return B_UNSUPPORTED;
1800
1801 if (vnode->stream.u.dev.partition != NULL) {
1802 if (request->Offset() + (off_t)request->Length()
1803 > vnode->stream.u.dev.partition->info.size) {
1804 request->SetStatusAndNotify(B_BAD_VALUE);
1805 return B_BAD_VALUE;
1806 }
1807 translate_partition_access(vnode->stream.u.dev.partition, request);
1808 }
1809
1810 return vnode->stream.u.dev.device->IO(cookie->device_cookie, request);
1811 }
1812
1813
1814 static status_t
devfs_read_stat(fs_volume * _volume,fs_vnode * _vnode,struct stat * stat)1815 devfs_read_stat(fs_volume* _volume, fs_vnode* _vnode, struct stat* stat)
1816 {
1817 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1818
1819 TRACE(("devfs_read_stat: vnode %p (%" B_PRIdINO "), stat %p\n",
1820 vnode, vnode->id, stat));
1821
1822 stat->st_ino = vnode->id;
1823 stat->st_rdev = vnode->id;
1824 stat->st_size = 0;
1825 stat->st_mode = vnode->stream.type;
1826
1827 stat->st_nlink = 1;
1828 stat->st_blksize = 65536;
1829 stat->st_blocks = 0;
1830
1831 stat->st_uid = vnode->uid;
1832 stat->st_gid = vnode->gid;
1833
1834 stat->st_atim = current_timespec();
1835 stat->st_mtim = stat->st_ctim = vnode->modification_time;
1836 stat->st_crtim = vnode->creation_time;
1837
1838 // TODO: this only works for partitions right now - if we should decide
1839 // to keep this feature, we should have a better solution
1840 if (S_ISCHR(vnode->stream.type)) {
1841 //device_geometry geometry;
1842
1843 // if it's a real block device, then let's report a useful size
1844 if (vnode->stream.u.dev.partition != NULL) {
1845 stat->st_size = vnode->stream.u.dev.partition->info.size;
1846 #if 0
1847 } else if (vnode->stream.u.dev.info->control(cookie->device_cookie,
1848 B_GET_GEOMETRY, &geometry, sizeof(struct device_geometry)) >= B_OK) {
1849 stat->st_size = 1LL * geometry.head_count * geometry.cylinder_count
1850 * geometry.sectors_per_track * geometry.bytes_per_sector;
1851 #endif
1852 }
1853
1854 // is this a real block device? then let's have it reported like that
1855 if (stat->st_size != 0)
1856 stat->st_mode = S_IFBLK | (vnode->stream.type & S_IUMSK);
1857 } else if (S_ISLNK(vnode->stream.type)) {
1858 stat->st_size = vnode->stream.u.symlink.length;
1859 }
1860
1861 return B_OK;
1862 }
1863
1864
1865 static status_t
devfs_write_stat(fs_volume * _volume,fs_vnode * _vnode,const struct stat * stat,uint32 statMask)1866 devfs_write_stat(fs_volume* _volume, fs_vnode* _vnode, const struct stat* stat,
1867 uint32 statMask)
1868 {
1869 struct devfs* fs = (struct devfs*)_volume->private_volume;
1870 struct devfs_vnode* vnode = (struct devfs_vnode*)_vnode->private_node;
1871
1872 TRACE(("devfs_write_stat: vnode %p (0x%" B_PRIdINO "), stat %p\n",
1873 vnode, vnode->id, stat));
1874
1875 // we cannot change the size of anything
1876 if (statMask & B_STAT_SIZE)
1877 return B_BAD_VALUE;
1878
1879 RecursiveLocker locker(&fs->lock);
1880
1881 if (statMask & B_STAT_MODE) {
1882 vnode->stream.type = (vnode->stream.type & ~S_IUMSK)
1883 | (stat->st_mode & S_IUMSK);
1884 }
1885
1886 if (statMask & B_STAT_UID)
1887 vnode->uid = stat->st_uid;
1888 if (statMask & B_STAT_GID)
1889 vnode->gid = stat->st_gid;
1890
1891 if (statMask & B_STAT_MODIFICATION_TIME)
1892 vnode->modification_time = stat->st_mtim;
1893 if (statMask & B_STAT_CREATION_TIME)
1894 vnode->creation_time = stat->st_crtim;
1895
1896 notify_stat_changed(fs->id, get_parent_id(vnode), vnode->id, statMask);
1897 return B_OK;
1898 }
1899
1900
1901 static status_t
devfs_std_ops(int32 op,...)1902 devfs_std_ops(int32 op, ...)
1903 {
1904 switch (op) {
1905 case B_MODULE_INIT:
1906 add_debugger_command_etc("devfs_node", &dump_node,
1907 "Print info on a private devfs node",
1908 "<address>\n"
1909 "Prints information on a devfs node given by <address>.\n",
1910 0);
1911 add_debugger_command_etc("devfs_cookie", &dump_cookie,
1912 "Print info on a private devfs cookie",
1913 "<address>\n"
1914 "Prints information on a devfs cookie given by <address>.\n",
1915 0);
1916
1917 legacy_driver_init();
1918 return B_OK;
1919
1920 case B_MODULE_UNINIT:
1921 remove_debugger_command("devfs_node", &dump_node);
1922 remove_debugger_command("devfs_cookie", &dump_cookie);
1923 return B_OK;
1924
1925 default:
1926 return B_ERROR;
1927 }
1928 }
1929
1930 namespace {
1931
1932 fs_volume_ops kVolumeOps = {
1933 &devfs_unmount,
1934 NULL,
1935 NULL,
1936 &devfs_sync,
1937 &devfs_get_vnode,
1938
1939 // the other operations are not supported (attributes, indices, queries)
1940 NULL,
1941 };
1942
1943 fs_vnode_ops kVnodeOps = {
1944 &devfs_lookup,
1945 &devfs_get_vnode_name,
1946
1947 &devfs_put_vnode,
1948 &devfs_remove_vnode,
1949
1950 &devfs_can_page,
1951 &devfs_read_pages,
1952 &devfs_write_pages,
1953
1954 &devfs_io,
1955 NULL, // cancel_io()
1956
1957 NULL, // get_file_map
1958
1959 /* common */
1960 &devfs_ioctl,
1961 &devfs_set_flags,
1962 &devfs_select,
1963 &devfs_deselect,
1964 &devfs_fsync,
1965
1966 &devfs_read_link,
1967 NULL, // symlink
1968 NULL, // link
1969 NULL, // unlink
1970 NULL, // rename
1971
1972 NULL, // access
1973 &devfs_read_stat,
1974 &devfs_write_stat,
1975 NULL,
1976
1977 /* file */
1978 NULL, // create
1979 &devfs_open,
1980 &devfs_close,
1981 &devfs_free_cookie,
1982 &devfs_read,
1983 &devfs_write,
1984
1985 /* directory */
1986 &devfs_create_dir,
1987 NULL, // remove_dir
1988 &devfs_open_dir,
1989 &devfs_close,
1990 // same as for files - it does nothing for directories, anyway
1991 &devfs_free_dir_cookie,
1992 &devfs_read_dir,
1993 &devfs_rewind_dir,
1994
1995 // attributes operations are not supported
1996 NULL,
1997 };
1998
1999 } // namespace
2000
2001 file_system_module_info gDeviceFileSystem = {
2002 {
2003 "file_systems/devfs" B_CURRENT_FS_API_VERSION,
2004 0,
2005 devfs_std_ops,
2006 },
2007
2008 "devfs", // short_name
2009 "Device File System", // pretty_name
2010 0, // DDM flags
2011
2012 NULL, // identify_partition()
2013 NULL, // scan_partition()
2014 NULL, // free_identify_partition_cookie()
2015 NULL, // free_partition_content_cookie()
2016
2017 &devfs_mount,
2018 };
2019
2020
2021 // #pragma mark - kernel private API
2022
2023
2024 extern "C" status_t
devfs_unpublish_file_device(const char * path)2025 devfs_unpublish_file_device(const char* path)
2026 {
2027 // get the device node
2028 devfs_vnode* node;
2029 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2030 if (status != B_OK)
2031 return status;
2032
2033 if (!S_ISCHR(node->stream.type)) {
2034 put_vnode(sDeviceFileSystem->volume, node->id);
2035 return B_BAD_VALUE;
2036 }
2037
2038 // if it is indeed a file device, unpublish it
2039 FileDevice* device = dynamic_cast<FileDevice*>(node->stream.u.dev.device);
2040 if (device == NULL) {
2041 put_vnode(sDeviceFileSystem->volume, node->id);
2042 return B_BAD_VALUE;
2043 }
2044
2045 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2046
2047 put_vnode(sDeviceFileSystem->volume, node->id);
2048 return status;
2049 }
2050
2051
2052 extern "C" status_t
devfs_publish_file_device(const char * path,const char * filePath)2053 devfs_publish_file_device(const char* path, const char* filePath)
2054 {
2055 // create a FileDevice for the file
2056 FileDevice* device = new(std::nothrow) FileDevice;
2057 if (device == NULL)
2058 return B_NO_MEMORY;
2059 ObjectDeleter<FileDevice> deviceDeleter(device);
2060
2061 status_t error = device->Init(filePath);
2062 if (error != B_OK)
2063 return error;
2064
2065 // publish the device
2066 error = publish_device(sDeviceFileSystem, path, device);
2067 if (error != B_OK)
2068 return error;
2069
2070 deviceDeleter.Detach();
2071 return B_OK;
2072 }
2073
2074
2075 extern "C" status_t
devfs_unpublish_partition(const char * path)2076 devfs_unpublish_partition(const char* path)
2077 {
2078 devfs_vnode* node;
2079 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2080 if (status != B_OK)
2081 return status;
2082
2083 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2084 put_vnode(sDeviceFileSystem->volume, node->id);
2085 return status;
2086 }
2087
2088
2089 extern "C" status_t
devfs_publish_partition(const char * name,const partition_info * info)2090 devfs_publish_partition(const char* name, const partition_info* info)
2091 {
2092 if (name == NULL || info == NULL)
2093 return B_BAD_VALUE;
2094 TRACE(("publish partition: %s (device \"%s\", offset %" B_PRIdOFF
2095 ", size %" B_PRIdOFF ")\n",
2096 name, info->device, info->offset, info->size));
2097
2098 devfs_vnode* device;
2099 status_t status = get_node_for_path(sDeviceFileSystem, info->device,
2100 &device);
2101 if (status != B_OK)
2102 return status;
2103
2104 status = add_partition(sDeviceFileSystem, device, name, *info);
2105
2106 put_vnode(sDeviceFileSystem->volume, device->id);
2107 return status;
2108 }
2109
2110
2111 extern "C" status_t
devfs_rename_partition(const char * devicePath,const char * oldName,const char * newName)2112 devfs_rename_partition(const char* devicePath, const char* oldName,
2113 const char* newName)
2114 {
2115 if (oldName == NULL || newName == NULL)
2116 return B_BAD_VALUE;
2117
2118 devfs_vnode* device;
2119 status_t status = get_node_for_path(sDeviceFileSystem, devicePath, &device);
2120 if (status != B_OK)
2121 return status;
2122
2123 RecursiveLocker locker(sDeviceFileSystem->lock);
2124 devfs_vnode* node = devfs_find_in_dir(device->parent, oldName);
2125 if (node == NULL)
2126 return B_ENTRY_NOT_FOUND;
2127
2128 // check if the new path already exists
2129 if (devfs_find_in_dir(device->parent, newName))
2130 return B_BAD_VALUE;
2131
2132 char* name = strdup(newName);
2133 if (name == NULL)
2134 return B_NO_MEMORY;
2135
2136 devfs_remove_from_dir(device->parent, node, false);
2137
2138 free(node->name);
2139 node->name = name;
2140
2141 devfs_insert_in_dir(device->parent, node, false);
2142
2143 notify_entry_moved(sDeviceFileSystem->id, device->parent->id, oldName,
2144 device->parent->id, newName, node->id);
2145 notify_stat_changed(sDeviceFileSystem->id, get_parent_id(device->parent),
2146 device->parent->id, B_STAT_MODIFICATION_TIME);
2147
2148 return B_OK;
2149 }
2150
2151
2152 extern "C" status_t
devfs_publish_directory(const char * path)2153 devfs_publish_directory(const char* path)
2154 {
2155 RecursiveLocker locker(&sDeviceFileSystem->lock);
2156
2157 return publish_directory(sDeviceFileSystem, path);
2158 }
2159
2160
2161 extern "C" status_t
devfs_unpublish_device(const char * path,bool disconnect)2162 devfs_unpublish_device(const char* path, bool disconnect)
2163 {
2164 devfs_vnode* node;
2165 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2166 if (status != B_OK)
2167 return status;
2168
2169 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2170
2171 if (status == B_OK && disconnect)
2172 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id);
2173
2174 put_vnode(sDeviceFileSystem->volume, node->id);
2175 return status;
2176 }
2177
2178
2179 // #pragma mark - device_manager private API
2180
2181
2182 status_t
devfs_publish_device(const char * path,BaseDevice * device)2183 devfs_publish_device(const char* path, BaseDevice* device)
2184 {
2185 return publish_device(sDeviceFileSystem, path, device);
2186 }
2187
2188
2189 status_t
devfs_unpublish_device(BaseDevice * device,bool disconnect)2190 devfs_unpublish_device(BaseDevice* device, bool disconnect)
2191 {
2192 devfs_vnode* node;
2193 status_t status = get_vnode(sDeviceFileSystem->volume, device->ID(),
2194 (void**)&node);
2195 if (status != B_OK)
2196 return status;
2197
2198 status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
2199
2200 if (status == B_OK && disconnect)
2201 vfs_disconnect_vnode(sDeviceFileSystem->id, node->id);
2202
2203 put_vnode(sDeviceFileSystem->volume, node->id);
2204 return status;
2205 }
2206
2207
2208 /*! Gets the device for a given devfs relative path.
2209 If successful the call must be balanced with a call to devfs_put_device().
2210 */
2211 status_t
devfs_get_device(const char * path,BaseDevice * & _device)2212 devfs_get_device(const char* path, BaseDevice*& _device)
2213 {
2214 devfs_vnode* node;
2215 status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
2216 if (status != B_OK)
2217 return status;
2218
2219 if (!S_ISCHR(node->stream.type) || node->stream.u.dev.partition != NULL) {
2220 put_vnode(sDeviceFileSystem->volume, node->id);
2221 return B_BAD_VALUE;
2222 }
2223
2224 _device = node->stream.u.dev.device;
2225 return B_OK;
2226 }
2227
2228
2229 void
devfs_put_device(BaseDevice * device)2230 devfs_put_device(BaseDevice* device)
2231 {
2232 put_vnode(sDeviceFileSystem->volume, device->ID());
2233 }
2234
2235
2236 void
devfs_compute_geometry_size(device_geometry * geometry,uint64 blockCount,uint32 blockSize)2237 devfs_compute_geometry_size(device_geometry* geometry, uint64 blockCount,
2238 uint32 blockSize)
2239 {
2240 geometry->head_count = 1;
2241 while (blockCount > UINT32_MAX) {
2242 geometry->head_count <<= 1;
2243 blockCount >>= 1;
2244 }
2245
2246 geometry->cylinder_count = 1;
2247 geometry->sectors_per_track = blockCount;
2248 geometry->bytes_per_sector = blockSize;
2249 }
2250
2251
2252 // #pragma mark - support API for legacy drivers
2253
2254
2255 extern "C" status_t
devfs_rescan_driver(const char * driverName)2256 devfs_rescan_driver(const char* driverName)
2257 {
2258 TRACE(("devfs_rescan_driver: %s\n", driverName));
2259
2260 return legacy_driver_rescan(driverName);
2261 }
2262
2263
2264 extern "C" status_t
devfs_publish_device(const char * path,device_hooks * hooks)2265 devfs_publish_device(const char* path, device_hooks* hooks)
2266 {
2267 return legacy_driver_publish(path, hooks);
2268 }
2269