tmpfs.c 15 KB

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