tmpfs.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2014-2018 K. Lange
  5. */
  6. #include <kernel/system.h>
  7. #include <kernel/logging.h>
  8. #include <kernel/fs.h>
  9. #include <kernel/version.h>
  10. #include <kernel/process.h>
  11. #include <kernel/mem.h>
  12. #include <kernel/module.h>
  13. #include <kernel/mod/tmpfs.h>
  14. /* 4KB */
  15. #define BLOCKSIZE 0x1000
  16. #define TMPFS_TYPE_FILE 1
  17. #define TMPFS_TYPE_DIR 2
  18. #define TMPFS_TYPE_LINK 3
  19. static char * buf_space = NULL;
  20. static spin_lock_t tmpfs_lock = { 0 };
  21. static spin_lock_t tmpfs_page_lock = { 0 };
  22. struct tmpfs_dir * tmpfs_root = NULL;
  23. static fs_node_t * tmpfs_from_dir(struct tmpfs_dir * d);
  24. static struct tmpfs_file * tmpfs_file_new(char * name) {
  25. spin_lock(tmpfs_lock);
  26. struct tmpfs_file * t = malloc(sizeof(struct tmpfs_file));
  27. t->name = strdup(name);
  28. t->type = TMPFS_TYPE_FILE;
  29. t->length = 0;
  30. t->pointers = 2;
  31. t->block_count = 0;
  32. t->mask = 0;
  33. t->uid = 0;
  34. t->gid = 0;
  35. t->atime = now();
  36. t->mtime = t->atime;
  37. t->ctime = t->atime;
  38. t->blocks = malloc(t->pointers * sizeof(char *));
  39. for (size_t i = 0; i < t->pointers; ++i) {
  40. t->blocks[i] = NULL;
  41. }
  42. spin_unlock(tmpfs_lock);
  43. return t;
  44. }
  45. static int symlink_tmpfs(fs_node_t * parent, char * target, char * name) {
  46. struct tmpfs_dir * d = (struct tmpfs_dir *)parent->device;
  47. debug_print(NOTICE, "Creating TMPFS file (symlink) %s in %s", name, d->name);
  48. spin_lock(tmpfs_lock);
  49. foreach(f, d->files) {
  50. struct tmpfs_file * t = (struct tmpfs_file *)f->value;
  51. if (!strcmp(name, t->name)) {
  52. spin_unlock(tmpfs_lock);
  53. debug_print(WARNING, "... already exists.");
  54. return -EEXIST; /* Already exists */
  55. }
  56. }
  57. spin_unlock(tmpfs_lock);
  58. debug_print(NOTICE, "... creating a new file (symlink).");
  59. struct tmpfs_file * t = tmpfs_file_new(name);
  60. t->type = TMPFS_TYPE_LINK;
  61. debug_print(NOTICE, "symlink target is [%s]", target);
  62. t->target = strdup(target);
  63. t->mask = 0777;
  64. t->uid = current_process->user;
  65. t->gid = current_process->user;
  66. spin_lock(tmpfs_lock);
  67. list_insert(d->files, t);
  68. spin_unlock(tmpfs_lock);
  69. return 0;
  70. }
  71. static int readlink_tmpfs(fs_node_t * node, char * buf, size_t size) {
  72. struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
  73. if (t->type != TMPFS_TYPE_LINK) {
  74. debug_print(WARNING, "Not a symlink? Very confused!");
  75. return -1;
  76. }
  77. if (size < strlen(t->target) + 1) {
  78. debug_print(INFO, "Requested read size was only %d, need %d.", size, strlen(t->target)+1);
  79. memcpy(buf, t->target, size);
  80. buf[size] = '\0';
  81. return size-1;
  82. } else {
  83. debug_print(INFO, "Reading link target is [%s]", t->target);
  84. memcpy(buf, t->target, strlen(t->target) + 1);
  85. return strlen(t->target);
  86. }
  87. }
  88. static struct tmpfs_dir * tmpfs_dir_new(char * name, struct tmpfs_dir * parent) {
  89. spin_lock(tmpfs_lock);
  90. struct tmpfs_dir * d = malloc(sizeof(struct tmpfs_dir));
  91. d->name = strdup(name);
  92. d->type = TMPFS_TYPE_DIR;
  93. d->mask = 0;
  94. d->uid = 0;
  95. d->gid = 0;
  96. d->atime = now();
  97. d->mtime = d->atime;
  98. d->ctime = d->atime;
  99. d->files = list_create();
  100. spin_unlock(tmpfs_lock);
  101. return d;
  102. }
  103. static void tmpfs_file_free(struct tmpfs_file * t) {
  104. if (t->type == TMPFS_TYPE_LINK) {
  105. debug_print(ERROR, "uh, what");
  106. free(t->target);
  107. }
  108. for (size_t i = 0; i < t->block_count; ++i) {
  109. clear_frame((uintptr_t)t->blocks[i] * 0x1000);
  110. }
  111. }
  112. static void tmpfs_file_blocks_embiggen(struct tmpfs_file * t) {
  113. t->pointers *= 2;
  114. debug_print(INFO, "Embiggening file %s to %d blocks", t->name, t->pointers);
  115. t->blocks = realloc(t->blocks, sizeof(char *) * t->pointers);
  116. }
  117. static char * tmpfs_file_getset_block(struct tmpfs_file * t, size_t blockid, int create) {
  118. debug_print(INFO, "Reading block %d from file %s", blockid, t->name);
  119. spin_lock(tmpfs_page_lock);
  120. if (create) {
  121. spin_lock(tmpfs_lock);
  122. while (blockid >= t->pointers) {
  123. tmpfs_file_blocks_embiggen(t);
  124. }
  125. while (blockid >= t->block_count) {
  126. debug_print(INFO, "Allocating block %d for file %s", blockid, t->name);
  127. uintptr_t index = first_frame();
  128. set_frame(index * 0x1000);
  129. t->blocks[t->block_count] = (char*)index;
  130. t->block_count += 1;
  131. }
  132. spin_unlock(tmpfs_lock);
  133. } else {
  134. if (blockid >= t->block_count) {
  135. debug_print(ERROR, "This will probably end badly.");
  136. return NULL;
  137. }
  138. }
  139. debug_print(INFO, "Using block %d->0x%x (of %d) on file %s", blockid, t->blocks[blockid], t->block_count, t->name);
  140. page_t * page = get_page((uintptr_t)buf_space,0,current_directory);
  141. page->rw = 1;
  142. page->user = 0;
  143. page->frame = (uintptr_t)t->blocks[blockid];
  144. page->present = 1;
  145. invalidate_tables_at((uintptr_t)buf_space);
  146. return (char *)buf_space;
  147. }
  148. static uint32_t read_tmpfs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
  149. struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
  150. t->atime = now();
  151. uint32_t end;
  152. if (offset + size > t->length) {
  153. end = t->length;
  154. } else {
  155. end = offset + size;
  156. }
  157. debug_print(INFO, "reading from %d to %d", offset, end);
  158. uint32_t start_block = offset / BLOCKSIZE;
  159. uint32_t end_block = end / BLOCKSIZE;
  160. uint32_t end_size = end - end_block * BLOCKSIZE;
  161. uint32_t size_to_read = end - offset;
  162. if (start_block == end_block && offset == end) return 0;
  163. if (start_block == end_block) {
  164. void *buf = tmpfs_file_getset_block(t, start_block, 0);
  165. memcpy(buffer, (uint8_t *)(((uint32_t)buf) + (offset % BLOCKSIZE)), size_to_read);
  166. spin_unlock(tmpfs_page_lock);
  167. return size_to_read;
  168. } else {
  169. uint32_t block_offset;
  170. uint32_t blocks_read = 0;
  171. for (block_offset = start_block; block_offset < end_block; block_offset++, blocks_read++) {
  172. if (block_offset == start_block) {
  173. void *buf = tmpfs_file_getset_block(t, block_offset, 0);
  174. memcpy(buffer, (uint8_t *)(((uint32_t)buf) + (offset % BLOCKSIZE)), BLOCKSIZE - (offset % BLOCKSIZE));
  175. spin_unlock(tmpfs_page_lock);
  176. } else {
  177. void *buf = tmpfs_file_getset_block(t, block_offset, 0);
  178. memcpy(buffer + BLOCKSIZE * blocks_read - (offset % BLOCKSIZE), buf, BLOCKSIZE);
  179. spin_unlock(tmpfs_page_lock);
  180. }
  181. }
  182. if (end_size) {
  183. void *buf = tmpfs_file_getset_block(t, end_block, 0);
  184. memcpy(buffer + BLOCKSIZE * blocks_read - (offset % BLOCKSIZE), buf, end_size);
  185. spin_unlock(tmpfs_page_lock);
  186. }
  187. }
  188. return size_to_read;
  189. }
  190. static uint32_t write_tmpfs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
  191. struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
  192. t->atime = now();
  193. t->mtime = t->atime;
  194. uint32_t end;
  195. if (offset + size > t->length) {
  196. t->length = offset + size;
  197. }
  198. end = offset + size;
  199. uint32_t start_block = offset / BLOCKSIZE;
  200. uint32_t end_block = end / BLOCKSIZE;
  201. uint32_t end_size = end - end_block * BLOCKSIZE;
  202. uint32_t size_to_read = end - offset;
  203. if (start_block == end_block) {
  204. void *buf = tmpfs_file_getset_block(t, start_block, 1);
  205. memcpy((uint8_t *)(((uint32_t)buf) + (offset % BLOCKSIZE)), buffer, size_to_read);
  206. spin_unlock(tmpfs_page_lock);
  207. return size_to_read;
  208. } else {
  209. uint32_t block_offset;
  210. uint32_t blocks_read = 0;
  211. for (block_offset = start_block; block_offset < end_block; block_offset++, blocks_read++) {
  212. if (block_offset == start_block) {
  213. void *buf = tmpfs_file_getset_block(t, block_offset, 1);
  214. memcpy((uint8_t *)(((uint32_t)buf) + (offset % BLOCKSIZE)), buffer, BLOCKSIZE - (offset % BLOCKSIZE));
  215. spin_unlock(tmpfs_page_lock);
  216. } else {
  217. void *buf = tmpfs_file_getset_block(t, block_offset, 1);
  218. memcpy(buf, buffer + BLOCKSIZE * blocks_read - (offset % BLOCKSIZE), BLOCKSIZE);
  219. spin_unlock(tmpfs_page_lock);
  220. }
  221. }
  222. if (end_size) {
  223. void *buf = tmpfs_file_getset_block(t, end_block, 1);
  224. memcpy(buf, buffer + BLOCKSIZE * blocks_read - (offset % BLOCKSIZE), end_size);
  225. spin_unlock(tmpfs_page_lock);
  226. }
  227. }
  228. return size_to_read;
  229. }
  230. static int chmod_tmpfs(fs_node_t * node, int mode) {
  231. struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
  232. /* XXX permissions */
  233. t->mask = mode;
  234. return 0;
  235. }
  236. static int chown_tmpfs(fs_node_t * node, int uid, int gid) {
  237. struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
  238. debug_print(INFO, "chown(..., %d, %d)", uid, gid);
  239. t->uid = uid;
  240. t->gid = gid;
  241. return 0;
  242. }
  243. static void open_tmpfs(fs_node_t * node, unsigned int flags) {
  244. struct tmpfs_file * t = (struct tmpfs_file *)(node->device);
  245. debug_print(INFO, "---- Opened TMPFS file %s with flags 0x%x ----", t->name, flags);
  246. if (flags & O_TRUNC) {
  247. debug_print(INFO, "Truncating file %s", t->name);
  248. for (size_t i = 0; i < t->block_count; ++i) {
  249. clear_frame((uintptr_t)t->blocks[i] * 0x1000);
  250. t->blocks[i] = 0;
  251. }
  252. t->block_count = 0;
  253. t->length = 0;
  254. }
  255. return;
  256. }
  257. static fs_node_t * tmpfs_from_file(struct tmpfs_file * t) {
  258. fs_node_t * fnode = malloc(sizeof(fs_node_t));
  259. memset(fnode, 0x00, sizeof(fs_node_t));
  260. fnode->inode = 0;
  261. strcpy(fnode->name, t->name);
  262. fnode->device = t;
  263. fnode->mask = t->mask;
  264. fnode->uid = t->uid;
  265. fnode->gid = t->gid;
  266. fnode->atime = t->atime;
  267. fnode->ctime = t->ctime;
  268. fnode->mtime = t->mtime;
  269. fnode->flags = FS_FILE;
  270. fnode->read = read_tmpfs;
  271. fnode->write = write_tmpfs;
  272. fnode->open = open_tmpfs;
  273. fnode->close = NULL;
  274. fnode->readdir = NULL;
  275. fnode->finddir = NULL;
  276. fnode->chmod = chmod_tmpfs;
  277. fnode->chown = chown_tmpfs;
  278. fnode->length = t->length;
  279. fnode->nlink = 1;
  280. return fnode;
  281. }
  282. static fs_node_t * tmpfs_from_link(struct tmpfs_file * t) {
  283. fs_node_t * fnode = tmpfs_from_file(t);
  284. fnode->flags |= FS_SYMLINK;
  285. fnode->readlink = readlink_tmpfs;
  286. fnode->read = NULL;
  287. fnode->write = NULL;
  288. fnode->create = NULL;
  289. fnode->mkdir = NULL;
  290. fnode->readdir = NULL;
  291. fnode->finddir = NULL;
  292. return fnode;
  293. }
  294. static struct dirent * readdir_tmpfs(fs_node_t *node, uint32_t index) {
  295. struct tmpfs_dir * d = (struct tmpfs_dir *)node->device;
  296. uint32_t i = 0;
  297. debug_print(NOTICE, "tmpfs - readdir id=%d", index);
  298. if (index == 0) {
  299. struct dirent * out = malloc(sizeof(struct dirent));
  300. memset(out, 0x00, sizeof(struct dirent));
  301. out->ino = 0;
  302. strcpy(out->name, ".");
  303. return out;
  304. }
  305. if (index == 1) {
  306. struct dirent * out = malloc(sizeof(struct dirent));
  307. memset(out, 0x00, sizeof(struct dirent));
  308. out->ino = 0;
  309. strcpy(out->name, "..");
  310. return out;
  311. }
  312. index -= 2;
  313. if (index >= d->files->length) return NULL;
  314. foreach(f, d->files) {
  315. if (i == index) {
  316. struct tmpfs_file * t = (struct tmpfs_file *)f->value;
  317. struct dirent * out = malloc(sizeof(struct dirent));
  318. memset(out, 0x00, sizeof(struct dirent));
  319. out->ino = (uint32_t)t;
  320. strcpy(out->name, t->name);
  321. return out;
  322. } else {
  323. ++i;
  324. }
  325. }
  326. return NULL;
  327. }
  328. static fs_node_t * finddir_tmpfs(fs_node_t * node, char * name) {
  329. if (!name) return NULL;
  330. struct tmpfs_dir * d = (struct tmpfs_dir *)node->device;
  331. spin_lock(tmpfs_lock);
  332. foreach(f, d->files) {
  333. struct tmpfs_file * t = (struct tmpfs_file *)f->value;
  334. if (!strcmp(name, t->name)) {
  335. spin_unlock(tmpfs_lock);
  336. switch (t->type) {
  337. case TMPFS_TYPE_FILE:
  338. return tmpfs_from_file(t);
  339. case TMPFS_TYPE_LINK:
  340. return tmpfs_from_link(t);
  341. case TMPFS_TYPE_DIR:
  342. return tmpfs_from_dir((struct tmpfs_dir *)t);
  343. }
  344. return NULL;
  345. }
  346. }
  347. spin_unlock(tmpfs_lock);
  348. return NULL;
  349. }
  350. static int unlink_tmpfs(fs_node_t * node, char * name) {
  351. struct tmpfs_dir * d = (struct tmpfs_dir *)node->device;
  352. int i = -1, j = 0;
  353. spin_lock(tmpfs_lock);
  354. foreach(f, d->files) {
  355. struct tmpfs_file * t = (struct tmpfs_file *)f->value;
  356. if (!strcmp(name, t->name)) {
  357. tmpfs_file_free(t);
  358. free(t);
  359. i = j;
  360. break;
  361. }
  362. j++;
  363. }
  364. if (i >= 0) {
  365. list_remove(d->files, i);
  366. } else {
  367. spin_unlock(tmpfs_lock);
  368. return -ENOENT;
  369. }
  370. spin_unlock(tmpfs_lock);
  371. return 0;
  372. }
  373. static int create_tmpfs(fs_node_t *parent, char *name, uint16_t permission) {
  374. if (!name) return -EINVAL;
  375. struct tmpfs_dir * d = (struct tmpfs_dir *)parent->device;
  376. debug_print(NOTICE, "Creating TMPFS file %s in %s", name, d->name);
  377. spin_lock(tmpfs_lock);
  378. foreach(f, d->files) {
  379. struct tmpfs_file * t = (struct tmpfs_file *)f->value;
  380. if (!strcmp(name, t->name)) {
  381. spin_unlock(tmpfs_lock);
  382. debug_print(WARNING, "... already exists.");
  383. return -EEXIST; /* Already exists */
  384. }
  385. }
  386. spin_unlock(tmpfs_lock);
  387. debug_print(NOTICE, "... creating a new file.");
  388. struct tmpfs_file * t = tmpfs_file_new(name);
  389. t->mask = permission;
  390. t->uid = current_process->user;
  391. t->gid = current_process->user;
  392. spin_lock(tmpfs_lock);
  393. list_insert(d->files, t);
  394. spin_unlock(tmpfs_lock);
  395. return 0;
  396. }
  397. static int mkdir_tmpfs(fs_node_t * parent, char * name, uint16_t permission) {
  398. if (!name) return -EINVAL;
  399. struct tmpfs_dir * d = (struct tmpfs_dir *)parent->device;
  400. debug_print(NOTICE, "Creating TMPFS directory %s (in %s)", name, d->name);
  401. spin_lock(tmpfs_lock);
  402. foreach(f, d->files) {
  403. struct tmpfs_file * t = (struct tmpfs_file *)f->value;
  404. if (!strcmp(name, t->name)) {
  405. spin_unlock(tmpfs_lock);
  406. debug_print(WARNING, "... already exists.");
  407. return -EEXIST; /* Already exists */
  408. }
  409. }
  410. spin_unlock(tmpfs_lock);
  411. debug_print(NOTICE, "... creating a new directory.");
  412. struct tmpfs_dir * out = tmpfs_dir_new(name, d);
  413. out->mask = permission;
  414. out->uid = current_process->user;
  415. out->gid = current_process->user;
  416. spin_lock(tmpfs_lock);
  417. list_insert(d->files, out);
  418. spin_unlock(tmpfs_lock);
  419. return 0;
  420. }
  421. static fs_node_t * tmpfs_from_dir(struct tmpfs_dir * d) {
  422. fs_node_t * fnode = malloc(sizeof(fs_node_t));
  423. memset(fnode, 0x00, sizeof(fs_node_t));
  424. fnode->inode = 0;
  425. strcpy(fnode->name, "tmp");
  426. fnode->mask = d->mask;
  427. fnode->uid = d->uid;
  428. fnode->gid = d->gid;
  429. fnode->device = d;
  430. fnode->atime = d->atime;
  431. fnode->mtime = d->mtime;
  432. fnode->ctime = d->ctime;
  433. fnode->flags = FS_DIRECTORY;
  434. fnode->read = NULL;
  435. fnode->write = NULL;
  436. fnode->open = NULL;
  437. fnode->close = NULL;
  438. fnode->readdir = readdir_tmpfs;
  439. fnode->finddir = finddir_tmpfs;
  440. fnode->create = create_tmpfs;
  441. fnode->unlink = unlink_tmpfs;
  442. fnode->mkdir = mkdir_tmpfs;
  443. fnode->nlink = 1; /* should be "number of children that are directories + 1" */
  444. fnode->symlink = symlink_tmpfs;
  445. fnode->chown = chown_tmpfs;
  446. fnode->chmod = chmod_tmpfs;
  447. return fnode;
  448. }
  449. fs_node_t * tmpfs_create(char * name) {
  450. tmpfs_root = tmpfs_dir_new(name, NULL);
  451. tmpfs_root->mask = 0777;
  452. tmpfs_root->uid = 0;
  453. tmpfs_root->gid = 0;
  454. return tmpfs_from_dir(tmpfs_root);
  455. }
  456. fs_node_t * tmpfs_mount(char * device, char * mount_path) {
  457. fs_node_t * fs = tmpfs_create(device);
  458. return fs;
  459. }
  460. static int tmpfs_initialize(void) {
  461. buf_space = (void*)kvmalloc(BLOCKSIZE);
  462. vfs_mount("/tmp", tmpfs_create("tmp"));
  463. vfs_mount("/var", tmpfs_create("var"));
  464. vfs_register("tmpfs", tmpfs_mount);
  465. return 0;
  466. }
  467. static int tmpfs_finalize(void) {
  468. return 0;
  469. }
  470. MODULE_DEF(tmpfs, tmpfs_initialize, tmpfs_finalize);