shim_dcache.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
  2. /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
  3. /* Copyright (C) 2014 Stony Brook University,
  4. 2017 University of North Carolina at Chapel Hill and Fortanix, Inc.
  5. This file is part of Graphene Library OS.
  6. Graphene Library OS is free software: you can redistribute it and/or
  7. modify it under the terms of the GNU Lesser General Public License
  8. as published by the Free Software Foundation, either version 3 of the
  9. License, or (at your option) any later version.
  10. Graphene Library OS is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU Lesser General Public License for more details.
  14. You should have received a copy of the GNU Lesser General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  16. /*
  17. * shim_dcache.c
  18. *
  19. * This file contains codes for maintaining directory cache in library OS.
  20. */
  21. #include <shim_types.h>
  22. #include <shim_internal.h>
  23. #include <shim_handle.h>
  24. #include <shim_fs.h>
  25. #include <shim_checkpoint.h>
  26. #include <list.h>
  27. LOCKTYPE dcache_lock;
  28. #define DCACHE_MGR_ALLOC 64
  29. #define PAGE_SIZE allocsize
  30. #define OBJ_TYPE struct shim_dentry
  31. #include <memmgr.h>
  32. static MEM_MGR dentry_mgr = NULL;
  33. struct shim_dentry * dentry_root = NULL;
  34. static inline
  35. HASHTYPE hash_dentry (struct shim_dentry * start, const char * path, int len)
  36. {
  37. return rehash_path(start ? start->rel_path.hash : 0,
  38. path, len, NULL);
  39. }
  40. static struct shim_dentry * alloc_dentry (void)
  41. {
  42. struct shim_dentry * dent =
  43. get_mem_obj_from_mgr_enlarge(dentry_mgr,
  44. size_align_up(DCACHE_MGR_ALLOC));
  45. if (!dent)
  46. return NULL;
  47. memset(dent, 0, sizeof(struct shim_dentry));
  48. dent->mode = NO_MODE;
  49. INIT_LIST_HEAD(dent, hlist);
  50. INIT_LIST_HEAD(dent, list);
  51. INIT_LISTP(&dent->children);
  52. INIT_LIST_HEAD(dent, siblings);
  53. return dent;
  54. }
  55. int init_dcache (void)
  56. {
  57. dentry_mgr = create_mem_mgr(init_align_up(DCACHE_MGR_ALLOC));
  58. create_lock(dcache_lock);
  59. dentry_root = alloc_dentry();
  60. /* The root is special; we assume it won't change or be freed, and
  61. * set its refcount to 1. */
  62. get_dentry(dentry_root);
  63. /* Initialize the root to a valid state, as a low-level lookup
  64. * will fail. */
  65. dentry_root->state |= DENTRY_VALID;
  66. /* The root should be a directory too*/
  67. dentry_root->state |= DENTRY_ISDIRECTORY;
  68. qstrsetstr(&dentry_root->name, "", 0);
  69. qstrsetstr(&dentry_root->rel_path, "", 0);
  70. get_dentry(dentry_root);
  71. return 0;
  72. }
  73. /* Increment the reference count for a dentry */
  74. void get_dentry (struct shim_dentry * dent)
  75. {
  76. #ifdef DEBUG_REF
  77. int count = REF_INC(dent->ref_count);
  78. debug("get dentry %p(%s/%s) (ref_count = %d)\n", dent,
  79. dent->fs ?
  80. qstrgetstr(&dent->fs->path) : "",
  81. qstrgetstr(&dent->rel_path), count);
  82. #else
  83. REF_INC(dent->ref_count);
  84. #endif
  85. }
  86. static void free_dentry (struct shim_dentry *dent) {
  87. free_mem_obj_to_mgr(dentry_mgr, dent);
  88. }
  89. /* Decrement the reference count on dent.
  90. *
  91. * For now, we don't have an eviction policy, so just
  92. * keep everything.
  93. *
  94. * If a dentry is on the children list of a parent, it has
  95. * a refcount of at least 1.
  96. *
  97. * If the ref count ever hits zero, we free the dentry.
  98. *
  99. */
  100. void put_dentry (struct shim_dentry * dent) {
  101. int count = REF_DEC(dent->ref_count);
  102. assert (count >= 0);
  103. // We don't expect this to commonly free a dentry, and may represent a
  104. // reference counting bug.
  105. if (count == 0) {
  106. debug("XXX Churn Warning: Freeing dentry %p; may not be expected\n", dent);
  107. // Add some assertions that the dentry is properly cleaned up, like it
  108. // isn't on a parent's children list
  109. assert(list_empty(dent, siblings));
  110. free_dentry(dent);
  111. }
  112. return;
  113. }
  114. /* Allocate and initialize a new dentry for path name, under
  115. * parent. Return the dentry.
  116. *
  117. * mount is the mountpoint the dentry is under; this is typically
  118. * the parent->fs, but is passed explicitly for initializing
  119. * the dentry of a mountpoint.
  120. *
  121. * If hashptr is passed (as an optimization), this is a hash
  122. * of the name.
  123. *
  124. * If parent is non-null, the ref count is 1; else it is zero.
  125. *
  126. * This function also sets up both a name and a relative path
  127. */
  128. struct shim_dentry * get_new_dentry (struct shim_mount *mount,
  129. struct shim_dentry * parent,
  130. const char * name, int namelen,
  131. HASHTYPE * hashptr)
  132. {
  133. struct shim_dentry * dent = alloc_dentry();
  134. HASHTYPE hash;
  135. if (!dent)
  136. return NULL;
  137. if (hashptr) {
  138. #ifdef DEBUG
  139. // For debug builds, assert that the hash passed in is correct.
  140. assert(*hashptr == hash_dentry(parent, name, namelen));
  141. #endif
  142. hash = *hashptr;
  143. } else
  144. hash = hash_dentry(parent, name, namelen);
  145. qstrsetstr(&dent->name, name, namelen);
  146. dent->rel_path.hash = hash;
  147. /* DEP 6/16/17: Not sure this flag is strictly necessary.
  148. * But keeping it for now.
  149. */
  150. dent->state |= DENTRY_HASHED;
  151. if (mount) {
  152. get_mount(mount);
  153. dent->fs = mount;
  154. }
  155. if (parent) {
  156. // Increment both dentries' ref counts once they are linked
  157. get_dentry(parent);
  158. get_dentry(dent);
  159. listp_add_tail(dent, &parent->children, siblings);
  160. dent->parent = parent;
  161. parent->nchildren++;
  162. if (!qstrempty(&parent->rel_path)) {
  163. const char * strs[] = { qstrgetstr(&parent->rel_path), "/", name };
  164. size_t lens[] = { parent->rel_path.len, 1, namelen };
  165. qstrsetstrs(&dent->rel_path, 3, strs, lens);
  166. } else
  167. qstrsetstr(&dent->rel_path, name, namelen);
  168. } else {
  169. qstrsetstr(&dent->rel_path, name, namelen);
  170. }
  171. return dent;
  172. }
  173. /* This function searches for name/namelen (as the relative path) under
  174. * the parent directory (start).
  175. *
  176. * path/pathlen is the fully-qualified path, and an optional (unused)
  177. * argument. XXX: I am not sure what the case is where the full path
  178. * would resolve but the relative path would not; consider dropping
  179. * this argument, or documenting the reason (after testing).
  180. *
  181. * If requested, the expected hash of the dentry is returned in hashptr,
  182. * primarily so that the hashing can be reused to add the dentry later.
  183. *
  184. * The reference count on the found dentry is incremented by one.
  185. *
  186. * Used only by shim_namei.c
  187. */
  188. struct shim_dentry *
  189. __lookup_dcache (struct shim_dentry * start, const char * name, int namelen,
  190. const char * path, int pathlen, HASHTYPE * hashptr) {
  191. /* In this implementation, we just look at the children
  192. * under the parent and see if there are matches. It so,
  193. * return it; if not, don't.
  194. *
  195. * To minimize disruption (and possibly for future optimization)
  196. * we are keeping hashes, so let's start with that for a marginally
  197. * faster comparison
  198. */
  199. HASHTYPE hash = hash_dentry(start, name, namelen);
  200. struct shim_dentry *dent, *found = NULL;
  201. /* If start is NULL, there will be no hit in the cache.
  202. * This mainly happens when boostrapping; in general, we assume the
  203. * caller will use the current root or cwd.
  204. */
  205. if (!start) return NULL;
  206. /* If we are looking up an empty string, return start */
  207. if (namelen == 0) {
  208. found = start;
  209. goto out;
  210. }
  211. listp_for_each_entry(dent, &start->children, siblings) {
  212. /* DEP 6/20/XX: The old code skipped mountpoints; I don't see any good
  213. * reason for mount point lookup to fail, at least in this code.
  214. * Keeping a note just in case. That is why you always leave a note.
  215. */
  216. //if (dent->state & DENTRY_MOUNTPOINT)
  217. //continue;
  218. // Check for memory corruption
  219. assert(0 == (dent->state & DENTRY_INVALID_FLAGS));
  220. /* Compare the hash first */
  221. if (dent->rel_path.hash != hash)
  222. continue;
  223. /* I think comparing the relative path is adequate; with a global
  224. * hash table, a full path comparison may be needed, but I think
  225. * we can assume a parent has children with unique names */
  226. const char * filename = get_file_name(name, namelen);
  227. const char * dname = dentry_get_name(dent);
  228. int dname_len = strlen(dname);
  229. int fname_len = name + namelen - filename;
  230. if (dname_len != fname_len || memcmp(dname, filename, fname_len))
  231. continue;
  232. /* If we get this far, we have a match */
  233. get_dentry(dent);
  234. found = dent;
  235. break;
  236. }
  237. out:
  238. if (hashptr)
  239. *hashptr = hash;
  240. return found;
  241. }
  242. /* This function recursively removes children and drops the reference count
  243. * under root (but not the root itself).
  244. *
  245. * For memory-constrained systems (arguably SGX enclaves), there is a
  246. * legitimate concern that this could overflow the stack, as a path can have
  247. * as many as 4096 characters, leading to as many as 2048 stack frames. It
  248. * may be preferable to rewrite this using tail recursion or allocating a
  249. * structure on the heap to track progress.
  250. */
  251. int __del_dentry_tree(struct shim_dentry * root) {
  252. struct shim_dentry *cursor, *n;
  253. listp_for_each_entry_safe(cursor, n, &root->children, siblings) {
  254. // Recur if this is a non-empty directory
  255. if (!listp_empty(&cursor->children))
  256. __del_dentry_tree(cursor);
  257. listp_del_init(cursor, &root->children, siblings);
  258. cursor->parent = NULL;
  259. root->nchildren--;
  260. // Clear the hashed flag, in case there is any vestigial code based
  261. // on this state machine (where hased == valid).
  262. cursor->state &= ~DENTRY_HASHED;
  263. put_dentry(cursor);
  264. }
  265. return 0;
  266. }
  267. BEGIN_CP_FUNC(dentry)
  268. {
  269. assert(size == sizeof(struct shim_dentry));
  270. struct shim_dentry * dent = (struct shim_dentry *) obj;
  271. struct shim_dentry * new_dent = NULL;
  272. ptr_t off = GET_FROM_CP_MAP(obj);
  273. if (!off) {
  274. off = ADD_CP_OFFSET(sizeof(struct shim_dentry));
  275. ADD_TO_CP_MAP(obj, off);
  276. new_dent = (struct shim_dentry *) (base + off);
  277. lock(dent->lock);
  278. *new_dent = *dent;
  279. INIT_LIST_HEAD(new_dent, hlist);
  280. INIT_LIST_HEAD(new_dent, list);
  281. INIT_LISTP(&new_dent->children);
  282. INIT_LIST_HEAD(new_dent, siblings);
  283. new_dent->data = NULL;
  284. clear_lock(new_dent->lock);
  285. REF_SET(new_dent->ref_count, 0);
  286. DO_CP_IN_MEMBER(qstr, new_dent, rel_path);
  287. DO_CP_IN_MEMBER(qstr, new_dent, name);
  288. if (dent->fs)
  289. DO_CP_MEMBER(mount, dent, new_dent, fs);
  290. if (dent->parent)
  291. DO_CP_MEMBER(dentry, dent, new_dent, parent);
  292. if (dent->mounted)
  293. DO_CP_MEMBER(mount, dent, new_dent, mounted);
  294. unlock(dent->lock);
  295. ADD_CP_FUNC_ENTRY(off);
  296. } else {
  297. new_dent = (struct shim_dentry *) (base + off);
  298. }
  299. if (objp)
  300. *objp = (void *) new_dent;
  301. }
  302. END_CP_FUNC(dentry)
  303. BEGIN_RS_FUNC(dentry)
  304. {
  305. struct shim_dentry * dent = (void *) (base + GET_CP_FUNC_ENTRY());
  306. CP_REBASE(dent->hlist);
  307. CP_REBASE(dent->list);
  308. CP_REBASE(dent->children);
  309. CP_REBASE(dent->siblings);
  310. CP_REBASE(dent->fs);
  311. CP_REBASE(dent->parent);
  312. CP_REBASE(dent->mounted);
  313. create_lock(dent->lock);
  314. /* DEP 6/16/17: I believe the point of this line is to
  315. * fix up the children linked list. Presumably the ref count and
  316. * child count is already correct in the checkpoint. */
  317. if (dent->parent) {
  318. get_dentry(dent->parent);
  319. get_dentry(dent);
  320. listp_add_tail(dent, &dent->parent->children, siblings);
  321. }
  322. DEBUG_RS("hash=%08x,path=%s,fs=%s", dent->rel_path.hash,
  323. dentry_get_path(dent, true, NULL),
  324. dent->fs ? qstrgetstr(&dent->fs->path) : NULL);
  325. }
  326. END_RS_FUNC(dentry)