shim_dcache.c 12 KB

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