tmpfs.cc 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #include <algorithm>
  2. #include <map>
  3. #include <vector>
  4. #include <assert.h>
  5. #include <stdint.h>
  6. #include <kernel/log.hpp>
  7. #include <kernel/vfs.hpp>
  8. using namespace fs;
  9. struct tmpfs_file_entry {
  10. ino_t ino;
  11. std::string filename;
  12. };
  13. class tmpfs : public virtual vfs {
  14. private:
  15. using fe_t = tmpfs_file_entry;
  16. using vfe_t = std::vector<fe_t>;
  17. using fdata_t = std::vector<char>;
  18. private:
  19. ino_t m_next_ino;
  20. private:
  21. ino_t assign_ino() { return m_next_ino++; }
  22. protected:
  23. inline vfe_t* make_vfe() { return new vfe_t{}; }
  24. inline fdata_t* make_fdata() { return new fdata_t{}; }
  25. void mklink(inode* dir, inode* ind, std::string filename) {
  26. auto& fes = *(vfe_t*)dir->fs_data;
  27. fes.emplace_back(fe_t{ind->ino, std::move(filename)});
  28. dir->size += sizeof(fe_t);
  29. ++ind->nlink;
  30. }
  31. virtual ssize_t readdir(inode* dir, size_t offset,
  32. const vfs::filldir_func& filldir) override {
  33. if (!S_ISDIR(dir->mode))
  34. return -ENOTDIR;
  35. auto& entries = *(vfe_t*)dir->fs_data;
  36. size_t off = offset / sizeof(fe_t);
  37. size_t nread = 0;
  38. for (; (off + 1) <= entries.size(); ++off, nread += sizeof(fe_t)) {
  39. const auto& entry = entries[off];
  40. auto* ind = get_inode(entry.ino);
  41. // inode mode filetype is compatible with user dentry filetype
  42. auto ret = filldir(entry.filename.c_str(), ind, ind->mode & S_IFMT);
  43. if (ret != 0)
  44. break;
  45. }
  46. return nread;
  47. }
  48. public:
  49. explicit tmpfs() : vfs(make_device(0, 2), 4096), m_next_ino{1} {
  50. auto* in = alloc_inode(assign_ino());
  51. in->fs_data = make_vfe();
  52. in->mode = S_IFDIR | 0777;
  53. mklink(in, in, ".");
  54. mklink(in, in, "..");
  55. register_root_node(in);
  56. }
  57. virtual ssize_t read(struct inode* file, char* buf, size_t buf_size,
  58. size_t count, off_t offset) override {
  59. if (!S_ISREG(file->mode))
  60. return -EINVAL;
  61. auto* data = (fdata_t*)file->fs_data;
  62. size_t fsize = data->size();
  63. if (offset + count > fsize)
  64. count = fsize - offset;
  65. if (buf_size < count) {
  66. count = buf_size;
  67. }
  68. memcpy(buf, data->data() + offset, count);
  69. return count;
  70. }
  71. virtual ssize_t write(struct inode* file, const char* buf, size_t count,
  72. off_t offset) override {
  73. if (!S_ISREG(file->mode))
  74. return -EINVAL;
  75. auto* data = (fdata_t*)file->fs_data;
  76. if (data->size() < offset + count)
  77. data->resize(offset + count);
  78. memcpy(data->data() + offset, buf, count);
  79. file->size = data->size();
  80. return count;
  81. }
  82. virtual int creat(struct inode* dir, struct dentry* at,
  83. mode_t mode) override {
  84. if (!S_ISDIR(dir->mode))
  85. return -ENOTDIR;
  86. assert(at->parent && at->parent->inode == dir);
  87. auto* file = alloc_inode(assign_ino());
  88. file->mode = S_IFREG | (mode & 0777);
  89. file->fs_data = make_fdata();
  90. mklink(dir, file, at->name);
  91. at->inode = file;
  92. at->flags |= fs::D_PRESENT;
  93. return 0;
  94. }
  95. virtual int mknod(struct inode* dir, struct dentry* at, mode_t mode,
  96. dev_t dev) override {
  97. if (!S_ISDIR(dir->mode))
  98. return -ENOTDIR;
  99. assert(at->parent && at->parent->inode == dir);
  100. if (!S_ISBLK(mode) && !S_ISCHR(mode))
  101. return -EINVAL;
  102. if (dev & ~0xffff)
  103. return -EINVAL;
  104. auto* node = alloc_inode(assign_ino());
  105. node->mode = mode;
  106. node->fs_data = (void*)(uintptr_t)dev;
  107. mklink(dir, node, at->name);
  108. at->inode = node;
  109. at->flags |= fs::D_PRESENT;
  110. return 0;
  111. }
  112. virtual int mkdir(struct inode* dir, struct dentry* at,
  113. mode_t mode) override {
  114. if (!S_ISDIR(dir->mode))
  115. return -ENOTDIR;
  116. assert(at->parent && at->parent->inode == dir);
  117. auto* new_dir = alloc_inode(assign_ino());
  118. new_dir->mode = S_IFDIR | (mode & 0777);
  119. new_dir->fs_data = make_vfe();
  120. mklink(new_dir, new_dir, ".");
  121. mklink(new_dir, dir, "..");
  122. mklink(dir, new_dir, at->name);
  123. at->inode = new_dir;
  124. at->flags |= fs::D_PRESENT | fs::D_DIRECTORY | fs::D_LOADED;
  125. return 0;
  126. }
  127. virtual int symlink(struct inode* dir, struct dentry* at,
  128. const char* target) override {
  129. if (!S_ISDIR(dir->mode))
  130. return -ENOTDIR;
  131. assert(at->parent && at->parent->inode == dir);
  132. auto* data = make_fdata();
  133. data->resize(strlen(target));
  134. memcpy(data->data(), target, data->size());
  135. auto* file = alloc_inode(assign_ino());
  136. file->mode = S_IFLNK | 0777;
  137. file->fs_data = data;
  138. file->size = data->size();
  139. mklink(dir, file, at->name);
  140. at->inode = file;
  141. at->flags |= fs::D_PRESENT;
  142. return 0;
  143. }
  144. virtual int readlink(struct inode* file, char* buf,
  145. size_t buf_size) override {
  146. if (!S_ISLNK(file->mode))
  147. return -EINVAL;
  148. auto* data = (fdata_t*)file->fs_data;
  149. size_t size = data->size();
  150. size = std::min(size, buf_size);
  151. memcpy(buf, data->data(), size);
  152. return size;
  153. }
  154. virtual int unlink(struct inode* dir, struct dentry* at) override {
  155. if (!S_ISDIR(dir->mode))
  156. return -ENOTDIR;
  157. assert(at->parent && at->parent->inode == dir);
  158. auto* vfe = (vfe_t*)dir->fs_data;
  159. assert(vfe);
  160. for (auto iter = vfe->begin(); iter != vfe->end();) {
  161. if (iter->ino != at->inode->ino) {
  162. ++iter;
  163. continue;
  164. }
  165. if (S_ISDIR(at->inode->mode))
  166. return -EISDIR;
  167. if (S_ISREG(at->inode->mode)) {
  168. // since we do not allow hard links in tmpfs, there is no need
  169. // to check references, we remove the file data directly
  170. auto* filedata = (fdata_t*)at->inode->fs_data;
  171. assert(filedata);
  172. delete filedata;
  173. }
  174. free_inode(iter->ino);
  175. at->flags &= ~fs::D_PRESENT;
  176. at->inode = nullptr;
  177. vfe->erase(iter);
  178. return 0;
  179. }
  180. kmsg("[tmpfs] warning: file entry not found in vfe");
  181. return -EIO;
  182. }
  183. virtual dev_t i_device(inode* file) override {
  184. if (file->mode & S_IFMT & (S_IFBLK | S_IFCHR))
  185. return (dev_t)(uintptr_t)file->fs_data;
  186. return -ENODEV;
  187. }
  188. virtual int truncate(inode* file, size_t size) override {
  189. if (!S_ISREG(file->mode))
  190. return -EINVAL;
  191. auto* data = (fdata_t*)file->fs_data;
  192. data->resize(size);
  193. file->size = size;
  194. return 0;
  195. }
  196. };
  197. static tmpfs* create_tmpfs(const char*, unsigned long, const void*) {
  198. // TODO: flags
  199. return new tmpfs;
  200. }
  201. int fs::register_tmpfs() {
  202. fs::register_fs("tmpfs", {create_tmpfs});
  203. return 0;
  204. }