Advertisement
VladNitu

os_5_Vald

Mar 12th, 2024
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.38 KB | None | 0 0
  1. /**
  2. * @file os_assignment.c
  3. *
  4. * This is the file you will be working in. You will have to complete the (partial) implementation
  5. * given below. Any functions you have to complete are clearly marked as such.
  6. *
  7. * Good luck!
  8. */
  9.  
  10. #include "dfs.h"
  11. #include "dfs-path.h"
  12. #include "dfs-helpers.h"
  13. #include "strdup.h"
  14.  
  15. #define FUSE_USE_VERSION 31
  16. #include <fuse.h>
  17.  
  18. #include <assert.h>
  19. #include <errno.h>
  20. #include <fcntl.h>
  21. #include <stddef.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25.  
  26. /**
  27. * path = [<parent_path>/<entity_name>]
  28. * entity_name can be either the name of a file, or the name of a directory.
  29. */
  30. static int split_path(const char *path, char *parent_path, char *entity_name) {
  31. char *last_slash = strrchr(path, '/'); // Obtain a pointer to the last occurence of "/"
  32. if (NULL == last_slash) { // No "/" in the provided path
  33. return -1;
  34. }
  35. size_t parent_len = last_slash - path;
  36. size_t entity_len = strlen(path) - parent_len - 1;
  37. strncpy(parent_path, path, parent_len);
  38. parent_path[parent_len] = '\0';
  39. strncpy(entity_name, last_slash + 1, entity_len);
  40. entity_name[entity_len] = '\0';
  41. return 0;
  42. }
  43.  
  44.  
  45. size_t min(size_t a, size_t b) {
  46. return (a < b) ? a : b;
  47. }
  48.  
  49. /**
  50. * The root directory of the file system.
  51. */
  52. DfsDir *root_dir;
  53.  
  54. /**
  55. * Initializes the default file system.
  56. *
  57. * This fills the DFS with a number of directories and files. Add, remove and rename some files
  58. * and directories, if you wish.
  59. *
  60. * Note how this creates a top-level directory root_dir. You will have to use this directory.
  61. */
  62. void create_dfs(void) {
  63. // Define some directories
  64. root_dir = dfs_create_dir();
  65.  
  66. DfsDir *files_dir = dfs_create_dir();
  67. DfsDir *pictures_dir = dfs_create_dir();
  68. DfsDir *important_files_dir = dfs_create_dir();
  69. assert(files_dir && pictures_dir && important_files_dir);
  70.  
  71. // Define some files
  72. DfsFile *welcome_file = make_file("Welcome to the DFS.\n");
  73. DfsFile *budget_file = make_file(budget_text);
  74. DfsFile *hello_1_file = make_file("Hello there!\n");
  75. DfsFile *hello_2_file = make_file("Hei siellä!\n");
  76. DfsFile *image_1_file = make_binary_file(image_1, sizeof(image_1));
  77. DfsFile *image_2_file = make_binary_file(image_2, sizeof(image_2));
  78. assert(
  79. welcome_file && budget_file && hello_1_file && hello_2_file && image_1_file && image_2_file
  80. );
  81.  
  82. // Add everything together
  83. assert_dfs_ok(dfs_add_dir(root_dir, "Files", files_dir));
  84. assert_dfs_ok(dfs_add_dir(root_dir, "Pictures", pictures_dir));
  85.  
  86. assert_dfs_ok(dfs_add_dir(files_dir, "Important files", important_files_dir));
  87.  
  88. assert_dfs_ok(dfs_add_file(root_dir, "README.txt", welcome_file));
  89. assert_dfs_ok(dfs_add_file(files_dir, "hello.txt", hello_1_file));
  90. assert_dfs_ok(dfs_add_file(files_dir, "hello2.txt", hello_2_file));
  91. assert_dfs_ok(dfs_add_file(important_files_dir, "budget.csv", budget_file));
  92. assert_dfs_ok(dfs_add_file(pictures_dir, "some image.png", image_1_file));
  93. assert_dfs_ok(dfs_add_file(pictures_dir, "some other image.png", image_2_file));
  94. }
  95.  
  96. /**
  97. * @defgroup Assignment The assignment
  98. * These functions must be completed for the assignment.
  99. *
  100. * The functions here directly correspond to the syscall with the same name, unless noted otherwise.
  101. * @{
  102. */
  103.  
  104. /**
  105. * Lists files/directories available in a specific directory.
  106. *
  107. * This function is specific to FUSE; there is no directly corresponding system call.
  108. *
  109. * @param path directory which we are listing
  110. * @param buff buffer to fill with data
  111. * @param fill a function to help fill buffer
  112. * @param off not used
  113. * @param fi not used
  114. *
  115. * @return 0 or an appropriate error code
  116. */
  117. static int os_readdir(
  118. const char *path, void *buff, fuse_fill_dir_t fill, off_t off, struct fuse_file_info *fi
  119. ) {
  120. // Example: this is how you find the required directory.
  121. DfsDir *dir;
  122. DfsStatus dir_status = dfs_find_dir_str(root_dir, path, &dir); // Writes address of found directory to `dir`
  123. // This could fail though! You should return an error if this is so, in other functions as well
  124. if (dir_status != DFS_OK) {
  125. return -ENOENT; // Error: no entity
  126. }
  127. // Other errors are also possible
  128.  
  129. fill(buff, ".", NULL, 0);
  130. fill(buff, "..", NULL, 0);
  131.  
  132. // Iterate over all entries in a dir
  133. size_t cursor = 0;
  134. for (;;) {
  135. const char *name;
  136. DfsStatus get_next_status = dfs_get_next(dir, &cursor, &name, NULL);
  137. if (get_next_status == DFS_E_NO_MORE_ENTRIES) break;
  138. else
  139. fill(buff, name, NULL, 0);
  140. }
  141.  
  142. // Returning 0 means all-ok.
  143. return 0;
  144. }
  145.  
  146. /**
  147. * Creates a new directory.
  148. *
  149. * @param path the path to the new directory
  150. * @param mode the mode (permissions) of the new directory. Can be ignored
  151. *
  152. * @return 0 or an appropriate error code
  153. *
  154. * @see [`man 2 mkdir`](https://linux.die.net/man/2/mkdir)
  155. */
  156. static int os_mkdir(const char *path, mode_t mode) {
  157. // Example: this is how you find the required directory.
  158. char dir_name[128] = {0};
  159. char parent_path[128] = {0};
  160.  
  161. if (split_path(path, parent_path, dir_name) != 0)
  162. return -ENOENT;
  163.  
  164. DfsDir *parent_dir;
  165.  
  166. DfsStatus dir_status = dfs_find_dir_str(root_dir, parent_path, &parent_dir); // Writes address of found directory to `parent_dir`
  167. if (dir_status != DFS_OK)
  168. return -ENOENT; // Error: no entity
  169.  
  170.  
  171. DfsDir *new_dir = dfs_create_dir(); // Create new directory
  172.  
  173. // Add it to parent directory
  174. DfsStatus add_dir_status = dfs_add_dir(parent_dir, dir_name, new_dir);
  175. if (add_dir_status != DFS_OK) {
  176. dfs_destroy_dir(new_dir);
  177. return -EEXIST; // Error: Did already exists -> dir name conflict
  178. }
  179.  
  180. return 0;
  181.  
  182. }
  183.  
  184. /**
  185. * Removes a directory.
  186. *
  187. * This must return an error if the directory is not empty.
  188. *
  189. * @param path the path to the directory to remove
  190. *
  191. * @return 0 or an appropriate error code
  192. *
  193. * @see [`man 2 rmdir`](https://linux.die.net/man/2/rmdir)
  194. */
  195. static int os_rmdir(const char *path) {
  196.  
  197. char parent_path[128] = {0};
  198. char dir_name[128] = {0};
  199.  
  200. if (split_path(path, parent_path, dir_name) != 0)
  201. return -ENOENT;
  202.  
  203. DfsDir *parent_dir;
  204. DfsStatus dir_status = dfs_find_dir_str(root_dir, parent_path, &parent_dir); // Writes address of found directory to `parent_dir`
  205. if (dir_status != DFS_OK)
  206. return -ENOENT; // Error: no entity
  207.  
  208. DfsDir *dir_removed;
  209. DfsStatus find_dir = dfs_find_dir_str(parent_dir, dir_name, &dir_removed);
  210. if (find_dir == DFS_E_ENTRY_DOES_NOT_EXIST)
  211. return -ENOENT;
  212.  
  213. if (dir_removed->entries.num_entries != 0) { // dir is not empty
  214. return -ENOTEMPTY;
  215. }
  216. DfsStatus removed_status = dfs_remove_dir(parent_dir, dir_name, NULL);
  217. if (removed_status != DFS_OK)
  218. return -ENOENT;
  219. return 0;
  220. }
  221.  
  222. /**
  223. * Reads (part of) a file.
  224. *
  225. * This may return fewer bytes than requested, but only if the end of the file was reached. Note
  226. * that the system call behaves differently!
  227. *
  228. * @param path the path to the file
  229. * @param buff where to write the read data
  230. * @param size the number of bytes to read
  231. * @param off where to start reading from
  232. * @param fi can be ignored
  233. *
  234. * @return the number of bytes read or an appropriate error code
  235. *
  236. * @see [`man 2 read`](https://linux.die.net/man/2/read)
  237. */
  238. static int os_read(
  239. const char *path, char *buff, size_t size, off_t off, struct fuse_file_info *fi
  240. ) {
  241. DfsFile *file;
  242. DfsStatus find_file_status = dfs_find_file_str(root_dir, path, &file); // , find the file to read from
  243. if (find_file_status != DFS_OK) {
  244. return -ENOENT; // Error: no entity
  245. }
  246.  
  247. // try to write after EOF
  248. if (off >= file->length)
  249. return 0;
  250.  
  251. size_t read_size = min(size, file->length - off);
  252.  
  253. memcpy(buff, file->contents + off, read_size); // copy the chunk of bytes to the buffer; TODO: Check if resize of buff neede
  254.  
  255. return read_size; // return # of bytes read
  256. }
  257.  
  258. /**
  259. * Writes (part of) a file.
  260. *
  261. * This must write all given bytes. Note that the system call behaves differently!
  262. *
  263. * @param path the path to the file
  264. * @param buff the data to write
  265. * @param size the number of bytes to write
  266. * @param off where to start writing from
  267. * @param fi can be ignored
  268. *
  269. * @return the number of bytes written or an appropriate error code
  270. *
  271. * @see [`man 2 write`](https://linux.die.net/man/2/write)
  272. */
  273. static int os_write(
  274. const char *path, const char *buff, size_t size, off_t off, struct fuse_file_info *fi
  275. ) {
  276. DfsFile *file;
  277. DfsStatus find_file_status = dfs_find_file_str(root_dir, path, &file); // , find the file to read from
  278. if (find_file_status != DFS_OK) {
  279. return -ENOENT; // Error: no entity
  280. }
  281.  
  282. if (file->length - off < size) { // file too short
  283. char* copy_contents = realloc(file->contents, off + size);
  284. if (!copy_contents)
  285. return -ENOMEM;
  286. memset(copy_contents + file->length, 0, (off + size) - file->length);
  287.  
  288. file->contents = copy_contents;
  289. file->length = off + size;
  290. }
  291.  
  292. memcpy(file->contents + off, buff, size);
  293. return size; // Return # of written bytes
  294. }
  295.  
  296. /**
  297. * Creates a file.
  298. *
  299. * @param path the path to the file
  300. * @param mode the mode (permissions) of the new file. Can be ignored
  301. * @param fi can be ignored
  302. *
  303. * @return 0 or an appropriate error code
  304. *
  305. * @see [`man 2 creat`](https://linux.die.net/man/2/creat) (that is not a typo)
  306. */
  307. static int os_create(const char *path, mode_t mode, struct fuse_file_info *fi) {
  308. char file_name[128] = {0};
  309. char parent_path[128] = {0};
  310.  
  311. if (split_path(path, parent_path, file_name) != 0)
  312. return -ENOENT;
  313.  
  314. DfsDir *parent_dir;
  315.  
  316. DfsStatus dir_status = dfs_find_dir_str(root_dir, parent_path, &parent_dir); // Writes address of found directory to `parent_dir`
  317. if (dir_status != DFS_OK)
  318. return -ENOENT; // Error: no entity
  319.  
  320.  
  321. DfsFile *new_file = dfs_create_file(); // Create new file
  322. if (!new_file)
  323. return -ENOSPC; // mem. not allocd
  324.  
  325. // Add it to parent directory
  326. DfsStatus add_file_status = dfs_add_file(parent_dir, file_name, new_file);
  327. if (add_file_status != DFS_OK) {
  328. dfs_destroy_file(new_file);
  329. return -EEXIST; // Error: File already exists -> dir name conflict
  330. }
  331.  
  332. return 0;
  333. }
  334.  
  335. /**
  336. * Unlinks (deletes) a file.
  337. *
  338. * @param path the path to the file
  339. *
  340. * @return 0 or appropriate error code
  341. *
  342. * @see [`man 2 unlink`](https://linux.die.net/man/2/unlink)
  343. */
  344. static int os_unlink(const char *path) {
  345.  
  346. char file_name[128] = {0};
  347. char parent_path[128] = {0};
  348.  
  349. if (split_path(path, parent_path, file_name) != 0)
  350. return -ENOENT;
  351.  
  352.  
  353. DfsDir *parent_dir;
  354.  
  355. DfsStatus dir_status = dfs_find_dir_str(root_dir, parent_path, &parent_dir); // Writes address of found directory to `parent_dir`
  356. if (dir_status != DFS_OK)
  357. return -ENOENT; // Error: no entity
  358.  
  359.  
  360. DfsStatus removed_status = dfs_remove_file(parent_dir, file_name, NULL);
  361. if (removed_status != DFS_OK)
  362. return -ENOENT;
  363.  
  364. return 0;
  365. }
  366.  
  367. /// @}
  368. /**
  369. * @defgroup Predefined Predefined functions
  370. * You do not have to modify these for the assignment.
  371. * @{
  372. */
  373.  
  374. /**
  375. * Counts the number of (direct) subdirectories of a directory.
  376. *
  377. * @param dir the directory to scan
  378. *
  379. * @return the number of subdirectories
  380. */
  381. static size_t os_count_subdirs(DfsDir *dir) {
  382. size_t cursor = 0;
  383. size_t num_subdirs = 0;
  384. DfsEntry *entry;
  385. for (;;) {
  386. DfsStatus get_next_status = dfs_get_next(dir, &cursor, NULL, &entry);
  387. if (get_next_status == DFS_E_NO_MORE_ENTRIES) break;
  388. if (entry->type == DFS_ENT_DIR) ++num_subdirs;
  389. }
  390.  
  391. return num_subdirs;
  392. }
  393.  
  394. /**
  395. * Retrieves file/directory attributes.
  396. *
  397. * @param path the path for which attributes are requested
  398. * @param st struct to be filled with attributes
  399. *
  400. * @return 0 or an appropriate error code
  401. *
  402. * @see [`man 2 stat`](https://linux.die.net/man/2/stat)
  403. */
  404. static int os_getattr(const char *path, struct stat *st) {
  405. DfsEntry *entry;
  406. DfsStatus entry_status = dfs_find_entry_str(root_dir, path, &entry);
  407. if (entry_status == DFS_E_MALLOC_FAILURE) return -ENOMEM;
  408. if (entry_status == DFS_E_ENTRY_DOES_NOT_EXIST) return -ENOENT;
  409.  
  410. st->st_uid = entry->user;
  411. st->st_gid = entry->group;
  412. st->st_ctime = entry->ctime.tv_sec;
  413. st->st_mtime = entry->mtime.tv_sec;
  414. st->st_atime = entry->atime.tv_sec;
  415.  
  416. if (entry->type == DFS_ENT_FILE) {
  417. DfsFile *file = (DfsFile *) entry;
  418. st->st_mode = S_IFREG | entry->mode; // Requested item is (probably) a regular file
  419. st->st_nlink = 1; // Number of hard links: a file has at least one
  420. st->st_size = file->length;
  421. } else if (entry->type == DFS_ENT_DIR) {
  422. DfsDir *dir = (DfsDir *) entry;
  423. st->st_mode = S_IFDIR | entry->mode;
  424. st->st_nlink = os_count_subdirs(dir) + 1;
  425. st->st_size = dfs_get_dir_size(dir) * 32; // Estimate :)
  426. }
  427.  
  428. return 0;
  429. }
  430.  
  431. /**
  432. * Resizes a file.
  433. *
  434. * @param path the path to the file
  435. * @param off the new size of the file in bytes
  436. *
  437. * @return 0 or an appropriate error code
  438. *
  439. * @see [`man 2 truncate`](https://linux.die.net/man/2/truncate)
  440. */
  441. static int os_truncate(const char *path, off_t off) {
  442. DfsFile *file;
  443. DfsStatus file_status = dfs_find_file_str(root_dir, path, &file);
  444. if (file_status == DFS_E_MALLOC_FAILURE) return -ENOMEM;
  445. if (file_status == DFS_E_ENTRY_DOES_NOT_EXIST) return -ENOENT;
  446. if (file_status == DFS_E_NOT_A_FILE) return -EISDIR;
  447.  
  448. if (off == 0) {
  449. free(file->contents);
  450. file->contents = NULL;
  451. file->length = 0;
  452. return 0;
  453. }
  454.  
  455. char *new_contents = realloc(file->contents, off);
  456. if (!new_contents) return -ENOMEM;
  457.  
  458. file->contents = new_contents;
  459.  
  460. if ((size_t) off > file->length) {
  461. memset(file->contents + file->length, 0, off - file->length);
  462. }
  463.  
  464. file->length = off;
  465.  
  466. return 0;
  467. }
  468.  
  469. /**
  470. * Sets extended attributes on an entry.
  471. *
  472. * This is currently not implemented (and you do not have to implement it yourself).
  473. *
  474. * @param path the path to the file
  475. * @param name the name of the attribute
  476. * @param value the value of the attribute
  477. * @param length the length of the attribute
  478. * @param flags any flags
  479. *
  480. * @return 0 or an appropriate error code
  481. */
  482. static int os_setxattr(
  483. const char *path, const char *name, const char *value, size_t length, int flags
  484. ) {
  485. return -ENOTSUP;
  486. }
  487.  
  488. /**
  489. * Changes the mode (permissions) of an entry.
  490. *
  491. * @param path the path to the file
  492. * @param mode the new mode
  493. *
  494. * @return 0 or an appropriate error code
  495. *
  496. * @see [`man 2 chmod`](https://linux.die.net/man/2/chmod)
  497. */
  498. static int os_chmod(const char *path, mode_t mode) {
  499. DfsEntry *entry;
  500. DfsStatus entry_status = dfs_find_entry_str(root_dir, path, &entry);
  501. if (entry_status == DFS_E_MALLOC_FAILURE) return -ENOMEM;
  502. if (entry_status == DFS_E_ENTRY_DOES_NOT_EXIST) return -ENOENT;
  503.  
  504. entry->mode = mode;
  505.  
  506. return 0;
  507. }
  508.  
  509. /**
  510. * Changes the owning user and group of an entry.
  511. *
  512. * @param path the path to the file
  513. * @param uid the new owning user
  514. * @param gid the new owning group
  515. *
  516. * @return 0 or an appropriate error code
  517. *
  518. * @see [`man 2 chown`](https://linux.die.net/man/2/chown)
  519. */
  520. static int os_chown(const char *path, uid_t uid, gid_t gid) {
  521. DfsEntry *entry;
  522. DfsStatus entry_status = dfs_find_entry_str(root_dir, path, &entry);
  523. if (entry_status == DFS_E_MALLOC_FAILURE) return -ENOMEM;
  524. if (entry_status == DFS_E_ENTRY_DOES_NOT_EXIST) return -ENOENT;
  525.  
  526. entry->user = uid;
  527. entry->group = gid;
  528.  
  529. return 0;
  530. }
  531.  
  532. /**
  533. * Changes the atime and mtime of an entry.
  534. *
  535. * @param path the path to the entry
  536. * @param tv the new atime and mtime values, respectively
  537. *
  538. * @return 0 or an appropriate error code
  539. *
  540. * @see [`man 2 utimensat`](https://linux.die.net/man/2/utimensat)
  541. */
  542. static int os_utimens(const char *path, const struct timespec tv[2]) {
  543. DfsEntry *entry;
  544. DfsStatus entry_status = dfs_find_entry_str(root_dir, path, &entry);
  545. if (entry_status == DFS_E_MALLOC_FAILURE) return -ENOMEM;
  546. if (entry_status == DFS_E_ENTRY_DOES_NOT_EXIST) return -ENOENT;
  547.  
  548. entry->atime = tv[0];
  549. entry->mtime = tv[1];
  550.  
  551. return 0;
  552. }
  553.  
  554. /**
  555. * Here we define the operations FUSE has access to.
  556. *
  557. * Not all members have been populated and you should not call those!
  558. */
  559. static struct fuse_operations operations = {
  560. .getattr = os_getattr,
  561. .readdir = os_readdir,
  562. .read = os_read,
  563. .mkdir = os_mkdir,
  564. .rmdir = os_rmdir,
  565. .write = os_write,
  566. .setxattr = os_setxattr,
  567. .truncate = os_truncate,
  568. .chmod = os_chmod,
  569. .chown = os_chown,
  570. .utimens = os_utimens,
  571. .create = os_create,
  572. .unlink = os_unlink
  573. };
  574.  
  575. /**
  576. * Main function.
  577. *
  578. * @param argc the number of arguments
  579. * @param argv the arguments
  580. */
  581. int main(int argc, char **argv) {
  582. // Setup dumb file system
  583. create_dfs();
  584.  
  585. // Pass arguments on to FUSE.
  586. return fuse_main(argc, argv, &operations, NULL);
  587. }
  588.  
  589. /// @}
  590.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement