summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruen@gnu.org>2015-03-01 19:08:43 +0100
committerAndreas Gruenbacher <agruen@gnu.org>2015-03-05 22:57:44 +0100
commita6615bcb83b673082025db15ec323707f8305fa3 (patch)
tree76fb67d859d82b148ea6d46b2c6fc17224b45a81
parent914d06b7c3cc613df7617fae5903dbcaee601d5d (diff)
downloadpatch-a6615bcb83b673082025db15ec323707f8305fa3.tar.gz
Also cache resolved symlinks
When resolving a symlink in a pathname, we traverse each path component in the symlink and cache all of them. At the end, add an additional cache entry for the symlink itself so that we don't have to resolve the symlink again (even though this will usually be cached). Skip that if the symlink's parent isn't in the cache anymore, though. * src/safe.c (free_cached_dirfd): Remove from parent here instead of in callers. Move close() to remove_cached_dirfd() instead. (insert_cached_dirfd): Only insert if the entry's parent still exists; entries without parent are invalid (see compare_cached_dirfds()); "top-level" entries have cwd as their parent. (new_cached_dirfd): New function split off from openat_cached(). (openat_cached): Use new_cached_dirfd() here. (traverse_another_path): When starting to resolve a symlink, create an unhashed dirfd cache entry for the symlink lookup result. When the symlink is completely resolved, add that entry to the cache.
-rw-r--r--src/safe.c66
1 files changed, 53 insertions, 13 deletions
diff --git a/src/safe.c b/src/safe.c
index 488afbf..de7f306 100644
--- a/src/safe.c
+++ b/src/safe.c
@@ -83,11 +83,11 @@ static bool compare_cached_dirfds (const void *_a,
!strcmp (a->name, b->name));
}
-static void free_cached_dirfd (struct cached_dirfd *d)
+static void free_cached_dirfd (struct cached_dirfd *entry)
{
- close (d->fd);
- free (d->name);
- free (d);
+ list_del (&entry->children_link);
+ free (entry->name);
+ free (entry);
}
static void init_dirfd_cache (void)
@@ -132,9 +132,9 @@ static void remove_cached_dirfd (struct cached_dirfd *entry)
list_del_init (&child->children_link);
hash_delete (cached_dirfds, child);
}
- list_del (&entry->children_link);
list_del (&entry->lru_link);
hash_delete (cached_dirfds, entry);
+ close (entry->fd);
free_cached_dirfd (entry);
}
@@ -159,7 +159,9 @@ static void insert_cached_dirfd (struct cached_dirfd *entry, int keepfd)
remove_cached_dirfd (last);
}
- assert (hash_insert (cached_dirfds, entry) == entry);
+ /* Only insert if the parent still exists. */
+ if (! list_empty (&entry->children_link))
+ assert (hash_insert (cached_dirfds, entry) == entry);
}
static void invalidate_cached_dirfd (int dirfd, const char *name)
@@ -194,6 +196,19 @@ static int put_path (struct cached_dirfd *entry)
return fd;
}
+static struct cached_dirfd *new_cached_dirfd (struct cached_dirfd *dir, const char *name, int fd)
+{
+ struct cached_dirfd *entry = xmalloc (sizeof (struct cached_dirfd));
+
+ INIT_LIST_HEAD (&entry->lru_link);
+ list_add (&entry->children_link, &dir->children);
+ INIT_LIST_HEAD (&entry->children);
+ entry->parent = dir;
+ entry->name = xstrdup (name);
+ entry->fd = fd;
+ return entry;
+}
+
static struct cached_dirfd *openat_cached (struct cached_dirfd *dir, const char *name, int keepfd)
{
int fd;
@@ -215,13 +230,7 @@ static struct cached_dirfd *openat_cached (struct cached_dirfd *dir, const char
return NULL;
/* Store new cache entry */
- entry = xmalloc (sizeof (struct cached_dirfd));
- INIT_LIST_HEAD (&entry->lru_link);
- list_add (&entry->children_link, &dir->children);
- INIT_LIST_HEAD (&entry->children);
- entry->parent = dir;
- entry->name = xstrdup (name);
- entry->fd = fd;
+ entry = new_cached_dirfd (dir, name, fd);
insert_cached_dirfd (entry, keepfd);
out:
@@ -368,6 +377,7 @@ static int traverse_another_path (const char **pathname, int keepfd)
struct cached_dirfd *dir = &cwd;
struct symlink *stack = NULL;
unsigned int steps = count_path_components (path);
+ struct cached_dirfd *traversed_symlink = NULL;
INIT_LIST_HEAD (&cwd.children);
@@ -400,6 +410,8 @@ static int traverse_another_path (const char **pathname, int keepfd)
{
struct cached_dirfd *entry;
struct symlink *symlink = NULL;
+ const char *prev = path;
+
entry = traverse_next (dir, stack ? &stack->path : &path, keepfd, &symlink);
if (! entry)
{
@@ -411,6 +423,19 @@ static int traverse_another_path (const char **pathname, int keepfd)
goto fail;
}
dir = entry;
+ if (! stack && symlink)
+ {
+ const char *p = prev;
+ char *name;
+
+ while (*p && ! ISSLASH (*p))
+ p++;
+ name = alloca (p - prev + 1);
+ memcpy (name, prev, p - prev);
+ name[p - prev] = 0;
+
+ traversed_symlink = new_cached_dirfd (dir, name, -1);
+ }
if (stack && ! *stack->path)
pop_symlink (&stack);
if (symlink)
@@ -423,6 +448,19 @@ static int traverse_another_path (const char **pathname, int keepfd)
goto fail;
}
}
+ if (traversed_symlink && ! stack)
+ {
+ traversed_symlink->fd =
+ entry->fd == AT_FDCWD ? AT_FDCWD : dup (entry->fd);
+ if (traversed_symlink->fd != -1)
+ {
+ insert_cached_dirfd (traversed_symlink, keepfd);
+ list_add (&traversed_symlink->lru_link, &lru_list);
+ }
+ else
+ free_cached_dirfd (traversed_symlink);
+ traversed_symlink = NULL;
+ }
}
*pathname = last;
if (debug & 32)
@@ -437,6 +475,8 @@ static int traverse_another_path (const char **pathname, int keepfd)
return put_path (dir);
fail:
+ if (traversed_symlink)
+ free_cached_dirfd (traversed_symlink);
put_path (dir);
while (stack)
pop_symlink (&stack);