/*
- Copyright 2015 IoT.bzh
+ Copyright (C) 2015-2019 "IoT.bzh"
author: José Bollo <jose.bollo@iot.bzh>
#include <dirent.h>
#include "locale-root.h"
+#include "subpath.h"
/*
* Implementation of folder based localisation as described here:
struct locale_root {
int refcount;
- int refcount2;
+ int intcount;
int rootfd;
struct locale_container container;
struct locale_search *lru[LRU_COUNT];
+ struct locale_search *default_search;
};
-/* a valid subpath is a relative path not looking deeper than root using .. */
-static int validsubpath(const char *subpath)
-{
- int l = 0, i = 0;
-
- /* absolute path is not valid */
- if (subpath[i] == '/')
- return 0;
-
- /* inspect the path */
- while(subpath[i]) {
- switch(subpath[i++]) {
- case '.':
- if (!subpath[i])
- break;
- if (subpath[i] == '/') {
- i++;
- break;
- }
- if (subpath[i++] == '.') {
- if (!subpath[i]) {
- l--;
- break;
- }
- if (subpath[i++] == '/') {
- l--;
- break;
- }
- }
- default:
- while(subpath[i] && subpath[i] != '/')
- i++;
- if (l >= 0)
- l++;
- case '/':
- break;
- }
- }
- return l >= 0;
-}
-
-/*
- * Normalizes and checks the 'subpath'.
- * Removes any starting '/' and checks that 'subpath'
- * does not contains sequence of '..' going deeper than
- * root.
- * Returns the normalized subpath or NULL in case of
- * invalid subpath.
- */
-static const char *normalsubpath(const char *subpath)
-{
- while(*subpath == '/')
- subpath++;
- return validsubpath(subpath) ? subpath : NULL;
-}
-
/*
* Clear a container content
*/
return f;
if (c >= 0)
high = mid;
- else
+ else
low = mid + 1;
}
return NULL;
*/
static int init_container(struct locale_container *container, int dirfd)
{
- int rc, sfd;
+ int rc = 0, sfd;
DIR *dir;
- struct dirent dent, *e;
+ struct dirent *dent;
struct stat st;
size_t i, j;
struct locale_folder *f;
/* enumerate the entries */
for(;;) {
/* next entry */
- rc = readdir_r(dir, &dent, &e);
- if (rc < 0) {
- /* error */
- closedir(dir);
- return rc;
- }
- if (e == NULL) {
+ errno = 0;
+ dent = readdir(dir);
+ if (dent == NULL) {
/* end of entries */
closedir(dir);
+ if (errno != 0)
+ return -1;
break;
}
- if (dent.d_type == DT_DIR || (dent.d_type == DT_UNKNOWN && fstatat(sfd, dent.d_name, &st, 0) == 0 && S_ISDIR(st.st_mode))) {
+ rc = fstatat(sfd, dent->d_name, &st, 0);
+ if (rc == 0 && S_ISDIR(st.st_mode)) {
/* directory aka folder */
- if (dent.d_name[0] == '.' && (dent.d_name[1] == 0 || (dent.d_name[1] == '.' && dent.d_name[2] == 0))) {
+ if (dent->d_name[0] == '.' && (dent->d_name[1] == 0 || (dent->d_name[1] == '.' && dent->d_name[2] == 0))) {
/* nothing to do for special directories, basic detection, improves if needed */
} else {
- rc = add_folder(container, dent.d_name);
+ rc = add_folder(container, dent->d_name);
if (rc < 0) {
closedir(dir);
return rc;
}
/* sort the folders */
- qsort(container->folders, container->count, sizeof *container->folders, compare_folders_for_qsort);
+ if (container->count)
+ qsort(container->folders, container->count, sizeof *container->folders, compare_folders_for_qsort);
/* build the parents links */
i = container->count;
* Creates a locale root handler and returns it or return NULL
* in case of memory depletion.
*/
-struct locale_root *locale_root_create(int dirfd, const char *pathname)
+struct locale_root *locale_root_create(int dirfd)
{
- int rfd;
struct locale_root *root;
size_t i;
- rfd = (pathname && *pathname) ? openat(dirfd, pathname, O_PATH|O_DIRECTORY) : dirfd >= 0 ? dup(dirfd) : dirfd;
- if (rfd >= 0 || (!(pathname && *pathname) && dirfd < 0)) {
- root = calloc(1, sizeof * root);
- if (root == NULL)
- errno = ENOMEM;
- else {
- if (init_container(&root->container, rfd) == 0) {
- root->rootfd = rfd;
- root->refcount = 1;
- root->refcount2 = 1;
- for(i = 0 ; i < LRU_COUNT ; i++)
- root->lru[i] = NULL;
- return root;
- }
- free(root);
+ root = calloc(1, sizeof * root);
+ if (root == NULL)
+ errno = ENOMEM;
+ else {
+ if (init_container(&root->container, dirfd) == 0) {
+ root->rootfd = dirfd;
+ root->refcount = 1;
+ root->intcount = 1;
+ for(i = 0 ; i < LRU_COUNT ; i++)
+ root->lru[i] = NULL;
+ root->default_search = NULL;
+ return root;
}
- close(rfd);
+ free(root);
}
return NULL;
}
+/*
+ * Creates a locale root handler and returns it or return NULL
+ * in case of memory depletion.
+ */
+struct locale_root *locale_root_create_at(int dirfd, const char *path)
+{
+ int fd;
+ struct locale_root *root;
+
+ fd = openat(dirfd, path, O_PATH|O_DIRECTORY);
+ if (fd < 0)
+ root = NULL;
+ else {
+ root = locale_root_create(fd);
+ if (root == NULL)
+ close(fd);
+ }
+ return root;
+}
+
/*
* Adds a reference to 'root'
*/
struct locale_root *locale_root_addref(struct locale_root *root)
{
- root->refcount++;
+ __atomic_add_fetch(&root->refcount, 1, __ATOMIC_RELAXED);
return root;
}
/*
- * Drops a reference to 'root' and destroys it
+ * Drops an internal reference to 'root' and destroys it
* if not more referenced
*/
-static void locale_root_unref2(struct locale_root *root)
+static void internal_unref(struct locale_root *root)
{
- if (!--root->refcount2) {
+ if (!__atomic_sub_fetch(&root->intcount, 1, __ATOMIC_RELAXED)) {
clear_container(&root->container);
close(root->rootfd);
free(root);
{
size_t i;
- if (root != NULL && !--root->refcount) {
+ if (root && !__atomic_sub_fetch(&root->refcount, 1, __ATOMIC_RELAXED)) {
/* clear circular references through searchs */
for (i = 0 ; i < LRU_COUNT ; i++)
locale_search_unref(root->lru[i]);
/* finalize if needed */
- locale_root_unref2(root);
+ internal_unref(root);
}
}
+/*
+ * Get the filedescriptor for the 'root' directory
+ */
+int locale_root_get_dirfd(struct locale_root *root)
+{
+ return root->rootfd;
+}
+
/*
* append, if needed, a folder to the search
*/
/* allocate the structure */
search = malloc(sizeof *search + length);
- if (search != NULL) {
+ if (search == NULL) {
+ errno = ENOMEM;
+ } else {
/* init */
- root->refcount2++;
+ __atomic_add_fetch(&root->intcount, 1, __ATOMIC_RELAXED);
search->root = root;
search->head = NULL;
search->refcount = 1;
*/
struct locale_search *locale_search_addref(struct locale_search *search)
{
- search->refcount++;
+ __atomic_add_fetch(&search->refcount, 1, __ATOMIC_RELAXED);
return search;
}
{
struct locale_search_node *it, *nx;
- if (search && !--search->refcount) {
+ if (search && !__atomic_sub_fetch(&search->refcount, 1, __ATOMIC_RELAXED)) {
it = search->head;
while(it != NULL) {
nx = it->next;
free(it);
it = nx;
}
- locale_root_unref2(search->root);
+ internal_unref(search->root);
free(search);
}
}
/*
- * Opens 'filename' after search.
+ * Set the default search of 'root' to 'search'.
+ * This search is used as fallback when other search are failing.
+ */
+void locale_root_set_default_search(struct locale_root *root, struct locale_search *search)
+{
+ struct locale_search *older;
+
+ assert(search == NULL || search->root == root);
+
+ older = root->default_search;
+ root->default_search = search ? locale_search_addref(search) : NULL;
+ locale_search_unref(older);
+}
+
+/*
+ * Opens 'filename' for 'search' and 'root'.
*
* Returns the file descriptor as returned by openat
* system call or -1 in case of error.
*/
-int locale_search_open(struct locale_search *search, const char *filename, int mode)
+static int do_open(struct locale_search *search, const char *filename, int flags, struct locale_root *root)
{
size_t maxlength, length;
char *buffer, *p;
struct locale_folder *folder;
int rootfd, fd;
- /* no creation mode accepted */
- if ((mode & O_CREAT) != 0)
- goto inval;
-
/* check the path and normalize it */
- filename = normalsubpath(filename);
+ filename = subpath_force(filename);
if (filename == NULL)
goto inval;
+ /* no creation flags accepted */
+ if ((flags & O_CREAT) != 0)
+ goto inval;
+
/* search for folders */
- rootfd = search->root->rootfd;
- node = search->head;
+ rootfd = root->rootfd;
+ node = search ? search->head : NULL;
if (node != NULL) {
/* allocates a buffer big enough */
- maxlength = search->root->container.maxlength;
+ maxlength = root->container.maxlength;
length = strlen(filename);
if (length > PATH_MAX)
goto inval;
p = buffer + maxlength - folder->length;
memcpy(p, locales, sizeof locales - 1);
memcpy(p + sizeof locales - 1, folder->name, folder->length);
- fd = openat(rootfd, p, mode);
+ fd = openat(rootfd, p, flags);
if (fd >= 0)
return fd;
node = node->next;
+ if (node == NULL && search != root->default_search) {
+ search = root->default_search;
+ node = search ? search->head : NULL;
+ }
}
}
- /* default search */
- return openat(rootfd, filename, mode);
+ /* root search */
+ return openat(rootfd, filename, flags);
inval:
errno = EINVAL;
}
/*
- * Resolves 'filename' after search.
+ * Opens 'filename' after search.
+ *
+ * Returns the file descriptor as returned by openat
+ * system call or -1 in case of error.
+ */
+int locale_search_open(struct locale_search *search, const char *filename, int flags)
+{
+ return do_open(search, filename, flags, search->root);
+}
+
+/*
+ * Opens 'filename' for root with default search.
+ *
+ * Returns the file descriptor as returned by openat
+ * system call or -1 in case of error.
+ */
+int locale_root_open(struct locale_root *root, const char *filename, int flags, const char *locale)
+{
+ int result;
+ struct locale_search *search;
+
+ search = locale != NULL ? locale_root_search(root, locale, 0) : NULL;
+ result = do_open(search ? : root->default_search, filename, flags, root);
+ locale_search_unref(search);
+ return result;
+}
+
+/*
+ * Resolves 'filename' for 'root' and 'search'.
*
* returns a copy of the filename after search or NULL if not found.
* the returned string MUST be freed by the caller (using free).
*/
-char *locale_search_resolve(struct locale_search *search, const char *filename)
+static char *do_resolve(struct locale_search *search, const char *filename, struct locale_root *root)
{
size_t maxlength, length;
char *buffer, *p;
int rootfd;
/* check the path and normalize it */
- filename = normalsubpath(filename);
+ filename = subpath_force(filename);
if (filename == NULL)
goto inval;
/* search for folders */
- rootfd = search->root->rootfd;
- node = search->head;
+ rootfd = root->rootfd;
+ node = search ? search->head : NULL;
if (node != NULL) {
/* allocates a buffer big enough */
- maxlength = search->root->container.maxlength;
+ maxlength = root->container.maxlength;
length = strlen(filename);
if (length > PATH_MAX)
goto inval;
goto found;
}
node = node->next;
+ if (node == NULL && search != root->default_search) {
+ search = root->default_search;
+ node = search ? search->head : NULL;
+ }
}
}
- /* default search */
+ /* root search */
if (0 != faccessat(rootfd, filename, F_OK, 0)) {
errno = ENOENT;
return NULL;
return NULL;
}
-#if defined(TEST_locale_root_validsubpath)
-#include <stdio.h>
-void t(const char *subpath, int validity) {
- printf("%s -> %d = %d, %s\n", subpath, validity, validsubpath(subpath), validsubpath(subpath)==validity ? "ok" : "NOT OK");
+/*
+ * Resolves 'filename' at 'root' after default search.
+ *
+ * returns a copy of the filename after search or NULL if not found.
+ * the returned string MUST be freed by the caller (using free).
+ */
+char *locale_root_resolve(struct locale_root *root, const char *filename, const char *locale)
+{
+ char *result;
+ struct locale_search *search;
+
+ search = locale != NULL ? locale_root_search(root, locale, 0) : NULL;
+ result = do_resolve(search ? : root->default_search, filename, root);
+ locale_search_unref(search);
+ return result;
}
-int main() {
- t("/",0);
- t("..",0);
- t(".",1);
- t("../a",0);
- t("a/..",1);
- t("a/../////..",0);
- t("a/../b/..",1);
- t("a/b/c/..",1);
- t("a/b/c/../..",1);
- t("a/b/c/../../..",1);
- t("a/b/c/../../../.",1);
- t("./..a/././..b/..c/./.././.././../.",1);
- t("./..a/././..b/..c/./.././.././.././..",0);
- t("./..a//.//./..b/..c/./.././/./././///.././.././a/a/a/a/a",1);
- return 0;
+
+/*
+ * Resolves 'filename' after 'search'.
+ *
+ * returns a copy of the filename after search or NULL if not found.
+ * the returned string MUST be freed by the caller (using free).
+ */
+char *locale_search_resolve(struct locale_search *search, const char *filename)
+{
+ return do_resolve(search, filename, search->root);
}
-#endif
#if defined(TEST_locale_root)
int main(int ac,char**av)
{
- struct locale_root *root = locale_root_create(AT_FDCWD, NULL);
+ struct locale_root *root = locale_root_create(AT_FDCWD);
struct locale_search *search = NULL;
int fd, rc, i;
char buffer[256];