vfs.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. #include <assert.h>
  2. #include <errno.h>
  3. #include <kernel/mem.h>
  4. #include <kernel/process.hpp>
  5. #include <kernel/tty.hpp>
  6. #include <kernel/vfs.hpp>
  7. #include <stdint.h>
  8. #include <stdio.h>
  9. #include <types/allocator.hpp>
  10. #include <types/list.hpp>
  11. #include <types/map.hpp>
  12. #include <types/pair.hpp>
  13. #include <types/status.h>
  14. #include <types/string.hpp>
  15. #include <types/vector.hpp>
  16. using types::allocator_traits;
  17. using types::kernel_allocator;
  18. using types::string;
  19. using types::vector;
  20. struct tmpfs_file_entry {
  21. size_t ino;
  22. char filename[128];
  23. };
  24. fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, const name_type& _name)
  25. : parent(_parent)
  26. , ind(_ind)
  27. , flags { 0 }
  28. , name(_name)
  29. {
  30. if (!_ind || _ind->flags.in.directory) {
  31. children = types::pnew<allocator_type>(children);
  32. idx_children = types::pnew<allocator_type>(idx_children);
  33. }
  34. }
  35. fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, name_type&& _name)
  36. : parent(_parent)
  37. , ind(_ind)
  38. , flags { 0 }
  39. , name(types::move(_name))
  40. {
  41. if (!_ind || _ind->flags.in.directory) {
  42. children = types::pnew<allocator_type>(children);
  43. idx_children = types::pnew<allocator_type>(idx_children);
  44. }
  45. }
  46. fs::vfs::dentry::dentry(dentry&& val)
  47. : children(val.children)
  48. , idx_children(val.idx_children)
  49. , parent(val.parent)
  50. , ind(val.ind)
  51. , flags { val.flags }
  52. , name(types::move(val.name))
  53. {
  54. if (children) {
  55. for (auto& item : *children)
  56. item.parent = this;
  57. }
  58. memset(&val, 0x00, sizeof(dentry));
  59. }
  60. fs::vfs::dentry::~dentry()
  61. {
  62. if (children) {
  63. types::pdelete<allocator_type>(children);
  64. types::pdelete<allocator_type>(idx_children);
  65. }
  66. }
  67. fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, const name_type& name, bool set_dirty)
  68. {
  69. auto iter = children->emplace_back(this, ind, name);
  70. idx_children->emplace(iter->name, &iter);
  71. if (set_dirty)
  72. this->flags.in.dirty = 1;
  73. return &iter;
  74. }
  75. fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type&& name, bool set_dirty)
  76. {
  77. auto iter = children->emplace_back(this, ind, types::move(name));
  78. idx_children->emplace(iter->name, &iter);
  79. if (set_dirty)
  80. this->flags.in.dirty = 1;
  81. return &iter;
  82. }
  83. fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
  84. {
  85. if (!ind->flags.in.directory)
  86. return nullptr;
  87. if (ind->flags.in.directory && !flags.in.present)
  88. ind->fs->load_dentry(this);
  89. auto iter = idx_children->find(name);
  90. if (!iter) {
  91. errno = ENOENT;
  92. return nullptr;
  93. }
  94. return iter->value;
  95. }
  96. fs::vfs::dentry* fs::vfs::dentry::replace(dentry* val)
  97. {
  98. // TODO: prevent the dirent to be swapped out of memory
  99. parent->idx_children->find(this->name)->value = val;
  100. return this;
  101. }
  102. void fs::vfs::dentry::invalidate(void)
  103. {
  104. // TODO: write back
  105. flags.in.dirty = 0;
  106. children->clear();
  107. idx_children->clear();
  108. flags.in.present = 0;
  109. }
  110. fs::vfs::vfs(void)
  111. : _root(nullptr, nullptr, "/")
  112. {
  113. }
  114. fs::inode* fs::vfs::cache_inode(inode_flags flags, uint32_t perm, size_t size, ino_t ino)
  115. {
  116. auto iter = _inodes.insert(types::make_pair(ino, inode { flags, perm, ino, this, size }));
  117. return &iter->value;
  118. }
  119. fs::inode* fs::vfs::get_inode(ino_t ino)
  120. {
  121. auto iter = _inodes.find(ino);
  122. // TODO: load inode from disk if not found
  123. if (iter)
  124. return &iter->value;
  125. else
  126. return nullptr;
  127. }
  128. void fs::vfs::register_root_node(inode* root)
  129. {
  130. if (!_root.ind)
  131. _root.ind = root;
  132. }
  133. int fs::vfs::load_dentry(dentry* ent)
  134. {
  135. auto* ind = ent->ind;
  136. if (!ind->flags.in.directory) {
  137. errno = ENOTDIR;
  138. return GB_FAILED;
  139. }
  140. size_t offset = 0;
  141. for (int ret = 1; ret > 0; offset += ret) {
  142. ret = this->inode_readdir(ind, offset,
  143. [&, this](const char* name, size_t len, ino_t ino, uint8_t) -> int {
  144. if (!len)
  145. ent->append(get_inode(ino), name, false);
  146. else
  147. ent->append(get_inode(ino), dentry::name_type(name, len), false);
  148. return GB_OK;
  149. });
  150. }
  151. ent->flags.in.present = 1;
  152. return GB_OK;
  153. }
  154. int fs::vfs::mount(dentry* mnt, vfs* new_fs)
  155. {
  156. if (!mnt->ind->flags.in.directory) {
  157. errno = ENOTDIR;
  158. return GB_FAILED;
  159. }
  160. auto* new_ent = new_fs->root();
  161. new_ent->parent = mnt->parent;
  162. new_ent->name = mnt->name;
  163. auto* orig_ent = mnt->replace(new_ent);
  164. _mount_recover_list.emplace(new_ent, orig_ent);
  165. new_ent->ind->flags.in.mount_point = 1;
  166. return GB_OK;
  167. }
  168. size_t fs::vfs::inode_read(inode*, char*, size_t, size_t, size_t)
  169. {
  170. assert(false);
  171. return 0xffffffff;
  172. }
  173. size_t fs::vfs::inode_write(inode*, const char*, size_t, size_t)
  174. {
  175. assert(false);
  176. return 0xffffffff;
  177. }
  178. int fs::vfs::inode_mkfile(dentry*, const char*)
  179. {
  180. assert(false);
  181. return GB_FAILED;
  182. }
  183. int fs::vfs::inode_mknode(dentry*, const char*, node_t)
  184. {
  185. assert(false);
  186. return GB_FAILED;
  187. }
  188. int fs::vfs::inode_rmfile(dentry*, const char*)
  189. {
  190. assert(false);
  191. return GB_FAILED;
  192. }
  193. int fs::vfs::inode_mkdir(dentry*, const char*)
  194. {
  195. assert(false);
  196. return GB_FAILED;
  197. }
  198. int fs::vfs::inode_stat(dentry*, stat*)
  199. {
  200. assert(false);
  201. return GB_FAILED;
  202. }
  203. uint32_t fs::vfs::inode_getnode(fs::inode*)
  204. {
  205. assert(false);
  206. return 0xffffffff;
  207. }
  208. class tmpfs : public virtual fs::vfs {
  209. private:
  210. using fe_t = tmpfs_file_entry;
  211. using vfe_t = vector<fe_t>;
  212. using fdata_t = vector<char>;
  213. private:
  214. fs::ino_t _next_ino;
  215. types::map<fs::ino_t, void*> inode_data;
  216. private:
  217. fs::ino_t _assign_ino(void)
  218. {
  219. return _next_ino++;
  220. }
  221. static constexpr vfe_t* as_vfe(void* data)
  222. {
  223. return static_cast<vfe_t*>(data);
  224. }
  225. static constexpr fdata_t* as_fdata(void* data)
  226. {
  227. return static_cast<fdata_t*>(data);
  228. }
  229. static inline ptr_t as_val(void* data)
  230. {
  231. return reinterpret_cast<ptr_t>(data);
  232. }
  233. inline void* _getdata(fs::ino_t ino) const
  234. {
  235. return inode_data.find(ino)->value;
  236. }
  237. inline fs::ino_t _savedata(void* data)
  238. {
  239. fs::ino_t ino = _assign_ino();
  240. inode_data.insert(types::make_pair(ino, data));
  241. return ino;
  242. }
  243. inline fs::ino_t _savedata(ptr_t data)
  244. {
  245. return _savedata((void*)data);
  246. }
  247. protected:
  248. inline vfe_t* mk_fe_vector(void)
  249. {
  250. return allocator_traits<kernel_allocator<vfe_t>>::allocate_and_construct();
  251. }
  252. inline fdata_t* mk_data_vector(void)
  253. {
  254. return allocator_traits<kernel_allocator<fdata_t>>::allocate_and_construct();
  255. }
  256. void mklink(fs::inode* dir, fs::inode* inode, const char* filename)
  257. {
  258. auto* fes = as_vfe(_getdata(dir->ino));
  259. auto iter = fes->emplace_back(fe_t {
  260. .ino = inode->ino,
  261. .filename = {} });
  262. strncpy(iter->filename, filename, sizeof(iter->filename));
  263. iter->filename[sizeof(iter->filename) - 1] = 0;
  264. dir->size += sizeof(fe_t);
  265. }
  266. virtual int inode_readdir(fs::inode* dir, size_t offset, fs::vfs::filldir_func filldir) override
  267. {
  268. if (!dir->flags.in.directory) {
  269. return -1;
  270. }
  271. auto& entries = *as_vfe(_getdata(dir->ino));
  272. size_t off = offset / sizeof(fe_t);
  273. size_t nread = 0;
  274. for (; (off + 1) <= entries.size(); ++off, nread += sizeof(fe_t)) {
  275. const auto& entry = entries[off];
  276. auto* ind = get_inode(entry.ino);
  277. auto type = DT_REG;
  278. if (ind->flags.in.directory)
  279. type = DT_DIR;
  280. if (ind->flags.in.special_node)
  281. type = DT_BLK;
  282. auto ret = filldir(entry.filename, 0, entry.ino, type);
  283. if (ret != GB_OK)
  284. break;
  285. }
  286. return nread;
  287. }
  288. public:
  289. explicit tmpfs(void)
  290. : _next_ino(1)
  291. {
  292. auto& in = *cache_inode({ INODE_DIR | INODE_MNT }, 0777, 0, _savedata(mk_fe_vector()));
  293. mklink(&in, &in, ".");
  294. mklink(&in, &in, "..");
  295. register_root_node(&in);
  296. }
  297. virtual int inode_mkfile(dentry* dir, const char* filename) override
  298. {
  299. auto& file = *cache_inode({ .v = INODE_FILE }, 0777, 0, _savedata(mk_data_vector()));
  300. mklink(dir->ind, &file, filename);
  301. dir->append(get_inode(file.ino), filename, true);
  302. return GB_OK;
  303. }
  304. virtual int inode_mknode(dentry* dir, const char* filename, fs::node_t sn) override
  305. {
  306. auto& node = *cache_inode({ .v = INODE_NODE }, 0777, 0, _savedata(sn.v));
  307. mklink(dir->ind, &node, filename);
  308. dir->append(get_inode(node.ino), filename, true);
  309. return GB_OK;
  310. }
  311. virtual int inode_mkdir(dentry* dir, const char* dirname) override
  312. {
  313. auto new_dir = cache_inode({ .v = INODE_DIR }, 0777, 0, _savedata(mk_fe_vector()));
  314. mklink(new_dir, new_dir, ".");
  315. mklink(dir->ind, new_dir, dirname);
  316. mklink(new_dir, dir->ind, "..");
  317. dir->append(new_dir, dirname, true);
  318. return GB_OK;
  319. }
  320. virtual size_t inode_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override
  321. {
  322. if (file->flags.in.file != 1)
  323. return 0;
  324. auto* data = as_fdata(_getdata(file->ino));
  325. size_t fsize = data->size();
  326. if (offset + n > fsize)
  327. n = fsize - offset;
  328. if (buf_size < n) {
  329. n = buf_size;
  330. }
  331. memcpy(buf, data->data() + offset, n);
  332. return n;
  333. }
  334. virtual size_t inode_write(fs::inode* file, const char* buf, size_t offset, size_t n) override
  335. {
  336. if (file->flags.in.file != 1)
  337. return 0;
  338. auto* data = as_fdata(_getdata(file->ino));
  339. for (size_t i = data->size(); i < offset + n; ++i) {
  340. data->push_back(0);
  341. }
  342. memcpy(data->data() + offset, buf, n);
  343. return n;
  344. }
  345. virtual int inode_stat(dentry* dir, fs::stat* stat) override
  346. {
  347. auto* file_inode = dir->ind;
  348. stat->st_ino = file_inode->ino;
  349. stat->st_size = file_inode->size;
  350. if (file_inode->flags.in.file) {
  351. stat->st_rdev.v = 0;
  352. stat->st_blksize = 1;
  353. stat->st_blocks = file_inode->size;
  354. }
  355. if (file_inode->flags.in.directory) {
  356. stat->st_rdev.v = 0;
  357. stat->st_blksize = sizeof(fe_t);
  358. stat->st_blocks = file_inode->size;
  359. }
  360. if (file_inode->flags.in.special_node) {
  361. stat->st_rdev.v = as_val(_getdata(file_inode->ino));
  362. stat->st_blksize = 0;
  363. stat->st_blocks = 0;
  364. }
  365. return GB_OK;
  366. }
  367. virtual uint32_t inode_getnode(fs::inode* file) override
  368. {
  369. return as_val(_getdata(file->ino));
  370. }
  371. };
  372. // 8 * 8 for now
  373. static fs::special_node sns[8][8];
  374. size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
  375. {
  376. if (file->flags.in.special_node) {
  377. uint32_t ret = file->fs->inode_getnode(file);
  378. if (ret == SN_INVALID) {
  379. errno = EINVAL;
  380. return 0xffffffff;
  381. }
  382. fs::node_t sn {
  383. .v = ret
  384. };
  385. auto* ptr = &sns[sn.in.major][sn.in.minor];
  386. auto* ops = &ptr->ops;
  387. if (ops && ops->read)
  388. return ops->read(ptr, buf, buf_size, offset, n);
  389. else {
  390. errno = EINVAL;
  391. return 0xffffffff;
  392. }
  393. } else {
  394. return file->fs->inode_read(file, buf, buf_size, offset, n);
  395. }
  396. }
  397. size_t fs::vfs_write(fs::inode* file, const char* buf, size_t offset, size_t n)
  398. {
  399. if (file->flags.in.special_node) {
  400. uint32_t ret = file->fs->inode_getnode(file);
  401. if (ret == SN_INVALID) {
  402. errno = EINVAL;
  403. return 0xffffffff;
  404. }
  405. fs::node_t sn {
  406. .v = ret
  407. };
  408. auto* ptr = &sns[sn.in.major][sn.in.minor];
  409. auto* ops = &ptr->ops;
  410. if (ops && ops->write)
  411. return ops->write(ptr, buf, offset, n);
  412. else {
  413. errno = EINVAL;
  414. return 0xffffffff;
  415. }
  416. } else {
  417. return file->fs->inode_write(file, buf, offset, n);
  418. }
  419. }
  420. int fs::vfs_mkfile(fs::vfs::dentry* dir, const char* filename)
  421. {
  422. return dir->ind->fs->inode_mkfile(dir, filename);
  423. }
  424. int fs::vfs_mknode(fs::vfs::dentry* dir, const char* filename, fs::node_t sn)
  425. {
  426. return dir->ind->fs->inode_mknode(dir, filename, sn);
  427. }
  428. int fs::vfs_rmfile(fs::vfs::dentry* dir, const char* filename)
  429. {
  430. return dir->ind->fs->inode_rmfile(dir, filename);
  431. }
  432. int fs::vfs_mkdir(fs::vfs::dentry* dir, const char* dirname)
  433. {
  434. return dir->ind->fs->inode_mkdir(dir, dirname);
  435. }
  436. fs::vfs::dentry* fs::vfs_open(const char* path)
  437. {
  438. if (path[0] == '/' && path[1] == 0x00) {
  439. return fs::fs_root;
  440. }
  441. auto* cur = fs::fs_root;
  442. size_t n = 0;
  443. switch (*(path++)) {
  444. // absolute path
  445. case '/':
  446. while (true) {
  447. if (path[n] == 0x00) {
  448. cur = cur->find(string(path, n));
  449. return cur;
  450. }
  451. if (path[n] == '/') {
  452. if (n == 0) {
  453. ++path;
  454. continue;
  455. }
  456. cur = cur->find(string(path, n));
  457. if (!cur)
  458. return cur;
  459. if (path[n + 1] == 0x00) {
  460. return cur;
  461. } else {
  462. path += (n + 1);
  463. n = 0;
  464. continue;
  465. }
  466. }
  467. ++n;
  468. }
  469. break;
  470. // empty string
  471. case 0x00:
  472. return nullptr;
  473. break;
  474. // relative path
  475. default:
  476. return nullptr;
  477. break;
  478. }
  479. return nullptr;
  480. }
  481. int fs::vfs_stat(const char* filename, stat* stat)
  482. {
  483. auto ent = vfs_open(filename);
  484. if (!ent)
  485. return GB_FAILED;
  486. return vfs_stat(ent, stat);
  487. }
  488. int fs::vfs_stat(fs::vfs::dentry* ent, stat* stat)
  489. {
  490. return ent->ind->fs->inode_stat(ent, stat);
  491. }
  492. fs::vfs::dentry* fs::vfs_open_proc(const char* path)
  493. {
  494. if (!path)
  495. return nullptr;
  496. if (path[0] == '/')
  497. return fs::vfs_open(path);
  498. return fs::vfs_open(types::string<>(current_process->pwd).append("/").append(path).c_str());
  499. }
  500. static types::list<fs::vfs*>* fs_es;
  501. void fs::register_special_block(
  502. uint16_t major,
  503. uint16_t minor,
  504. fs::special_node_read read,
  505. fs::special_node_write write,
  506. uint32_t data1,
  507. uint32_t data2)
  508. {
  509. fs::special_node& sn = sns[major][minor];
  510. sn.ops.read = read;
  511. sn.ops.write = write;
  512. sn.data1 = data1;
  513. sn.data2 = data2;
  514. }
  515. fs::vfs* fs::register_fs(vfs* fs)
  516. {
  517. fs_es->push_back(fs);
  518. return fs;
  519. }
  520. size_t b_null_read(fs::special_node*, char* buf, size_t buf_size, size_t, size_t n)
  521. {
  522. if (n >= buf_size)
  523. n = buf_size;
  524. memset(buf, 0x00, n);
  525. return n;
  526. }
  527. size_t b_null_write(fs::special_node*, const char*, size_t, size_t n)
  528. {
  529. return n;
  530. }
  531. static size_t console_read(fs::special_node*, char* buf, size_t buf_size, size_t, size_t n)
  532. {
  533. return console->read(buf, buf_size, n);
  534. }
  535. static size_t console_write(fs::special_node*, const char* buf, size_t, size_t n)
  536. {
  537. size_t orig_n = n;
  538. while (n--)
  539. console->putchar(*(buf++));
  540. return orig_n;
  541. }
  542. fs::pipe::pipe(void)
  543. : buf { PIPE_SIZE }
  544. , flags { READABLE | WRITABLE }
  545. {
  546. }
  547. void fs::pipe::close_read(void)
  548. {
  549. {
  550. types::lock_guard lck(m_cv.mtx());
  551. flags &= (~READABLE);
  552. }
  553. m_cv.notify_all();
  554. }
  555. void fs::pipe::close_write(void)
  556. {
  557. {
  558. types::lock_guard lck(m_cv.mtx());
  559. flags &= (~WRITABLE);
  560. }
  561. m_cv.notify_all();
  562. }
  563. int fs::pipe::write(const char* buf, size_t n)
  564. {
  565. // TODO: check privilege
  566. // TODO: check EPIPE
  567. {
  568. auto& mtx = m_cv.mtx();
  569. types::lock_guard lck(mtx);
  570. if (!is_readable()) {
  571. current_process->signals.set(kernel::SIGPIPE);
  572. return -EPIPE;
  573. }
  574. while (this->buf.avail() < n) {
  575. if (!m_cv.wait(mtx))
  576. return -EINTR;
  577. if (!is_readable()) {
  578. current_process->signals.set(kernel::SIGPIPE);
  579. return -EPIPE;
  580. }
  581. }
  582. for (size_t i = 0; i < n; ++i)
  583. this->buf.put(*(buf++));
  584. }
  585. m_cv.notify();
  586. return n;
  587. }
  588. int fs::pipe::read(char* buf, size_t n)
  589. {
  590. // TODO: check privilege
  591. {
  592. auto& mtx = m_cv.mtx();
  593. types::lock_guard lck(mtx);
  594. if (!is_writeable()) {
  595. size_t orig_n = n;
  596. while (!this->buf.empty() && n--)
  597. *(buf++) = this->buf.get();
  598. return orig_n - n;
  599. }
  600. while (this->buf.size() < n) {
  601. if (!m_cv.wait(mtx))
  602. return -EINTR;
  603. if (!is_writeable()) {
  604. size_t orig_n = n;
  605. while (!this->buf.empty() && n--)
  606. *(buf++) = this->buf.get();
  607. return orig_n - n;
  608. }
  609. }
  610. for (size_t i = 0; i < n; ++i)
  611. *(buf++) = this->buf.get();
  612. }
  613. m_cv.notify();
  614. return n;
  615. }
  616. SECTION(".text.kinit")
  617. void init_vfs(void)
  618. {
  619. using namespace fs;
  620. // null
  621. register_special_block(0, 0, b_null_read, b_null_write, 0, 0);
  622. // console (supports serial console only for now)
  623. // TODO: add interface to bind console device to other devices
  624. register_special_block(1, 0, console_read, console_write, 0, 0);
  625. fs_es = types::pnew<types::kernel_ident_allocator>(fs_es);
  626. auto* rootfs = new tmpfs;
  627. fs_es->push_back(rootfs);
  628. fs_root = rootfs->root();
  629. vfs_mkdir(fs_root, "dev");
  630. vfs_mkdir(fs_root, "root");
  631. vfs_mkdir(fs_root, "mnt");
  632. vfs_mkfile(fs_root, "init");
  633. auto* init = vfs_open("/init");
  634. const char* str = "#/bin/sh\nexec /bin/sh\n";
  635. vfs_write(init->ind, str, 0, strlen(str));
  636. auto* dev = vfs_open("/dev");
  637. vfs_mknode(dev, "null", { .in { .major = 0, .minor = 0 } });
  638. vfs_mknode(dev, "console", { .in { .major = 1, .minor = 0 } });
  639. vfs_mknode(dev, "hda", { .in { .major = 2, .minor = 0 } });
  640. stat _stat {};
  641. vfs_stat("/init", &_stat);
  642. vfs_stat("/", &_stat);
  643. vfs_stat("/dev", &_stat);
  644. vfs_stat("/dev/null", &_stat);
  645. vfs_stat("/dev/console", &_stat);
  646. vfs_stat("/dev/hda", &_stat);
  647. }