《操作系统》的实验代码。
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

582 行
19 KiB

  1. #define _GNU_SOURCE
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <stdint.h>
  6. #include <limits.h>
  7. #include <dirent.h>
  8. #include <unistd.h>
  9. #include <fcntl.h>
  10. #include <sys/stat.h>
  11. #include <sys/types.h>
  12. #include <errno.h>
  13. #include <assert.h>
  14. typedef int bool;
  15. #define __error(msg, quit, ...) \
  16. do { \
  17. fprintf(stderr, #msg ": function %s - line %d: ", __FUNCTION__, __LINE__); \
  18. if (errno != 0) { \
  19. fprintf(stderr, "[error] %s: ", strerror(errno)); \
  20. } \
  21. fprintf(stderr, "\n\t"), fprintf(stderr, __VA_ARGS__); \
  22. errno = 0; \
  23. if (quit) { \
  24. exit(-1); \
  25. } \
  26. } while (0)
  27. #define warn(...) __error(warn, 0, __VA_ARGS__)
  28. #define bug(...) __error(bug, 1, __VA_ARGS__)
  29. #define static_assert(x) \
  30. switch (x) {case 0: case (x): ; }
  31. /* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
  32. #define GOLDEN_RATIO_PRIME_32 0x9e370001UL
  33. #define HASH_SHIFT 10
  34. #define HASH_LIST_SIZE (1 << HASH_SHIFT)
  35. static inline uint32_t
  36. __hash32(uint32_t val, unsigned int bits) {
  37. uint32_t hash = val * GOLDEN_RATIO_PRIME_32;
  38. return (hash >> (32 - bits));
  39. }
  40. static uint32_t
  41. hash32(uint32_t val) {
  42. return __hash32(val, HASH_SHIFT);
  43. }
  44. static uint32_t
  45. hash64(uint64_t val) {
  46. return __hash32((uint32_t)val, HASH_SHIFT);
  47. }
  48. void *
  49. safe_malloc(size_t size) {
  50. void *ret;
  51. if ((ret = malloc(size)) == NULL) {
  52. bug("malloc %lu bytes failed.\n", (long unsigned)size);
  53. }
  54. return ret;
  55. }
  56. char *
  57. safe_strdup(const char *str) {
  58. char *ret;
  59. if ((ret = strdup(str)) == NULL) {
  60. bug("strdup failed: %s\n", str);
  61. }
  62. return ret;
  63. }
  64. struct stat *
  65. safe_stat(const char *filename) {
  66. static struct stat __stat;
  67. if (stat(filename, &__stat) != 0) {
  68. bug("stat %s failed.\n", filename);
  69. }
  70. return &__stat;
  71. }
  72. struct stat *
  73. safe_fstat(int fd) {
  74. static struct stat __stat;
  75. if (fstat(fd, &__stat) != 0) {
  76. bug("fstat %d failed.\n", fd);
  77. }
  78. return &__stat;
  79. }
  80. struct stat *
  81. safe_lstat(const char *name) {
  82. static struct stat __stat;
  83. if (lstat(name, &__stat) != 0) {
  84. bug("lstat '%s' failed.\n", name);
  85. }
  86. return &__stat;
  87. }
  88. void
  89. safe_fchdir(int fd) {
  90. if (fchdir(fd) != 0) {
  91. bug("fchdir failed %d.\n", fd);
  92. }
  93. }
  94. #define SFS_MAGIC 0x2f8dbe2a
  95. #define SFS_NDIRECT 12
  96. #define SFS_BLKSIZE 4096 // 4K
  97. #define SFS_MAX_NBLKS (1024UL * 512) // 4K * 512K
  98. #define SFS_MAX_INFO_LEN 31
  99. #define SFS_MAX_FNAME_LEN 255
  100. #define SFS_MAX_FILE_SIZE (1024UL * 1024 * 128) // 128M
  101. #define SFS_BLKBITS (SFS_BLKSIZE * CHAR_BIT)
  102. #define SFS_TYPE_FILE 1
  103. #define SFS_TYPE_DIR 2
  104. #define SFS_TYPE_LINK 3
  105. #define SFS_BLKN_SUPER 0
  106. #define SFS_BLKN_ROOT 1
  107. #define SFS_BLKN_FREEMAP 2
  108. struct cache_block {
  109. uint32_t ino;
  110. struct cache_block *hash_next;
  111. void *cache;
  112. };
  113. struct cache_inode {
  114. struct inode {
  115. uint32_t size;
  116. uint16_t type;
  117. uint16_t nlinks;
  118. uint32_t blocks;
  119. uint32_t direct[SFS_NDIRECT];
  120. uint32_t indirect;
  121. uint32_t db_indirect;
  122. } inode;
  123. ino_t real;
  124. uint32_t ino;
  125. uint32_t nblks;
  126. struct cache_block *l1, *l2;
  127. struct cache_inode *hash_next;
  128. };
  129. struct sfs_fs {
  130. struct {
  131. uint32_t magic;
  132. uint32_t blocks;
  133. uint32_t unused_blocks;
  134. char info[SFS_MAX_INFO_LEN + 1];
  135. } super;
  136. struct subpath {
  137. struct subpath *next, *prev;
  138. char *subname;
  139. } __sp_nil, *sp_root, *sp_end;
  140. int imgfd;
  141. uint32_t ninos, next_ino;
  142. struct cache_inode *root;
  143. struct cache_inode *inodes[HASH_LIST_SIZE];
  144. struct cache_block *blocks[HASH_LIST_SIZE];
  145. };
  146. struct sfs_entry {
  147. uint32_t ino;
  148. char name[SFS_MAX_FNAME_LEN + 1];
  149. };
  150. static uint32_t
  151. sfs_alloc_ino(struct sfs_fs *sfs) {
  152. if (sfs->next_ino < sfs->ninos) {
  153. sfs->super.unused_blocks --;
  154. return sfs->next_ino ++;
  155. }
  156. bug("out of disk space.\n");
  157. }
  158. static struct cache_block *
  159. alloc_cache_block(struct sfs_fs *sfs, uint32_t ino) {
  160. struct cache_block *cb = safe_malloc(sizeof(struct cache_block));
  161. cb->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs);
  162. cb->cache = memset(safe_malloc(SFS_BLKSIZE), 0, SFS_BLKSIZE);
  163. struct cache_block **head = sfs->blocks + hash32(ino);
  164. cb->hash_next = *head, *head = cb;
  165. return cb;
  166. }
  167. struct cache_block *
  168. search_cache_block(struct sfs_fs *sfs, uint32_t ino) {
  169. struct cache_block *cb = sfs->blocks[hash32(ino)];
  170. while (cb != NULL && cb->ino != ino) {
  171. cb = cb->hash_next;
  172. }
  173. return cb;
  174. }
  175. static struct cache_inode *
  176. alloc_cache_inode(struct sfs_fs *sfs, ino_t real, uint32_t ino, uint16_t type) {
  177. struct cache_inode *ci = safe_malloc(sizeof(struct cache_inode));
  178. ci->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs);
  179. ci->real = real, ci->nblks = 0, ci->l1 = ci->l2 = NULL;
  180. struct inode *inode = &(ci->inode);
  181. memset(inode, 0, sizeof(struct inode));
  182. inode->type = type;
  183. struct cache_inode **head = sfs->inodes + hash64(real);
  184. ci->hash_next = *head, *head = ci;
  185. return ci;
  186. }
  187. struct cache_inode *
  188. search_cache_inode(struct sfs_fs *sfs, ino_t real) {
  189. struct cache_inode *ci = sfs->inodes[hash64(real)];
  190. while (ci != NULL && ci->real != real) {
  191. ci = ci->hash_next;
  192. }
  193. return ci;
  194. }
  195. struct sfs_fs *
  196. create_sfs(int imgfd) {
  197. uint32_t ninos, next_ino;
  198. struct stat *stat = safe_fstat(imgfd);
  199. if ((ninos = stat->st_size / SFS_BLKSIZE) > SFS_MAX_NBLKS) {
  200. ninos = SFS_MAX_NBLKS;
  201. warn("img file is too big (%llu bytes, only use %u blocks).\n",
  202. (unsigned long long)stat->st_size, ninos);
  203. }
  204. if ((next_ino = SFS_BLKN_FREEMAP + (ninos + SFS_BLKBITS - 1) / SFS_BLKBITS) >= ninos) {
  205. bug("img file is too small (%llu bytes, %u blocks, bitmap use at least %u blocks).\n",
  206. (unsigned long long)stat->st_size, ninos, next_ino - 2);
  207. }
  208. struct sfs_fs *sfs = safe_malloc(sizeof(struct sfs_fs));
  209. sfs->super.magic = SFS_MAGIC;
  210. sfs->super.blocks = ninos, sfs->super.unused_blocks = ninos - next_ino;
  211. snprintf(sfs->super.info, SFS_MAX_INFO_LEN, "simple file system");
  212. sfs->ninos = ninos, sfs->next_ino = next_ino, sfs->imgfd = imgfd;
  213. sfs->sp_root = sfs->sp_end = &(sfs->__sp_nil);
  214. sfs->sp_end->prev = sfs->sp_end->next = NULL;
  215. int i;
  216. for (i = 0; i < HASH_LIST_SIZE; i ++) {
  217. sfs->inodes[i] = NULL;
  218. sfs->blocks[i] = NULL;
  219. }
  220. sfs->root = alloc_cache_inode(sfs, 0, SFS_BLKN_ROOT, SFS_TYPE_DIR);
  221. return sfs;
  222. }
  223. static void
  224. subpath_push(struct sfs_fs *sfs, const char *subname) {
  225. struct subpath *subpath = safe_malloc(sizeof(struct subpath));
  226. subpath->subname = safe_strdup(subname);
  227. sfs->sp_end->next = subpath;
  228. subpath->prev = sfs->sp_end;
  229. subpath->next = NULL;
  230. sfs->sp_end = subpath;
  231. }
  232. static void
  233. subpath_pop(struct sfs_fs *sfs) {
  234. assert(sfs->sp_root != sfs->sp_end);
  235. struct subpath *subpath = sfs->sp_end;
  236. sfs->sp_end = sfs->sp_end->prev, sfs->sp_end->next = NULL;
  237. free(subpath->subname), free(subpath);
  238. }
  239. static void
  240. subpath_show(FILE *fout, struct sfs_fs *sfs, const char *name) {
  241. struct subpath *subpath = sfs->sp_root;
  242. fprintf(fout, "current is: /");
  243. while ((subpath = subpath->next) != NULL) {
  244. fprintf(fout, "%s/", subpath->subname);
  245. }
  246. if (name != NULL) {
  247. fprintf(fout, "%s", name);
  248. }
  249. fprintf(fout, "\n");
  250. }
  251. static void
  252. write_block(struct sfs_fs *sfs, void *data, size_t len, uint32_t ino) {
  253. assert(len <= SFS_BLKSIZE && ino < sfs->ninos);
  254. static char buffer[SFS_BLKSIZE];
  255. if (len != SFS_BLKSIZE) {
  256. memset(buffer, 0, sizeof(buffer));
  257. data = memcpy(buffer, data, len);
  258. }
  259. off_t offset = (off_t)ino * SFS_BLKSIZE;
  260. ssize_t ret;
  261. if ((ret = pwrite(sfs->imgfd, data, SFS_BLKSIZE, offset)) != SFS_BLKSIZE) {
  262. bug("write %u block failed: (%d/%d).\n", ino, (int)ret, SFS_BLKSIZE);
  263. }
  264. }
  265. static void
  266. flush_cache_block(struct sfs_fs *sfs, struct cache_block *cb) {
  267. write_block(sfs, cb->cache, SFS_BLKSIZE, cb->ino);
  268. }
  269. static void
  270. flush_cache_inode(struct sfs_fs *sfs, struct cache_inode *ci) {
  271. write_block(sfs, &(ci->inode), sizeof(ci->inode), ci->ino);
  272. }
  273. void
  274. close_sfs(struct sfs_fs *sfs) {
  275. static char buffer[SFS_BLKSIZE];
  276. uint32_t i, j, ino = SFS_BLKN_FREEMAP;
  277. uint32_t ninos = sfs->ninos, next_ino = sfs->next_ino;
  278. for (i = 0; i < ninos; ino ++, i += SFS_BLKBITS) {
  279. memset(buffer, 0, sizeof(buffer));
  280. if (i + SFS_BLKBITS > next_ino) {
  281. uint32_t start = 0, end = SFS_BLKBITS;
  282. if (i < next_ino) {
  283. start = next_ino - i;
  284. }
  285. if (i + SFS_BLKBITS > ninos) {
  286. end = ninos - i;
  287. }
  288. uint32_t *data = (uint32_t *)buffer;
  289. const uint32_t bits = sizeof(bits) * CHAR_BIT;
  290. for (j = start; j < end; j ++) {
  291. data[j / bits] |= (1 << (j % bits));
  292. }
  293. }
  294. write_block(sfs, buffer, sizeof(buffer), ino);
  295. }
  296. write_block(sfs, &(sfs->super), sizeof(sfs->super), SFS_BLKN_SUPER);
  297. for (i = 0; i < HASH_LIST_SIZE; i ++) {
  298. struct cache_block *cb = sfs->blocks[i];
  299. while (cb != NULL) {
  300. flush_cache_block(sfs, cb);
  301. cb = cb->hash_next;
  302. }
  303. struct cache_inode *ci = sfs->inodes[i];
  304. while (ci != NULL) {
  305. flush_cache_inode(sfs, ci);
  306. ci = ci->hash_next;
  307. }
  308. }
  309. }
  310. struct sfs_fs *
  311. open_img(const char *imgname) {
  312. const char *expect = ".img", *ext = imgname + strlen(imgname) - strlen(expect);
  313. if (ext <= imgname || strcmp(ext, expect) != 0) {
  314. bug("invalid .img file name '%s'.\n", imgname);
  315. }
  316. int imgfd;
  317. if ((imgfd = open(imgname, O_WRONLY)) < 0) {
  318. bug("open '%s' failed.\n", imgname);
  319. }
  320. return create_sfs(imgfd);
  321. }
  322. #define open_bug(sfs, name, ...) \
  323. do { \
  324. subpath_show(stderr, sfs, name); \
  325. bug(__VA_ARGS__); \
  326. } while (0)
  327. #define show_fullpath(sfs, name) subpath_show(stderr, sfs, name)
  328. void open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent);
  329. void open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd);
  330. void open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename);
  331. #define SFS_BLK_NENTRY (SFS_BLKSIZE / sizeof(uint32_t))
  332. #define SFS_L0_NBLKS SFS_NDIRECT
  333. #define SFS_L1_NBLKS (SFS_BLK_NENTRY + SFS_L0_NBLKS)
  334. #define SFS_L2_NBLKS (SFS_BLK_NENTRY * SFS_BLK_NENTRY + SFS_L1_NBLKS)
  335. #define SFS_LN_NBLKS (SFS_MAX_FILE_SIZE / SFS_BLKSIZE)
  336. static void
  337. update_cache(struct sfs_fs *sfs, struct cache_block **cbp, uint32_t *inop) {
  338. uint32_t ino = *inop;
  339. struct cache_block *cb = *cbp;
  340. if (ino == 0) {
  341. cb = alloc_cache_block(sfs, 0);
  342. ino = cb->ino;
  343. }
  344. else if (cb == NULL || cb->ino != ino) {
  345. cb = search_cache_block(sfs, ino);
  346. assert(cb != NULL && cb->ino == ino);
  347. }
  348. *cbp = cb, *inop = ino;
  349. }
  350. static void
  351. append_block(struct sfs_fs *sfs, struct cache_inode *file, size_t size, uint32_t ino, const char *filename) {
  352. static_assert(SFS_LN_NBLKS <= SFS_L2_NBLKS);
  353. assert(size <= SFS_BLKSIZE);
  354. uint32_t nblks = file->nblks;
  355. struct inode *inode = &(file->inode);
  356. if (nblks >= SFS_LN_NBLKS) {
  357. open_bug(sfs, filename, "file is too big.\n");
  358. }
  359. if (nblks < SFS_L0_NBLKS) {
  360. inode->direct[nblks] = ino;
  361. }
  362. else if (nblks < SFS_L1_NBLKS) {
  363. nblks -= SFS_L0_NBLKS;
  364. update_cache(sfs, &(file->l1), &(inode->indirect));
  365. uint32_t *data = file->l1->cache;
  366. data[nblks] = ino;
  367. }
  368. else if (nblks < SFS_L2_NBLKS) {
  369. nblks -= SFS_L1_NBLKS;
  370. update_cache(sfs, &(file->l2), &(inode->db_indirect));
  371. uint32_t *data2 = file->l2->cache;
  372. update_cache(sfs, &(file->l1), &data2[nblks / SFS_BLK_NENTRY]);
  373. uint32_t *data1 = file->l1->cache;
  374. data1[nblks % SFS_BLK_NENTRY] = ino;
  375. }
  376. file->nblks ++;
  377. inode->size += size;
  378. inode->blocks ++;
  379. }
  380. static void
  381. add_entry(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *file, const char *name) {
  382. static struct sfs_entry __entry, *entry = &__entry;
  383. assert(current->inode.type == SFS_TYPE_DIR && strlen(name) <= SFS_MAX_FNAME_LEN);
  384. entry->ino = file->ino, strcpy(entry->name, name);
  385. uint32_t entry_ino = sfs_alloc_ino(sfs);
  386. write_block(sfs, entry, sizeof(entry->name), entry_ino);
  387. append_block(sfs, current, sizeof(entry->name), entry_ino, name);
  388. file->inode.nlinks ++;
  389. }
  390. static void
  391. add_dir(struct sfs_fs *sfs, struct cache_inode *parent, const char *dirname, int curfd, int fd, ino_t real) {
  392. assert(search_cache_inode(sfs, real) == NULL);
  393. struct cache_inode *current = alloc_cache_inode(sfs, real, 0, SFS_TYPE_DIR);
  394. safe_fchdir(fd), subpath_push(sfs, dirname);
  395. open_dir(sfs, current, parent);
  396. safe_fchdir(curfd), subpath_pop(sfs);
  397. add_entry(sfs, parent, current, dirname);
  398. }
  399. static void
  400. add_file(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, int fd, ino_t real) {
  401. struct cache_inode *file;
  402. if ((file = search_cache_inode(sfs, real)) == NULL) {
  403. file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_FILE);
  404. open_file(sfs, file, filename, fd);
  405. }
  406. add_entry(sfs, current, file, filename);
  407. }
  408. static void
  409. add_link(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, ino_t real) {
  410. struct cache_inode *file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_LINK);
  411. open_link(sfs, file, filename);
  412. add_entry(sfs, current, file, filename);
  413. }
  414. void
  415. open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent) {
  416. DIR *dir;
  417. if ((dir = opendir(".")) == NULL) {
  418. open_bug(sfs, NULL, "opendir failed.\n");
  419. }
  420. add_entry(sfs, current, current, ".");
  421. add_entry(sfs, current, parent, "..");
  422. struct dirent *direntp;
  423. while ((direntp = readdir(dir)) != NULL) {
  424. const char *name = direntp->d_name;
  425. if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
  426. continue ;
  427. }
  428. if (name[0] == '.') {
  429. continue ;
  430. }
  431. if (strlen(name) > SFS_MAX_FNAME_LEN) {
  432. open_bug(sfs, NULL, "file name is too long: %s\n", name);
  433. }
  434. struct stat *stat = safe_lstat(name);
  435. if (S_ISLNK(stat->st_mode)) {
  436. add_link(sfs, current, name, stat->st_ino);
  437. }
  438. else {
  439. int fd;
  440. if ((fd = open(name, O_RDONLY)) < 0) {
  441. open_bug(sfs, NULL, "open failed: %s\n", name);
  442. }
  443. if (S_ISDIR(stat->st_mode)) {
  444. add_dir(sfs, current, name, dirfd(dir), fd, stat->st_ino);
  445. }
  446. else if (S_ISREG(stat->st_mode)) {
  447. add_file(sfs, current, name, fd, stat->st_ino);
  448. }
  449. else {
  450. char mode = '?';
  451. if (S_ISFIFO(stat->st_mode)) mode = 'f';
  452. if (S_ISSOCK(stat->st_mode)) mode = 's';
  453. if (S_ISCHR(stat->st_mode)) mode = 'c';
  454. if (S_ISBLK(stat->st_mode)) mode = 'b';
  455. show_fullpath(sfs, NULL);
  456. warn("unsupported mode %07x (%c): file %s\n", stat->st_mode, mode, name);
  457. }
  458. close(fd);
  459. }
  460. }
  461. closedir(dir);
  462. }
  463. void
  464. open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd) {
  465. static char buffer[SFS_BLKSIZE];
  466. ssize_t ret, last = SFS_BLKSIZE;
  467. while ((ret = read(fd, buffer, sizeof(buffer))) != 0) {
  468. assert(last == SFS_BLKSIZE);
  469. uint32_t ino = sfs_alloc_ino(sfs);
  470. write_block(sfs, buffer, ret, ino);
  471. append_block(sfs, file, ret, ino, filename);
  472. last = ret;
  473. }
  474. if (ret < 0) {
  475. open_bug(sfs, filename, "read file failed.\n");
  476. }
  477. }
  478. void
  479. open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename) {
  480. static char buffer[SFS_BLKSIZE];
  481. uint32_t ino = sfs_alloc_ino(sfs);
  482. ssize_t ret = readlink(filename, buffer, sizeof(buffer));
  483. if (ret < 0 || ret == SFS_BLKSIZE) {
  484. open_bug(sfs, filename, "read link failed, %d", (int)ret);
  485. }
  486. write_block(sfs, buffer, ret, ino);
  487. append_block(sfs, file, ret, ino, filename);
  488. }
  489. int
  490. create_img(struct sfs_fs *sfs, const char *home) {
  491. int curfd, homefd;
  492. if ((curfd = open(".", O_RDONLY)) < 0) {
  493. bug("get current fd failed.\n");
  494. }
  495. if ((homefd = open(home, O_RDONLY | O_NOFOLLOW)) < 0) {
  496. bug("open home directory '%s' failed.\n", home);
  497. }
  498. safe_fchdir(homefd);
  499. open_dir(sfs, sfs->root, sfs->root);
  500. safe_fchdir(curfd);
  501. close(curfd), close(homefd);
  502. close_sfs(sfs);
  503. return 0;
  504. }
  505. static void
  506. static_check(void) {
  507. static_assert(sizeof(off_t) == 8);
  508. static_assert(sizeof(ino_t) == 8);
  509. static_assert(SFS_MAX_NBLKS <= 0x80000000UL);
  510. static_assert(SFS_MAX_FILE_SIZE <= 0x80000000UL);
  511. }
  512. int
  513. main(int argc, char **argv) {
  514. static_check();
  515. if (argc != 3) {
  516. bug("usage: <input *.img> <input dirname>\n");
  517. }
  518. const char *imgname = argv[1], *home = argv[2];
  519. if (create_img(open_img(imgname), home) != 0) {
  520. bug("create img failed.\n");
  521. }
  522. printf("create %s (%s) successfully.\n", imgname, home);
  523. return 0;
  524. }