shim_dcache.c 12 KB

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