diff --git a/Documentation/applications/nsh/commands.rst b/Documentation/applications/nsh/commands.rst index d1477e958df5b..c1e79508a8926 100644 --- a/Documentation/applications/nsh/commands.rst +++ b/Documentation/applications/nsh/commands.rst @@ -215,6 +215,52 @@ Also sets the previous working directory environment variable ``cd ..`` sets the current working directory to the parent directory. ================== ===================================== +.. _cmdchroot: + +``chroot`` Change Root Directory +================================ + +**Command Syntax**:: + + chroot [ [args...]] + +**Synopsis**. Change the filesystem root of the current task group so +absolute path lookups start at ````. Requires +``CONFIG_FS_CHROOT``. This is a filesystem jail, not a container. + +The command performs ``chdir(newroot)``, ``chroot(".")``, then +``chdir("/")``. With no extra arguments the current NSH session stays +jailed (``pwd`` shows ``/``). An optional command is executed with +``execvp()`` after the jail is in place. + +When ``CONFIG_SCHED_USER_IDENTITY`` is enabled, ``chroot()`` requires +effective UID 0. Drop extra privilege after jailing so a later +``chroot()`` cannot be used to escape. + +File descriptors opened before ``chroot()`` are not retroactively +contained. The ``chroot `` form closes non-stdio +descriptors that are not already ``O_CLOEXEC`` before ``execvp()``. +The no-command form leaves the current session's existing descriptors +usable, including any that point outside the jail. + +**Example**:: + + nsh> mkdir /tmp/jail + nsh> echo hello > /tmp/jail/marker + nsh> chroot /tmp/jail + nsh> pwd + / + nsh> ls / + /: + marker + nsh> cat /marker + hello + +Note that ``ls /`` only lists ``marker``: ``/dev`` and ``/proc`` are not +visible inside the jail because they were never created under +``/tmp/jail``. ``chroot()`` does not bind-mount or otherwise populate +these pseudo-filesystems into the new root; see :ref:`chroot`. + .. _cmdchmod: ``chmod`` Change File Permissions diff --git a/Documentation/applications/nsh/config.rst b/Documentation/applications/nsh/config.rst index 19b92ebfe033b..31816b9798120 100644 --- a/Documentation/applications/nsh/config.rst +++ b/Documentation/applications/nsh/config.rst @@ -40,6 +40,7 @@ Command Depends on Configuration Can Be Disabl ! ``CONFIG_NSH_DISABLE_LOOPS``   :ref:`cmdcat` ``CONFIG_NSH_DISABLE_CAT`` . :ref:`cmdcd` ! ``CONFIG_DISABLE_ENVIRON`` ``CONFIG_NSH_DISABLE_CD`` +:ref:`cmdchroot` ``CONFIG_FS_CHROOT`` ``CONFIG_NSH_DISABLE_CHROOT`` :ref:`cmdcmp` ``CONFIG_NSH_DISABLE_CMP`` . :ref:`cmdcp` ``CONFIG_NSH_DISABLE_CP`` . :ref:`cmddate` ``CONFIG_NSH_DISABLE_DATE`` . diff --git a/Documentation/implementation/chroot.rst b/Documentation/implementation/chroot.rst new file mode 100644 index 0000000000000..8a06eeb101389 --- /dev/null +++ b/Documentation/implementation/chroot.rst @@ -0,0 +1,252 @@ +.. _chroot: + +====== +chroot +====== + +``chroot()`` is a kernel-enforced filesystem jail. When +``CONFIG_FS_CHROOT`` is enabled, each task group may pin a directory as +its root. Absolute path lookup starts there, so the group cannot see +files outside that tree. + +Limitations: this is **not** a container. The current implementation +only changes where pathname lookup begins; it does not provide PID, +mount, or network namespaces, and it does not populate the new root +with ``/dev`` or ``/proc``. See `TODO`_ for what each of these would +require. + +Design and implementation +========================== + +The Kconfig option and the syscall are the easy part; ``chroot()`` on +NuttX has no MMU-backed process isolation to lean on, so the whole +feature has to be built on top of the single, global pseudo-filesystem +inode tree that every task already shares. This section walks through +why that made the implementation harder than it looks, in the order +the pieces had to be worked out. + +Where does the jail live? +-------------------------- + +The first question is what a "jail" even is in a system with one +shared filesystem tree: it cannot be a separate tree, so it has to be +a *starting point* that path lookups are not allowed to walk above. +That starting point needs to be remembered somewhere per-caller, and +it needs to survive ``fork()``-style child creation the same way an +open file table or a working directory does. + +NuttX already keeps exactly that kind of shared, inheritable state on +the task group (``struct task_group_s``), not on the individual task, +because every thread in a task group is supposed to see the same +filesystem view. The jail is stored as a single absolute path:: + + struct task_group_s + { + ... + #ifdef CONFIG_FS_CHROOT + FAR char *tg_root; /* Absolute jail path, or NULL */ + #endif + }; + +A path is used instead of a cached inode so a later unmount/remount +at that location is picked up on the next lookup. ``tg_root`` is +``NULL`` when the group has not called ``chroot()``. + +``group_inherit_chroot()`` (``sched/group/group_create.c``) copies the +string to child task groups (kernel threads are skipped): + +.. code-block:: c + + if (rgroup->tg_root == NULL) + { + return OK; + } + + group->tg_root = strdup(rgroup->tg_root); + if (group->tg_root == NULL) + { + return -ENOMEM; + } + +That is what makes a jail apply to a whole subtree of children, not +just the one task that called ``chroot()``. + +How lookups stay inside the jail +--------------------------------- + +Every absolute path goes through ``inode_search_setup()`` and then +the original walk from ``g_root_inode``: + +1. Prepend ``tg_root`` to the incoming path (``/tmp/jail`` + ``/foo`` + becomes ``/tmp/jail/foo``). +2. Canonicalize the combined string with ``_inode_canonicalize()``: + drop empty and ``.`` segments and collapse ``..``. The jail prefix + is the floor for that walk, so ``..`` cannot pop above ``tg_root``. +3. ``/../etc`` inside the jail therefore becomes ``/tmp/jail/etc``, + not host ``/etc``. +4. Continue the original inode-tree walk on that host path. + +Without a jail the same canonicalize step still runs, so +``chroot(".")`` under a mount (``$PWD/.``) does not pass a leftover +``.`` to the filesystem as ``relpath``. + +There is no separate jailed walk, and no extra ``..`` handling inside +the tree traversal. + +Why ``chroot()`` does not touch ``PWD`` +----------------------------------------- + +Relative lookups go through ``inode_search()``, which prepends +``$PWD`` to the path and then calls the exact same absolute-path +logic described above. That raises an obvious question: what happens +to a task's current directory when its whole notion of "root" just +moved? + +An earlier version of this change rewrote ``PWD`` inside ``chroot()`` +itself to keep it consistent with the new jail. That turned out to be +both the wrong layer and unnecessary: + +* ``chroot()`` is a filesystem primitive; ``PWD`` is environ state. + POSIX ``chroot()`` does not touch the current directory either -- + the well-known Unix idiom is that the *caller* must ``chdir()`` + immediately after ``chroot()``, precisely so that no stale + reference to the old tree is left lying around. +* It is not needed for containment. A stale ``PWD`` used in a + relative lookup after ``chroot()`` is still rewritten by + ``inode_search_setup()`` (prepend the jail path, canonicalize, clamp + ``..``) before the tree walk. The lookup can fail or land on a + path inside the jail that was not intended, but it cannot resolve + to a node outside the jail. + +So ``chroot()`` leaves ``PWD`` alone, and the caller is responsible +for calling ``chdir()`` afterward if a sane current directory inside +the jail is needed -- exactly as the NSH ``chroot`` command already +does with its trailing ``chdir("/")`` (see `NSH`_ below), which sets +``PWD`` correctly via the ordinary ``chdir()`` path, with no special +jail-aware logic required. + +Why the privilege gate lives in ``chroot()`` itself +----------------------------------------------------- + +The last design question was who is allowed to call ``chroot()`` at +all. When ``CONFIG_SCHED_USER_IDENTITY`` is enabled, the syscall +checks ``tg_euid`` directly and returns ``EPERM`` for anything but +effective UID 0:: + + #ifdef CONFIG_SCHED_USER_IDENTITY + if (group->tg_euid != 0) + { + set_errno(EPERM); + return ERROR; + } + #endif + +This is intentionally the same *class* of check as the credential DAC +checks described in :ref:`user-identity`, and it inherits the same +caveat: on ``CONFIG_BUILD_FLAT``, kernel and application code share +one address space, so this is a userspace-visible gate rather than a +hardware-enforced boundary -- other code in that address space can +write ``tg_euid`` or ``tg_root`` directly. Protected and kernel builds +close that gap by enforcing the check at the syscall boundary, which +untrusted code cannot bypass. Without ``CONFIG_SCHED_USER_IDENTITY`` +at all, every task is effectively root, so ``chroot()`` stays +available to everyone and is a pure path-containment mechanism with no +privilege check gating it. + +Configuration +============= + +Enable ``CONFIG_FS_CHROOT`` in the filesystem configuration. The +syscall is then available from ``unistd.h``. + +Semantics +========= + +* ``chroot(path)`` resolves ``path`` relative to the caller's current + root (so a nested ``chroot()`` cannot walk back to the host tree). +* ``path`` must name a directory (``ENOTDIR`` otherwise). +* ``chroot("/")`` resolves to the host root and clears the jail + (``tg_root = NULL``). From inside a jail, ``/`` is the jail root, so + it cannot be used to escape. +* The jail is stored on the task group as the absolute path ``tg_root``. + Child tasks inherit it. Kernel threads do not. +* ``chroot()`` does not modify ``PWD`` or any other environ state; the + caller is responsible for calling ``chdir()`` afterward if a + specific current directory inside the jail is needed. See + `Why chroot() does not touch PWD`_. + +NSH +=== + +The NSH ``chroot`` command performs the usual Unix dance:: + + chdir(newroot); + chroot("."); + chdir("/"); + +With no extra arguments the current NSH session stays jailed (``pwd`` +shows ``/``, ``ls /`` lists the jail tree) because of the trailing +``chdir("/")`` in the sequence above, not because ``chroot()`` itself +touches ``PWD``. An optional command is executed with ``execvp()`` +after the jail is in place; NSH closes non-stdio, non-``O_CLOEXEC`` +descriptors first (see below). + +When ``CONFIG_SCHED_USER_IDENTITY`` is enabled, drop extra privilege +after the jail is in place (for example ``setuid()`` to a non-root +user) so a later ``chroot()`` cannot be used to escape. + +Open file descriptors +===================== + +File descriptors opened before ``chroot()`` are not retroactively +contained. POSIX allows this; NuttX does not close them. A jailed +task that inherits a host descriptor can read and write that file +without going through pathname lookup, so the jail does not apply. +This is the most common way ``chroot()`` is misused as a security +tool. Do not treat it as a sandbox against a process that already +holds host file descriptors. + +The NSH ``chroot `` form closes every open +descriptor above stderr that is not already marked ``O_CLOEXEC`` +before ``execvp()``. Stdio (fds 0--2) is left intact. The +no-command form leaves the current NSH session jailed with its +existing descriptors, including any that point outside the tree. + +TODO +==== + +The following are deliberately out of scope for this initial +implementation, and are listed with what each would require, since +that scoping was itself a large part of the design work: + +* **Populating ``/dev``, ``/proc``, etc. inside the jail.** Nothing + bind-mounts or otherwise recreates these pseudo-filesystems under + the new root, so a jailed task cannot open devices or read process + info unless the jail directory tree already contains them. Adding + this needs either a bind-mount primitive (mount an existing inode + subtree at a second path) or a per-jail selective mount step run at + ``chroot()`` time; neither existed in the VFS before this change, + and both are a materially larger change than pathname jailing. + This is the specific gap raised for using ``chroot()`` to sandbox + remote logins (telnet/ssh): without a minimal ``/dev``, a jailed + shell cannot even do much I/O. +* **PID namespaces.** NuttX has one flat, global task/PID table. + Isolating it per jail would mean making scheduler and IPC lookups + (``kill()``, ``/proc``-style listings, signal delivery) aware of a + namespace boundary, which touches the scheduler core, not just the + VFS. This implementation does not attempt that. +* **Mount namespaces.** The mount table (``g_root_inode`` and its + mounted filesystems) is process-global. A jailed task group can be + confined to a subtree of the existing mount table, but it cannot + have a private view where mounts made outside the jail are hidden, + or where the jailed task can mount/unmount without affecting the + rest of the system. That requires per-task-group mount tables. +* **Network namespaces.** Sockets and network interfaces are global + to the OS instance; nothing in this change touches the network + stack. +* **``pivot_root()``.** Swapping the process root while keeping the + old root reachable is not implemented; ``chroot()`` only changes + where lookups begin. + +None of these are ruled out architecturally -- they are simply not +part of this change, which is scoped to pathname-lookup containment. diff --git a/Documentation/implementation/index.rst b/Documentation/implementation/index.rst index 4d53b205c6d22..a0d71ddb15208 100644 --- a/Documentation/implementation/index.rst +++ b/Documentation/implementation/index.rst @@ -9,6 +9,7 @@ Implementation Details bottomhalf_interrupt.rst cancellation_points.rst chip_h.rst + chroot.rst context_switches.rst crc.rst critical_sections.rst diff --git a/Documentation/implementation/user_identity.rst b/Documentation/implementation/user_identity.rst index ffb08aee3b561..9f621c80e8a56 100644 --- a/Documentation/implementation/user_identity.rst +++ b/Documentation/implementation/user_identity.rst @@ -40,7 +40,10 @@ When ``CONFIG_SCHED_NGROUPS`` is greater than zero: with ``setgroups()``. * ``NGROUPS_MAX`` equals ``CONFIG_SCHED_NGROUPS``. -Filesystem DAC (``fs_checkmode()``) grants the group-class mode bits when the +Filesystem DAC (Discretionary Access Control -- ownership- and +mode-bit-based permission checks, as opposed to a mandatory policy +enforced independently of the file owner) is implemented by +``fs_checkmode()``, which grants the group-class mode bits when the file's group matches ``tg_egid`` **or** any entry in ``tg_groups``. Inheritance @@ -152,6 +155,23 @@ Configuration See :ref:`file-permission` for the VFS helpers, mount-crossing traverse rules, and testing notes. +Flat Build Trust Boundary +========================= + +This credential model is a DAC (Discretionary Access Control) layer for +cooperating tasks, not a process-isolation boundary. DAC here means +permission checks based on ownership and mode bits that the owner can +change (``chmod()``/``chown()``), rather than a mandatory policy +enforced independently of the object owner. On ``CONFIG_BUILD_FLAT``, +kernel and +application share one address space, so other code can write +``tg_euid`` / ``tg_egid`` (and other fields in ``task_group_s``) +directly and bypass the syscall checks. Protected and kernel builds +enforce the boundary via the syscall interface. + +The same caveat applies to ``chroot()``'s ``euid == 0`` gate and +``tg_root``; see :ref:`chroot`. + Pseudo-Filesystem Ownership =========================== diff --git a/Documentation/reference/user/10_filesystem.rst b/Documentation/reference/user/10_filesystem.rst index 695c1e0db3001..4e1aef5419e5f 100644 --- a/Documentation/reference/user/10_filesystem.rst +++ b/Documentation/reference/user/10_filesystem.rst @@ -219,6 +219,9 @@ UNIX Standard Operations (``unistd.h``) /* Working directory operations */ int chdir(FAR const char *path); + #ifdef CONFIG_FS_CHROOT + int chroot(FAR const char *path); + #endif FAR char *getcwd(FAR char *buf, size_t size); /* File path operations */ diff --git a/Documentation/standards/posix.rst b/Documentation/standards/posix.rst index 5333f6f11e737..e435248d746d4 100644 --- a/Documentation/standards/posix.rst +++ b/Documentation/standards/posix.rst @@ -1325,6 +1325,9 @@ POSIX_FILE_SYSTEM File System: +``chroot()`` is supported when ``CONFIG_FS_CHROOT`` is enabled. See +:ref:`chroot`. + +--------------------------------+---------+ | API | Support | +================================+=========+ @@ -1332,6 +1335,8 @@ File System: +--------------------------------+---------+ | :c:func:`chdir` | Yes | +--------------------------------+---------+ +| :c:func:`chroot` | Yes | ++--------------------------------+---------+ | :c:func:`closedir` | Yes | +--------------------------------+---------+ | :c:func:`creat` | Yes | diff --git a/fs/Kconfig b/fs/Kconfig index b2aef67bb81a0..ba862c0386fcb 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -94,6 +94,18 @@ config PSEUDOFS_ATTRIBUTES Enable support for attributes(e.g. mode, uid, gid and time) in the pseudo file system. +config FS_CHROOT + bool "chroot() filesystem jail" + default n + ---help--- + Enable POSIX chroot(). Each task group may pin a directory as its + root; absolute path lookup starts there so the group cannot see + files outside that tree. This is a filesystem jail, not a + container (open file descriptors that already point outside the + tree remain usable). Nested chroot() is relative to the current + root. When SCHED_USER_IDENTITY is enabled, chroot() requires + effective UID 0. + config FS_PERMISSION bool "Enable UNIX Filesystem Permission Support" default n diff --git a/fs/driver/fs_findblockdriver.c b/fs/driver/fs_findblockdriver.c index 656bdc7a866ed..abc6ac54401d6 100644 --- a/fs/driver/fs_findblockdriver.c +++ b/fs/driver/fs_findblockdriver.c @@ -77,20 +77,19 @@ int find_blockdriver(FAR const char *pathname, int mountflags, /* Find the inode registered with this pathname */ - SETUP_SEARCH(&desc, pathname, false); + ret = inode_search_setup(&desc, pathname, false); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { ferr("ERROR: Failed to find %s\n", pathname); - ret = -ENOENT; goto errout_with_search; } - /* Get the search results */ - - inode = desc.node; - /* Verify that the inode is a block driver. */ if (!INODE_IS_BLOCK(inode)) @@ -121,12 +120,12 @@ int find_blockdriver(FAR const char *pathname, int mountflags, } *ppinode = inode; - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return OK; errout_with_inode: inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/driver/fs_finddriver.c b/fs/driver/fs_finddriver.c index f2724ba43d73c..4f071510a3c3c 100644 --- a/fs/driver/fs_finddriver.c +++ b/fs/driver/fs_finddriver.c @@ -50,29 +50,33 @@ FAR void *find_driver(FAR const char *pathname) { struct inode_search_s desc; + FAR struct inode *inode; FAR void *drvr = NULL; DEBUGASSERT(pathname != NULL); /* Find the inode registered with this pathname */ - SETUP_SEARCH(&desc, pathname, false); + if (inode_search_setup(&desc, pathname, false) < 0) + { + return NULL; + } /* Get the search results */ inode_lock(); - if (inode_find(&desc) < 0) + if (inode_find(&desc, &inode) < 0) { ferr("ERROR: Failed to find %s\n", pathname); } else { - drvr = desc.node->i_private; - inode_release(desc.node); + drvr = inode->i_private; + inode_release(inode); } inode_unlock(); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return drvr; } diff --git a/fs/driver/fs_findmtddriver.c b/fs/driver/fs_findmtddriver.c index e20e490acefd9..df3b0fe679e40 100644 --- a/fs/driver/fs_findmtddriver.c +++ b/fs/driver/fs_findmtddriver.c @@ -70,20 +70,19 @@ int find_mtddriver(FAR const char *pathname, FAR struct inode **ppinode) /* Find the inode registered with this pathname */ - SETUP_SEARCH(&desc, pathname, false); + ret = inode_search_setup(&desc, pathname, false); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { ferr("ERROR: Failed to find %s\n", pathname); - ret = -ENOENT; goto errout_with_search; } - /* Get the search results */ - - inode = desc.node; - /* Verify that the inode is a block driver. */ if (!INODE_IS_MTD(inode)) @@ -98,14 +97,14 @@ int find_mtddriver(FAR const char *pathname, FAR struct inode **ppinode) DEBUGASSERT(inode->u.i_mtd != NULL); *ppinode = inode; - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return OK; errout_with_inode: inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/event/event_open.c b/fs/event/event_open.c index 6781d055029dc..f6d40d7b9530c 100644 --- a/fs/event/event_open.c +++ b/fs/event/event_open.c @@ -102,15 +102,17 @@ int nxevent_open(FAR nxevent_t **event, FAR const char *name, * will have incremented the reference count on the inode. */ - SETUP_SEARCH(&desc, fullpath, false); + ret = inode_search_setup(&desc, fullpath, false); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret >= 0) { /* Something exists at this path. Get the search results */ - inode = desc.node; - /* Verify that the inode is a event group */ if (!INODE_IS_NAMEDEVENT(inode)) @@ -206,7 +208,7 @@ int nxevent_open(FAR nxevent_t **event, FAR const char *name, *event = &nevent->ne_event; } - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_open(fullpath, oflags); #endif @@ -216,6 +218,6 @@ int nxevent_open(FAR nxevent_t **event, FAR const char *name, inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/fat/fs_fat32attrib.c b/fs/fat/fs_fat32attrib.c index 6688049223444..fb2dbcc0b4d80 100644 --- a/fs/fat/fs_fat32attrib.c +++ b/fs/fat/fs_fat32attrib.c @@ -59,9 +59,13 @@ static int fat_attrib(const char *path, fat_attrib_t *retattrib, /* Find the inode for this file */ - SETUP_SEARCH(&desc, path, false); + ret = inode_search_setup(&desc, path, false); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { /* There is no mountpoint that includes in this path */ @@ -69,10 +73,6 @@ static int fat_attrib(const char *path, fat_attrib_t *retattrib, goto errout; } - /* Get the search results */ - - inode = desc.node; - /* Verify that the inode is a valid mountpoint. */ if (!INODE_IS_MOUNTPT(inode) || !inode->u.i_mops || !inode->i_private) @@ -154,7 +154,7 @@ static int fat_attrib(const char *path, fat_attrib_t *retattrib, nxmutex_unlock(&fs->fs_lock); inode_release(inode); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return OK; errout_with_lock: @@ -164,7 +164,7 @@ static int fat_attrib(const char *path, fat_attrib_t *retattrib, inode_release(inode); errout: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/inode/fs_inodefind.c b/fs/inode/fs_inodefind.c index 78cd426cf13bc..b975cf6fcaafc 100644 --- a/fs/inode/fs_inodefind.c +++ b/fs/inode/fs_inodefind.c @@ -49,7 +49,7 @@ * ****************************************************************************/ -int inode_find(FAR struct inode_search_s *desc) +int inode_find(FAR struct inode_search_s *desc, FAR struct inode **inode) { int ret; @@ -57,18 +57,22 @@ int inode_find(FAR struct inode_search_s *desc) * references on the node. */ + if (inode == NULL) + { + return -EINVAL; + } + inode_rlock(); - ret = inode_search(desc); + ret = inode_search(desc, inode); if (ret >= 0) { /* Found it */ - FAR struct inode *inode = desc->node; - DEBUGASSERT(inode != NULL); + DEBUGASSERT(*inode != NULL); /* Increment the reference count on the inode */ - atomic_add(&inode->i_crefs, 1); + atomic_add(&(*inode)->i_crefs, 1); } inode_runlock(); diff --git a/fs/inode/fs_inoderemove.c b/fs/inode/fs_inoderemove.c index 064d4a7ccfe6e..921244bcb8066 100644 --- a/fs/inode/fs_inoderemove.c +++ b/fs/inode/fs_inoderemove.c @@ -71,12 +71,14 @@ static FAR struct inode *inode_unlink(FAR const char *path) /* Find the node to unlink */ - SETUP_SEARCH(&desc, path, true); + if (inode_search_setup(&desc, path, true) < 0) + { + return NULL; + } - ret = inode_search(&desc); + ret = inode_search(&desc, &inode); if (ret >= 0) { - inode = desc.node; DEBUGASSERT(inode != NULL); if (desc.parent != NULL) @@ -137,7 +139,7 @@ static FAR struct inode *inode_unlink(FAR const char *path) } errout: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return inode; } diff --git a/fs/inode/fs_inodereserve.c b/fs/inode/fs_inodereserve.c index 1b3c970e7e1bd..a547e28026e7f 100644 --- a/fs/inode/fs_inodereserve.c +++ b/fs/inode/fs_inodereserve.c @@ -208,9 +208,13 @@ int inode_reserve(FAR const char *path, /* Find the location to insert the new subtree */ - SETUP_SEARCH(&desc, path, false); + ret = inode_search_setup(&desc, path, false); + if (ret < 0) + { + return ret; + } - ret = inode_search(&desc); + ret = inode_search(&desc, NULL); if (ret != -ENOENT) { /* It is an error if the node already exists in the tree (or if it @@ -291,6 +295,6 @@ int inode_reserve(FAR const char *path, } errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/inode/fs_inodesearch.c b/fs/inode/fs_inodesearch.c index fac611a7c8f57..7862c21e15a54 100644 --- a/fs/inode/fs_inodesearch.c +++ b/fs/inode/fs_inodesearch.c @@ -34,6 +34,8 @@ #include #include +#include +#include #include "inode/inode.h" @@ -43,12 +45,11 @@ static int _inode_compare(FAR const char *fname, FAR struct inode *inode); #ifdef CONFIG_FS_LINKS -static int _inode_linktarget(FAR struct inode *inode, +static int _inode_linktarget(FAR struct inode **inode, FAR struct inode_search_s *desc); #endif -static int _inode_search(FAR struct inode_search_s *desc); static FAR const char *_inode_getcwd(void); -static int _inode_canonicalize(FAR char *path); +static int _inode_canonicalize(FAR char *path, FAR char *dst_min); /**************************************************************************** * Public Data @@ -154,30 +155,34 @@ static int _inode_compare(FAR const char *fname, FAR struct inode *inode) ****************************************************************************/ #ifdef CONFIG_FS_LINKS -static int _inode_linktarget(FAR struct inode *inode, +static int _inode_linktarget(FAR struct inode **inode, FAR struct inode_search_s *desc) { unsigned int count = 0; bool save; int ret = -ENOENT; - DEBUGASSERT(desc != NULL && inode != NULL); + DEBUGASSERT(desc != NULL && inode != NULL && *inode != NULL); /* An infinite loop is avoided only by the loop count. */ save = desc->nofollow; - while (INODE_IS_SOFTLINK(inode)) + while (INODE_IS_SOFTLINK(*inode)) { - FAR const char *link = (FAR const char *)inode->u.i_link; + FAR const char *link = (FAR const char *)(*inode)->u.i_link; /* Reset and reinitialize the search descriptor. */ - RELEASE_SEARCH(desc); - SETUP_SEARCH(desc, link, true); + inode_search_release(desc); + ret = inode_search_setup(desc, link, true); + if (ret < 0) + { + break; + } /* Look up inode associated with the target of the symbolic link */ - ret = inode_search(desc); + ret = inode_search(desc, inode); if (ret < 0) { break; @@ -193,8 +198,7 @@ static int _inode_linktarget(FAR struct inode *inode, /* Set up for the next time through the loop */ - inode = desc->node; - DEBUGASSERT(inode != NULL); + DEBUGASSERT(*inode != NULL); } desc->nofollow = save; @@ -229,13 +233,15 @@ static int _compute_path_depth(FAR const char *path) * * Description: * Remove "." and ".." segments from an absolute path in-place. - * The path MUST start with '/'. Returns -EINVAL if ".." attempts - * to ascend beyond the root directory, or -ENAMETOOLONG if the - * canonicalized result is >= PATH_MAX bytes. + * The path MUST start with '/'. 'dst_min' is the lowest write + * position ".." may pop to (path + 1 for the host root, or just + * past the chroot prefix). ".." that would ascend beyond that + * floor is dropped. Returns -ENAMETOOLONG if the canonicalized + * result is >= PATH_MAX bytes. * ****************************************************************************/ -static int _inode_canonicalize(FAR char *path) +static int _inode_canonicalize(FAR char *path, FAR char *dst_min) { /* Skip the initial '/' -- caller guarantees absolute path */ @@ -265,22 +271,18 @@ static int _inode_canonicalize(FAR char *path) if (src[0] == '.' && src[1] == '.' && (src[2] == '/' || src[2] == '\0')) { - /* Cannot go above root */ - - if (dst <= path + 1) + if (dst > dst_min) { - return -EINVAL; - } + /* Remove trailing slash first */ - /* Remove trailing slash first */ - - dst--; + dst--; - /* Scan backward to find the previous '/' */ + /* Scan backward to find the previous '/' */ - while (dst > path + 1 && *(dst - 1) != '/') - { - dst--; + while (dst > dst_min && *(dst - 1) != '/') + { + dst--; + } } src += (src[2] == '/') ? 3 : 2; @@ -358,108 +360,240 @@ static int _inode_checkpath(const char *path) return pathlen >= PATH_MAX ? -ENAMETOOLONG : OK; } +#ifdef CONFIG_FS_CHROOT /**************************************************************************** - * Name: _inode_search + * Name: _inode_root_path * * Description: - * Find the inode associated with 'path' returning the inode references - * and references to its companion nodes. This is the internal, common - * implementation of inode_search(). + * Return the calling group's jail prefix, or NULL if none is installed. * - * If a mountpoint is encountered in the search prior to encountering the - * terminal node, the search will terminate at the mountpoint inode. That - * inode and the relative path from the mountpoint, 'relpath' will be - * returned. + ****************************************************************************/ + +static FAR const char *_inode_root_path(void) +{ + FAR struct tcb_s *tcb = nxsched_self(); + + if (tcb != NULL && tcb->group != NULL) + { + return tcb->group->tg_root; + } + + return NULL; +} +#endif + +/**************************************************************************** + * Name: _inode_getcwd * - * If a soft link is encountered that is not the terminal node in the path, - * that link WILL be deferenced unconditionally. + * Description: + * Return the current working directory * - * Assumptions: - * The caller holds the g_inode_sem semaphore + ****************************************************************************/ + +static FAR const char *_inode_getcwd(void) +{ + FAR const char *pwd = ""; + +#ifndef CONFIG_DISABLE_ENVIRON + pwd = getenv("PWD"); + if (pwd == NULL) + { + pwd = CONFIG_LIBC_HOMEDIR; + } +#endif + + return pwd; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: inode_search_release + * + * Description: + * Release any buffer allocated by inode_search_setup(). * ****************************************************************************/ -static int _inode_search(FAR struct inode_search_s *desc) +void inode_search_release(FAR struct inode_search_s *desc) { - FAR const char *name; - FAR struct inode *inode = g_root_inode; - FAR struct inode *left = NULL; - FAR struct inode *above = NULL; - FAR const char *relpath = NULL; + if (desc->buffer != NULL) + { + lib_put_tempbuffer(desc->buffer); + desc->buffer = NULL; + } +} + +/**************************************************************************** + * Name: inode_search_setup + * + * Description: + * Initialize a search descriptor and make 'path' host-absolute: join + * $PWD if it is relative, prepend the chroot jail if one is installed, + * and canonicalize "." / "..". On success desc->path points at + * desc->buffer. + * + ****************************************************************************/ + +int inode_search_setup(FAR struct inode_search_s *desc, + FAR const char *path, bool nofollow) +{ + FAR const char *cwd = NULL; + FAR const char *root = NULL; + FAR char *dst_min; + size_t rootlen = 0; + size_t buflen; int ret; - ret = _inode_checkpath(desc->path); + desc->path = path; + desc->peer = NULL; + desc->parent = NULL; + desc->relpath = NULL; + desc->buffer = NULL; + desc->nofollow = nofollow; + + if (path == NULL) + { + return -EINVAL; + } + + ret = _inode_checkpath(path); if (ret < 0) { return ret; } - /* Ensure we have a writable buffer for path manipulation */ - - if (desc->buffer == NULL) +#ifdef CONFIG_FS_CHROOT + root = _inode_root_path(); + if (root != NULL) { - FAR const char *cwd = NULL; - size_t buflen; - - /* For a relative path the absolute form is "/". That - * concatenation can exceed PATH_MAX even when the relative path - * itself is within PATH_MAX: a relative path of PATH_MAX-1 bytes - * is legal per pathconf(_PC_PATH_MAX), but the prefix added by the - * cwd pushes the uncanonicalized form past the limit. Size the - * buffer to hold the full absolute form so that ".." segments are - * collapsed against the correct suffix; truncating first could - * drop the trailing component and let ".." collapse the path onto - * a directory (yielding the wrong errno, e.g. EISDIR, instead of - * resolving the file). _inode_canonicalize() still rejects any - * result whose canonicalized length reaches PATH_MAX. - */ + rootlen = strlen(root); + } +#endif - if (*desc->path != '/') - { - cwd = _inode_getcwd(); - buflen = strlen(cwd) + 1 + strlen(desc->path) + 1; - } - else - { - buflen = strlen(desc->path) + 1; - } + /* For a relative path the absolute form is "/". That + * concatenation can exceed PATH_MAX even when the relative path + * itself is within PATH_MAX: a relative path of PATH_MAX-1 bytes + * is legal per pathconf(_PC_PATH_MAX), but the prefix added by the + * cwd pushes the uncanonicalized form past the limit. Size the + * buffer to hold the full absolute form so that ".." segments are + * collapsed against the correct suffix; truncating first could + * drop the trailing component and let ".." collapse the path onto + * a directory (yielding the wrong errno, e.g. EISDIR, instead of + * resolving the file). _inode_canonicalize() still rejects any + * result whose canonicalized length reaches PATH_MAX. + */ - if (buflen < PATH_MAX) - { - buflen = PATH_MAX; - } + if (*path != '/') + { + cwd = _inode_getcwd(); + buflen = strlen(cwd) + 1 + strlen(path) + 1; + } + else + { + buflen = strlen(path) + 1; + } - desc->buffer = lib_get_tempbuffer(buflen); - if (desc->buffer == NULL) - { - return -ENOMEM; - } + buflen += rootlen; + if (buflen < PATH_MAX) + { + buflen = PATH_MAX; + } + + desc->buffer = lib_get_tempbuffer(buflen); + if (desc->buffer == NULL) + { + return -ENOMEM; + } + if (root != NULL) + { if (cwd != NULL) { - snprintf(desc->buffer, buflen, "%s/%s", cwd, desc->path); + snprintf(desc->buffer, buflen, "%s%s/%s", root, cwd, path); } else { - strlcpy(desc->buffer, desc->path, buflen); + snprintf(desc->buffer, buflen, "%s%s", root, path); } - - desc->path = desc->buffer; } + else if (cwd != NULL) + { + snprintf(desc->buffer, buflen, "%s/%s", cwd, path); + } + else + { + strlcpy(desc->buffer, path, buflen); + } + + desc->path = desc->buffer; /* Canonicalize the path to remove "." and ".." segments. This ensures * that mountpoint relpath never contains ".." which most filesystems - * (tmpfs, romfs, etc.) cannot resolve. + * (tmpfs, romfs, etc.) cannot resolve. When a jail is installed, + * dst_min keeps ".." from popping above tg_root. */ - ret = _inode_canonicalize(desc->buffer); + dst_min = desc->buffer + 1; +#ifdef CONFIG_FS_CHROOT + if (root != NULL) + { + dst_min = desc->buffer + rootlen; + if (rootlen > 0 && root[rootlen - 1] != '/') + { + dst_min++; + } + } +#endif + + ret = _inode_canonicalize(desc->buffer, dst_min); if (ret < 0) { - return ret; + inode_search_release(desc); } + return ret; +} + +/**************************************************************************** + * Name: inode_search + * + * Description: + * Find the inode associated with 'path' returning the inode references + * and references to its companion nodes. + * + * If a mountpoint is encountered in the search prior to encountering the + * terminal node, the search will terminate at the mountpoint inode. That + * inode and the relative path from the mountpoint, 'relpath' will be + * returned. + * + * inode_search will follow soft links in path leading up to the terminal + * node. Whether or no inode_search() will deference that terminal node + * depends on the 'nofollow' input. + * + * If a soft link is encountered that is not the terminal node in the path, + * that link WILL be deferenced unconditionally. + * + * Assumptions: + * The caller holds the g_inode_sem semaphore + * The descriptor was initialized with inode_search_setup() + * + ****************************************************************************/ + +int inode_search(FAR struct inode_search_s *desc, FAR struct inode **inodep) +{ + FAR const char *name; + FAR struct inode *inode = g_root_inode; + FAR struct inode *left = NULL; + FAR struct inode *above = NULL; + FAR const char *relpath = NULL; + int ret = -ENOENT; + + DEBUGASSERT(desc != NULL && desc->path != NULL); + name = desc->path; - ret = -ENOENT; /* Traverse the pseudo file system node tree until either (1) all nodes * have been examined without finding the matching node, or (2) the @@ -531,6 +665,7 @@ static int _inode_search(FAR struct inode_search_s *desc) if (INODE_IS_SOFTLINK(inode)) { + FAR struct inode *newnode = inode; int status; /* If this intermediate inode in the is a soft link, then @@ -539,7 +674,7 @@ static int _inode_search(FAR struct inode_search_s *desc) * instead. */ - status = _inode_linktarget(inode, desc); + status = _inode_linktarget(&newnode, desc); if (status < 0) { /* Probably means that the target of the symbolic link @@ -551,8 +686,6 @@ static int _inode_search(FAR struct inode_search_s *desc) } else { - FAR struct inode *newnode = desc->node; - if (newnode != inode) { /* The node was a valid symbolic link and we have @@ -642,85 +775,13 @@ static int _inode_search(FAR struct inode_search_s *desc) */ desc->path = name; - desc->node = inode; desc->peer = left; desc->parent = above; desc->relpath = relpath; - return ret; -} - -/**************************************************************************** - * Name: _inode_getcwd - * - * Description: - * Return the current working directory - * - ****************************************************************************/ - -static FAR const char *_inode_getcwd(void) -{ - FAR const char *pwd = ""; - -#ifndef CONFIG_DISABLE_ENVIRON - pwd = getenv("PWD"); - if (pwd == NULL) - { - pwd = CONFIG_LIBC_HOMEDIR; - } -#endif - - return pwd; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: inode_search - * - * Description: - * Find the inode associated with 'path' returning the inode references - * and references to its companion nodes. - * - * If a mountpoint is encountered in the search prior to encountering the - * terminal node, the search will terminate at the mountpoint inode. That - * inode and the relative path from the mountpoint, 'relpath' will be - * returned. - * - * inode_search will follow soft links in path leading up to the terminal - * node. Whether or no inode_search() will deference that terminal node - * depends on the 'nofollow' input. - * - * If a soft link is encountered that is not the terminal node in the path, - * that link WILL be deferenced unconditionally. - * - * Assumptions: - * The caller holds the g_inode_sem semaphore - * - ****************************************************************************/ - -int inode_search(FAR struct inode_search_s *desc) -{ - int ret; - - /* Perform the common _inode_search() logic. This does everything except - * operations special operations that must be performed on the terminal - * node if node is a symbolic link. - */ - - DEBUGASSERT(desc != NULL && desc->path != NULL); - - ret = _inode_search(desc); #ifdef CONFIG_FS_LINKS if (ret >= 0) { - FAR struct inode *inode; - - /* Search completed successfully */ - - inode = desc->node; DEBUGASSERT(inode != NULL); /* Is the terminal node a softlink? Should we follow it? */ @@ -733,7 +794,7 @@ int inode_search(FAR struct inode_search_s *desc) * link target of the final symbolic link in the series. */ - ret = _inode_linktarget(inode, desc); + ret = _inode_linktarget(&inode, desc); if (ret < 0) { /* The most likely cause for failure is that the target of the @@ -749,12 +810,15 @@ int inode_search(FAR struct inode_search_s *desc) inode = inode->i_private; DEBUGASSERT(inode != NULL); - - desc->node = inode; } } #endif + if (inodep != NULL) + { + *inodep = inode; + } + return ret; } diff --git a/fs/inode/inode.h b/fs/inode/inode.h index 79a241e6dcce9..eb015c7afb4e7 100644 --- a/fs/inode/inode.h +++ b/fs/inode/inode.h @@ -39,36 +39,11 @@ #include #include #include -#include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -#define SETUP_SEARCH(d,p,n) \ - do \ - { \ - (d)->path = (p); \ - (d)->node = NULL; \ - (d)->peer = NULL; \ - (d)->parent = NULL; \ - (d)->relpath = NULL; \ - (d)->buffer = NULL; \ - (d)->nofollow = (n); \ - } \ - while (0) - -#define RELEASE_SEARCH(d) \ - do \ - { \ - if ((d)->buffer != NULL) \ - { \ - lib_put_tempbuffer((d)->buffer); \ - (d)->buffer = NULL; \ - } \ - } \ - while (0) - #if CONFIG_FS_BACKTRACE > 0 # define FS_ADD_BACKTRACE(fd) \ do \ @@ -110,8 +85,6 @@ * * path - INPUT: Path of inode to find * OUTPUT: Residual part of path not traversed - * node - INPUT: (not used) - * OUTPUT: On success, holds the pointer to the inode found. * peer - INPUT: (not used) * OUTPUT: The inode to the "left" of the inode found. * parent - INPUT: (not used) @@ -138,7 +111,6 @@ struct inode_search_s { FAR const char *path; /* Path of inode to find */ - FAR struct inode *node; /* Pointer to the inode found */ FAR struct inode *peer; /* Node to the "left" for the found inode */ FAR struct inode *parent; /* Node "above" the found inode */ FAR const char *relpath; /* Relative path into the mountpoint */ @@ -243,12 +215,41 @@ void inode_runlock(void); * If a soft link is encountered that is not the terminal node in the path, * that link WILL be deferenced unconditionally. * + * Input Parameters: + * desc - Search descriptor initialized with inode_search_setup() + * inode - OUTPUT: The found inode. May be NULL if the caller only + * cares about existence (e.g. inode_reserve). + * * Assumptions: * The caller holds the g_inode_sem semaphore + * The descriptor was initialized with inode_search_setup() + * + ****************************************************************************/ + +int inode_search(FAR struct inode_search_s *desc, FAR struct inode **inode); + +/**************************************************************************** + * Name: inode_search_setup + * + * Description: + * Initialize a search descriptor and make 'path' host-absolute: join + * $PWD if it is relative, prepend the chroot jail if one is installed, + * and canonicalize "." / "..". * ****************************************************************************/ -int inode_search(FAR struct inode_search_s *desc); +int inode_search_setup(FAR struct inode_search_s *desc, + FAR const char *path, bool nofollow); + +/**************************************************************************** + * Name: inode_search_release + * + * Description: + * Release any buffer allocated by inode_search_setup(). + * + ****************************************************************************/ + +void inode_search_release(FAR struct inode_search_s *desc); /**************************************************************************** * Name: inode_find @@ -260,9 +261,13 @@ int inode_search(FAR struct inode_search_s *desc); * difference between inode_find() and inode_search is that inode_find() * will lock the inode tree and increment the reference count on the inode. * + * Input Parameters: + * desc - Search descriptor initialized with inode_search_setup() + * inode - OUTPUT: The found inode. Must not be NULL. + * ****************************************************************************/ -int inode_find(FAR struct inode_search_s *desc); +int inode_find(FAR struct inode_search_s *desc, FAR struct inode **inode); /**************************************************************************** * Name: inode_stat diff --git a/fs/littlefs/lfs_vfs.c b/fs/littlefs/lfs_vfs.c index e1853421c4333..8cc3c0585e17d 100644 --- a/fs/littlefs/lfs_vfs.c +++ b/fs/littlefs/lfs_vfs.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -901,10 +902,12 @@ static int littlefs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) case FIOC_FILEPATH: { FAR char *path = (FAR char *)(uintptr_t)arg; + ret = inode_getpath(inode, path, PATH_MAX); if (ret >= 0) { size_t len = strlen(path); + if (path[len - 1] != '/') { path[len++] = '/'; diff --git a/fs/mount/fs_automount.c b/fs/mount/fs_automount.c index 2d5988aeea4b9..37e28ee44cc37 100644 --- a/fs/mount/fs_automount.c +++ b/fs/mount/fs_automount.c @@ -397,6 +397,7 @@ static int automount_ioctl(FAR struct file *filep, int cmd, static int automount_findinode(FAR const char *path) { struct inode_search_s desc; + FAR struct inode *inode; int ret; /* Make sure that we were given a path */ @@ -409,9 +410,14 @@ static int automount_findinode(FAR const char *path) /* Find the inode */ - SETUP_SEARCH(&desc, path, false); + ret = inode_search_setup(&desc, path, false); + if (ret < 0) + { + inode_runlock(); + return ret; + } - ret = inode_search(&desc); + ret = inode_search(&desc, &inode); /* Did we find it? */ @@ -420,27 +426,33 @@ static int automount_findinode(FAR const char *path) /* No.. Not found */ ret = OK_NOENT; + inode = NULL; } + else + { + inode_addref(inode); - /* Yes.. is it a mount point? */ + /* Yes.. is it a mount point? */ - else if (INODE_IS_MOUNTPT(desc.node)) - { - /* Yes.. we found a mountpoint at this path */ + if (INODE_IS_MOUNTPT(inode)) + { + /* Yes.. we found a mountpoint at this path */ - ret = OK_EXIST; - } - else - { - /* No.. then something is in the way */ + ret = OK_EXIST; + } + else + { + /* No.. then something is in the way */ - ret = -ENOTDIR; + ret = -ENOTDIR; + } } /* Relinquish our exclusive access to the inode try and return the result */ inode_runlock(); - RELEASE_SEARCH(&desc); + inode_release(inode); + inode_search_release(&desc); return ret; } @@ -470,56 +482,56 @@ static void automount_mount(FAR struct automounter_state_s *priv) ret = automount_findinode(lower->mountpoint); switch (ret) { - case OK_EXIST: + case OK_EXIST: - /* REVISIT: What should we do in this case? I think that this would - * happen only if a previous unmount failed? I suppose that we should - * try to unmount again because the mount might be stale. - */ + /* REVISIT: What should we do in this case? I think that this would + * happen only if a previous unmount failed? I suppose that we + * should try to unmount again because the mount might be stale. + */ - fwarn("WARNING: Mountpoint %s already exists\n", lower->mountpoint); - ret = automount_unmount(priv); - if (ret < 0) - { - /* We failed to unmount (again?). Complain and abort. */ + fwarn("WARNING: Mountpoint %s already exists\n", lower->mountpoint); + ret = automount_unmount(priv); + if (ret < 0) + { + /* We failed to unmount (again?). Complain and abort. */ - ferr("ERROR: automount_unmount failed: %d\n", ret); - return; - } + ferr("ERROR: automount_unmount failed: %d\n", ret); + return; + } - /* We successfully unmounted the file system. Fall through to - * mount it again. - */ + /* We successfully unmounted the file system. Fall through to + * mount it again. + */ - case OK_NOENT: + case OK_NOENT: - /* If we get here, then the volume must not be mounted */ + /* If we get here, then the volume must not be mounted */ - DEBUGASSERT(!priv->mounted); + DEBUGASSERT(!priv->mounted); - /* Mount the file system */ + /* Mount the file system */ - ret = nx_mount(lower->blockdev, lower->mountpoint, lower->fstype, - 0, NULL); - if (ret < 0) - { - ferr("ERROR: Mount failed: %d\n", ret); - return; - } + ret = nx_mount(lower->blockdev, lower->mountpoint, lower->fstype, + 0, NULL); + if (ret < 0) + { + ferr("ERROR: Mount failed: %d\n", ret); + return; + } - /* Indicate that the volume is mounted */ + /* Indicate that the volume is mounted */ - priv->mounted = true; + priv->mounted = true; #ifdef CONFIG_FS_AUTOMOUNTER_DRIVER - automount_notify(priv); + automount_notify(priv); #endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ - break; + break; - default: - ferr("ERROR: automount_findinode failed: %d\n", ret); - break; + default: + ferr("ERROR: automount_findinode failed: %d\n", ret); + break; } } @@ -550,69 +562,69 @@ static int automount_unmount(FAR struct automounter_state_s *priv) ret = automount_findinode(lower->mountpoint); switch (ret) { - case OK_EXIST: + case OK_EXIST: - /* If we get here, then the volume must be mounted */ + /* If we get here, then the volume must be mounted */ - DEBUGASSERT(priv->mounted); + DEBUGASSERT(priv->mounted); - /* Un-mount the volume */ + /* Un-mount the volume */ - ret = nx_umount2(lower->mountpoint, MNT_FORCE); - if (ret < 0) - { - /* We expect the error to be EBUSY meaning that the volume could - * not be unmounted because there are currently reference via open - * files or directories. - */ + ret = nx_umount2(lower->mountpoint, MNT_FORCE); + if (ret < 0) + { + /* We expect the error to be EBUSY meaning that the volume could + * not be unmounted because there are currently reference via + * open files or directories. + */ - if (ret == -EBUSY) - { - finfo("WARNING: Volume is busy, try again later\n"); + if (ret == -EBUSY) + { + finfo("WARNING: Volume is busy, try again later\n"); - /* Start a timer to retry the umount2 after a delay */ + /* Start a timer to retry the umount2 after a delay */ - ret = wd_start(&priv->wdog, lower->udelay, - automount_timeout, (wdparm_t)priv); - if (ret < 0) - { - ferr("ERROR: wd_start failed: %d\n", ret); - return ret; - } - } + ret = wd_start(&priv->wdog, lower->udelay, + automount_timeout, (wdparm_t)priv); + if (ret < 0) + { + ferr("ERROR: wd_start failed: %d\n", ret); + return ret; + } + } - /* Other errors are fatal */ + /* Other errors are fatal */ - else - { - ferr("ERROR: umount2 failed: %d\n", ret); - return ret; - } - } + else + { + ferr("ERROR: umount2 failed: %d\n", ret); + return ret; + } + } - /* Fall through */ + /* Fall through */ - case OK_NOENT: + case OK_NOENT: - /* The mountpoint is not present. This is normal behavior in the - * case where the user manually un-mounted the volume before removing - * media. Nice job, Mr. user. - */ + /* The mountpoint is not present. This is normal behavior in the + * case where the user manually un-mounted the volume before removing + * media. Nice job, Mr. user. + */ - if (priv->mounted) - { - priv->mounted = false; + if (priv->mounted) + { + priv->mounted = false; #ifdef CONFIG_FS_AUTOMOUNTER_DRIVER - automount_notify(priv); + automount_notify(priv); #endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ - } + } - return OK; + return OK; - default: - ferr("ERROR: automount_findinode failed: %d\n", ret); - return ret; + default: + ferr("ERROR: automount_findinode failed: %d\n", ret); + return ret; } } @@ -807,6 +819,7 @@ FAR void *automount_initialize(FAR const struct automount_lower_s *lower) int ret; #ifdef CONFIG_FS_AUTOMOUNTER_DRIVER FAR char *devpath = lib_get_pathbuffer(); + if (devpath == NULL) { return NULL; @@ -917,6 +930,7 @@ void automount_uninitialize(FAR void *handle) if (priv->registered) { FAR char *devpath = lib_get_pathbuffer(); + if (devpath == NULL) { return; diff --git a/fs/mount/fs_mount.c b/fs/mount/fs_mount.c index 39c87e95cdac9..70fc755c3dfcf 100644 --- a/fs/mount/fs_mount.c +++ b/fs/mount/fs_mount.c @@ -380,16 +380,20 @@ int nx_mount(FAR const char *source, FAR const char *target, #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS /* Check if the inode already exists */ - SETUP_SEARCH(&desc, target, false); + ret = inode_search_setup(&desc, target, false); + if (ret < 0) + { + inode_unlock(); + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &mountpt_inode); if (ret >= 0) { /* Successfully found. The reference count on the inode has been * incremented. */ - mountpt_inode = desc.node; DEBUGASSERT(mountpt_inode != NULL); /* But is it a directory node (i.e., not a driver or other special @@ -521,7 +525,7 @@ int nx_mount(FAR const char *source, FAR const char *target, #endif #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #endif #ifdef CONFIG_FS_NOTIFY notify_create(target); @@ -539,7 +543,7 @@ int nx_mount(FAR const char *source, FAR const char *target, errout_with_lock: inode_unlock(); #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #endif errout_with_inode: diff --git a/fs/mount/fs_umount2.c b/fs/mount/fs_umount2.c index a0cced51370fd..6ac21c7f405ad 100644 --- a/fs/mount/fs_umount2.c +++ b/fs/mount/fs_umount2.c @@ -72,9 +72,13 @@ int nx_umount2(FAR const char *target, unsigned int flags) /* Find the mountpt */ - SETUP_SEARCH(&desc, target, false); + ret = inode_search_setup(&desc, target, false); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &mountpt_inode); if (ret < 0) { goto errout_with_search; @@ -82,7 +86,6 @@ int nx_umount2(FAR const char *target, unsigned int flags) /* Get the search results */ - mountpt_inode = desc.node; DEBUGASSERT(mountpt_inode != NULL); /* Verify that the inode is a mountpoint */ @@ -189,7 +192,7 @@ int nx_umount2(FAR const char *target, unsigned int flags) inode_release(blkdrvr_inode); } - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_unmount(target); #endif @@ -208,7 +211,7 @@ int nx_umount2(FAR const char *target, unsigned int flags) } errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); errout: return ret; diff --git a/fs/mqueue/mq_open.c b/fs/mqueue/mq_open.c index 798ece2e26404..cb4a0ec1f5cb5 100644 --- a/fs/mqueue/mq_open.c +++ b/fs/mqueue/mq_open.c @@ -174,15 +174,13 @@ static int file_mq_vopen(FAR struct file *mq, FAR const char *mq_name, if (!mq || !mq_name || *mq_name == '\0') { - ret = -EINVAL; - goto errout; + return -EINVAL; } if (sizeof(CONFIG_FS_MQUEUE_VFS_PATH) + 1 + strlen(mq_name) >= MAX_MQUEUE_PATH) { - ret = -ENAMETOOLONG; - goto errout; + return -ENAMETOOLONG; } /* Were we asked to create it? */ @@ -235,15 +233,18 @@ static int file_mq_vopen(FAR struct file *mq, FAR const char *mq_name, * have incremented the reference count on the inode. */ - SETUP_SEARCH(&desc, fullpath, false); + ret = inode_search_setup(&desc, fullpath, false); + if (ret < 0) + { + leave_critical_section(flags); + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret >= 0) { /* Something exists at this path. Get the search results */ - inode = desc.node; - /* Verify that the inode is a message queue */ if (!INODE_IS_MQUEUE(inode)) @@ -345,7 +346,7 @@ static int file_mq_vopen(FAR struct file *mq, FAR const char *mq_name, } } - RELEASE_SEARCH(&desc); + inode_search_release(&desc); leave_critical_section(flags); #ifdef CONFIG_FS_NOTIFY notify_open(fullpath, oflags); @@ -356,7 +357,7 @@ static int file_mq_vopen(FAR struct file *mq, FAR const char *mq_name, inode_release(inode); errout_with_lock: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); leave_critical_section(flags); errout: diff --git a/fs/mqueue/mq_unlink.c b/fs/mqueue/mq_unlink.c index 508231ee6f4c4..107683b1a5961 100644 --- a/fs/mqueue/mq_unlink.c +++ b/fs/mqueue/mq_unlink.c @@ -113,9 +113,13 @@ int file_mq_unlink(FAR const char *mq_name) /* Get the inode for this message queue. */ - SETUP_SEARCH(&desc, fullpath, false); + ret = inode_search_setup(&desc, fullpath, false); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { /* There is no inode that includes in this path */ @@ -125,8 +129,6 @@ int file_mq_unlink(FAR const char *mq_name) /* Get the search results */ - inode = desc.node; - /* Verify that what we found is, indeed, a message queue */ if (!INODE_IS_MQUEUE(inode)) @@ -172,7 +174,7 @@ int file_mq_unlink(FAR const char *mq_name) inode_unlock(); mq_inode_release(inode); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_unlink(fullpath); #endif @@ -185,7 +187,7 @@ int file_mq_unlink(FAR const char *mq_name) inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/partition/fs_partition.c b/fs/partition/fs_partition.c index 9bb6fab735676..e751d00e33688 100644 --- a/fs/partition/fs_partition.c +++ b/fs/partition/fs_partition.c @@ -29,6 +29,8 @@ #include #include +#include + #include "driver/driver.h" #include "partition.h" diff --git a/fs/semaphore/sem_open.c b/fs/semaphore/sem_open.c index 491e34945d285..f39f6f2e5e37f 100644 --- a/fs/semaphore/sem_open.c +++ b/fs/semaphore/sem_open.c @@ -111,15 +111,17 @@ int nxsem_open(FAR sem_t **sem, FAR const char *name, int oflags, ...) * will have incremented the reference count on the inode. */ - SETUP_SEARCH(&desc, fullpath, false); + ret = inode_search_setup(&desc, fullpath, false); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret >= 0) { /* Something exists at this path. Get the search results */ - inode = desc.node; - /* Verify that the inode is a semaphore */ if (!INODE_IS_NAMEDSEM(inode)) @@ -231,7 +233,7 @@ int nxsem_open(FAR sem_t **sem, FAR const char *name, int oflags, ...) *sem = &nsem->ns_sem; } - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_open(fullpath, oflags); #endif @@ -241,7 +243,7 @@ int nxsem_open(FAR sem_t **sem, FAR const char *name, int oflags, ...) inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/semaphore/sem_unlink.c b/fs/semaphore/sem_unlink.c index 9fde96acc9520..2aab652877760 100644 --- a/fs/semaphore/sem_unlink.c +++ b/fs/semaphore/sem_unlink.c @@ -78,9 +78,13 @@ int nxsem_unlink(FAR const char *name) /* Get the inode for this semaphore. */ - SETUP_SEARCH(&desc, fullpath, false); + ret = inode_search_setup(&desc, fullpath, false); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { /* There is no inode that includes in this path */ @@ -90,8 +94,6 @@ int nxsem_unlink(FAR const char *name) /* Get the search results */ - inode = desc.node; - /* Verify that what we found is, indeed, a semaphore */ if (!INODE_IS_NAMEDSEM(inode)) @@ -136,7 +138,7 @@ int nxsem_unlink(FAR const char *name) inode_unlock(); ret = nxsem_close(&inode->u.i_nsem->ns_sem); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_unlink(fullpath); #endif @@ -149,6 +151,6 @@ int nxsem_unlink(FAR const char *name) inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/shm/shm_open.c b/fs/shm/shm_open.c index e7db849cb9050..c33602659519d 100644 --- a/fs/shm/shm_open.c +++ b/fs/shm/shm_open.c @@ -82,23 +82,25 @@ static int file_shm_open(FAR struct file *shm, FAR const char *name, /* Get the inode for this shm object */ - SETUP_SEARCH(&desc, fullpath, false); + ret = inode_search_setup(&desc, fullpath, false); + if (ret < 0) + { + return ret; + } inode_lock(); - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret >= 0) { /* Something exists at this path. Get the search results */ - inode = desc.node; - /* Verify that the inode is an shm object */ if (!INODE_IS_SHM(inode)) { ret = -EINVAL; inode_release(inode); - goto errout_with_sem; + goto errout_with_lock; } /* It exists and is an shm object. Check if the caller wanted to @@ -109,7 +111,7 @@ static int file_shm_open(FAR struct file *shm, FAR const char *name, { ret = -EEXIST; inode_release(inode); - goto errout_with_sem; + goto errout_with_lock; } #ifdef CONFIG_FS_PERMISSION @@ -117,7 +119,7 @@ static int file_shm_open(FAR struct file *shm, FAR const char *name, if (ret < 0) { inode_release(inode); - goto errout_with_sem; + goto errout_with_lock; } #endif @@ -141,7 +143,7 @@ static int file_shm_open(FAR struct file *shm, FAR const char *name, /* The shm does not exist and O_CREAT is not set */ ret = -ENOENT; - goto errout_with_sem; + goto errout_with_lock; } /* Create an inode in the pseudo-filesystem at this path */ @@ -149,7 +151,7 @@ static int file_shm_open(FAR struct file *shm, FAR const char *name, ret = inode_reserve(fullpath, mode, &inode); if (ret < 0) { - goto errout_with_sem; + goto errout_with_lock; } INODE_SET_SHM(inode); @@ -163,9 +165,9 @@ static int file_shm_open(FAR struct file *shm, FAR const char *name, shm->f_oflags = oflags | O_NOFOLLOW; shm->f_inode = inode; -errout_with_sem: +errout_with_lock: inode_unlock(); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY if (ret >= 0) { diff --git a/fs/shm/shm_unlink.c b/fs/shm/shm_unlink.c index bd4850c8294d5..67960392f48af 100644 --- a/fs/shm/shm_unlink.c +++ b/fs/shm/shm_unlink.c @@ -77,21 +77,23 @@ static int file_shm_unlink(FAR const char *name) /* Get the inode for this shm object */ - SETUP_SEARCH(&desc, fullpath, false); + ret = inode_search_setup(&desc, fullpath, false); + if (ret < 0) + { + return ret; + } inode_lock(); - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { /* There is no inode that includes in this path */ - goto errout_with_sem; + goto errout_with_lock; } /* Get the search results */ - inode = desc.node; - /* Verify that what we found is, indeed, an shm inode */ if (!INODE_IS_SHM(inode)) @@ -134,9 +136,9 @@ static int file_shm_unlink(FAR const char *name) errout_with_inode: inode_release(inode); -errout_with_sem: +errout_with_lock: inode_unlock(); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY if (ret >= 0) { diff --git a/fs/unionfs/fs_unionfs.c b/fs/unionfs/fs_unionfs.c index 87da2bd577a8e..06b86cd32a508 100644 --- a/fs/unionfs/fs_unionfs.c +++ b/fs/unionfs/fs_unionfs.c @@ -2538,9 +2538,13 @@ static int unionfs_getmount(FAR const char *path, FAR struct inode **inode) /* Find the mountpt */ - SETUP_SEARCH(&desc, path, false); + ret = inode_search_setup(&desc, path, false); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &minode); if (ret < 0) { /* Mountpoint inode not found */ @@ -2550,7 +2554,6 @@ static int unionfs_getmount(FAR const char *path, FAR struct inode **inode) /* Get the search results */ - minode = desc.node; DEBUGASSERT(minode != NULL); /* Verify that the inode is a mountpoint. @@ -2571,14 +2574,14 @@ static int unionfs_getmount(FAR const char *path, FAR struct inode **inode) /* Success! */ *inode = minode; - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return OK; errout_with_inode: inode_release(minode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/vfs/CMakeLists.txt b/fs/vfs/CMakeLists.txt index b5318de671952..85a99add6baeb 100644 --- a/fs/vfs/CMakeLists.txt +++ b/fs/vfs/CMakeLists.txt @@ -54,6 +54,10 @@ set(SRCS fs_truncate.c fs_link.c) +if(CONFIG_FS_CHROOT) + list(APPEND SRCS fs_chroot.c) +endif() + # File notify support if(CONFIG_FS_NOTIFY) diff --git a/fs/vfs/Make.defs b/fs/vfs/Make.defs index e8e588680e9aa..172d779fc95de 100644 --- a/fs/vfs/Make.defs +++ b/fs/vfs/Make.defs @@ -29,6 +29,10 @@ CSRCS += fs_rename.c fs_rmdir.c fs_select.c fs_sendfile.c fs_stat.c CSRCS += fs_statfs.c fs_uio.c fs_unlink.c fs_write.c fs_dir.c fs_fsync.c CSRCS += fs_syncfs.c fs_truncate.c fs_link.c +ifeq ($(CONFIG_FS_CHROOT),y) +CSRCS += fs_chroot.c +endif + ifeq ($(CONFIG_FS_NOTIFY),y) CSRCS += fs_inotify.c endif diff --git a/fs/vfs/fs_chroot.c b/fs/vfs/fs_chroot.c new file mode 100644 index 0000000000000..35b4ac8eebfd1 --- /dev/null +++ b/fs/vfs/fs_chroot.c @@ -0,0 +1,132 @@ +/**************************************************************************** + * fs/vfs/fs_chroot.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "inode/inode.h" +#include "fs_heap.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: chroot + * + * Description: + * Cause the named directory to become the root directory, that is, the + * starting point for path names beginning with '/'. + * + * Input Parameters: + * path - Directory to use as the new root + * + * Returned Value: + * 0(OK) on success; -1(ERROR) on failure with errno set appropriately. + * + ****************************************************************************/ + +int chroot(FAR const char *path) +{ + FAR struct tcb_s *rtcb; + FAR struct task_group_s *group; + FAR char *newroot; + struct inode_search_s desc; + struct stat buf; + int ret; + + if (path == NULL || path[0] == '\0') + { + set_errno(ENOENT); + return ERROR; + } + + rtcb = nxsched_self(); + DEBUGASSERT(rtcb != NULL && rtcb->group != NULL); + group = rtcb->group; + +#ifdef CONFIG_SCHED_USER_IDENTITY + if (group->tg_euid != 0) + { + set_errno(EPERM); + return ERROR; + } +#endif + + ret = nx_stat(path, &buf, 1); + if (ret < 0) + { + set_errno(-ret); + return ERROR; + } + + if (!S_ISDIR(buf.st_mode)) + { + set_errno(ENOTDIR); + return ERROR; + } + + /* Resolve to a host absolute path the same way lookups do: make + * absolute, prepend the current jail, and canonicalize. No second + * inode walk. + */ + + ret = inode_search_setup(&desc, path, true); + if (ret < 0) + { + set_errno(-ret); + return ERROR; + } + + /* Host "/" means no jail. Clear any previous root. */ + + if (strcmp(desc.path, "/") == 0) + { + fs_heap_free(group->tg_root); + group->tg_root = NULL; + inode_search_release(&desc); + return OK; + } + + newroot = fs_heap_strdup(desc.path); + inode_search_release(&desc); + if (newroot == NULL) + { + set_errno(ENOMEM); + return ERROR; + } + + fs_heap_free(group->tg_root); + group->tg_root = newroot; + return OK; +} diff --git a/fs/vfs/fs_chstat.c b/fs/vfs/fs_chstat.c index a8adcb342a3d9..6fdc6e31a4878 100644 --- a/fs/vfs/fs_chstat.c +++ b/fs/vfs/fs_chstat.c @@ -54,9 +54,13 @@ static int chstat_recursive(FAR const char *path, /* Get an inode for this path */ - SETUP_SEARCH(&desc, path, true); + ret = inode_search_setup(&desc, path, true); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { /* This name does not refer to an inode in the pseudo file system and @@ -68,7 +72,6 @@ static int chstat_recursive(FAR const char *path, /* Get the search results */ - inode = desc.node; DEBUGASSERT(inode != NULL); ret = inode_checkpathperm(inode, 0, 0); @@ -113,7 +116,7 @@ static int chstat_recursive(FAR const char *path, inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/vfs/fs_close.c b/fs/vfs/fs_close.c index aa8ddbf5b33c8..4f87712cab455 100644 --- a/fs/vfs/fs_close.c +++ b/fs/vfs/fs_close.c @@ -34,6 +34,7 @@ #include #include +#include #ifdef CONFIG_FDSAN # include diff --git a/fs/vfs/fs_link.c b/fs/vfs/fs_link.c index 2b8c652a67acb..e31b73d2232f4 100644 --- a/fs/vfs/fs_link.c +++ b/fs/vfs/fs_link.c @@ -80,36 +80,37 @@ int link(FAR const char *path1, FAR const char *path2) struct inode_search_s desc_path2; FAR struct inode *target = NULL; FAR struct inode *newinode = NULL; - int errcode; int ret; if (path1 == NULL || path2 == NULL) { - errcode = EINVAL; + ret = -EINVAL; goto errout; } if (*path1 == '\0' || *path2 == '\0') { - errcode = ENOENT; + ret = -ENOENT; goto errout; } - SETUP_SEARCH(&desc_path1, path1, false); - ret = inode_find(&desc_path1); + ret = inode_search_setup(&desc_path1, path1, false); if (ret < 0) { - errcode = -ret; - goto errout_with_search_path1; + goto errout; } - target = desc_path1.node; + ret = inode_find(&desc_path1, &target); + if (ret < 0) + { + goto errout_with_search_path1; + } if (INODE_GET_NLINK(target) >= _POSIX_LINK_MAX) { /* Too many links to the target inode */ - errcode = EMLINK; + ret = -EMLINK; goto errout_with_target; } @@ -117,13 +118,15 @@ int link(FAR const char *path1, FAR const char *path2) * 'path2' does not lie on a mounted volume. */ - SETUP_SEARCH(&desc_path2, path2, true); + ret = inode_search_setup(&desc_path2, path2, true); + if (ret < 0) + { + goto errout_with_target; + } - ret = inode_find(&desc_path2); + ret = inode_find(&desc_path2, &newinode); if (ret >= 0) { - newinode = desc_path2.node; - /* Something exists at the path2 where we are trying to create the * link. */ @@ -138,7 +141,7 @@ int link(FAR const char *path1, FAR const char *path2) if (newinode != target) { - errcode = EXDEV; + ret = -EXDEV; goto errout_with_newinode; } @@ -152,7 +155,6 @@ int link(FAR const char *path1, FAR const char *path2) desc_path2.relpath); if (ret < 0) { - errcode = -ret; goto errout_with_newinode; } } @@ -160,7 +162,7 @@ int link(FAR const char *path1, FAR const char *path2) { /* Hard links within this type of fs are not supported */ - errcode = ENOSYS; + ret = -ENOSYS; goto errout_with_newinode; } } @@ -169,7 +171,7 @@ int link(FAR const char *path1, FAR const char *path2) { /* A node already exists in the pseudofs at 'path2' */ - errcode = EEXIST; + ret = -EEXIST; goto errout_with_newinode; } } @@ -187,7 +189,6 @@ int link(FAR const char *path1, FAR const char *path2) if (ret != -ENOENT && ret != -ENOTDIR) { - errcode = -ret; goto errout_with_newinode; } @@ -195,7 +196,7 @@ int link(FAR const char *path1, FAR const char *path2) if (INODE_IS_MOUNTPT(target)) { - errcode = EXDEV; + ret = -EXDEV; goto errout_with_newinode; } @@ -216,15 +217,14 @@ int link(FAR const char *path1, FAR const char *path2) inode_unlock(); if (ret < 0) { - errcode = -ret; goto errout_with_newinode; } } /* Hard link successfully created */ - RELEASE_SEARCH(&desc_path1); - RELEASE_SEARCH(&desc_path2); + inode_search_release(&desc_path1); + inode_search_release(&desc_path2); inode_release(target); #ifdef CONFIG_FS_NOTIFY @@ -234,14 +234,14 @@ int link(FAR const char *path1, FAR const char *path2) errout_with_newinode: inode_release(newinode); - RELEASE_SEARCH(&desc_path2); + inode_search_release(&desc_path2); errout_with_target: inode_release(target); errout_with_search_path1: - RELEASE_SEARCH(&desc_path1); + inode_search_release(&desc_path1); errout: - set_errno(errcode); + set_errno(-ret); return ERROR; } diff --git a/fs/vfs/fs_mkdir.c b/fs/vfs/fs_mkdir.c index 6efc790ed112b..8f0d6613f46db 100644 --- a/fs/vfs/fs_mkdir.c +++ b/fs/vfs/fs_mkdir.c @@ -64,28 +64,30 @@ int mkdir(const char *pathname, mode_t mode) { struct inode_search_s desc; FAR struct inode *inode; - int errcode; int ret; mode &= ~getumask(); /* Find the inode that includes this path */ - SETUP_SEARCH(&desc, pathname, false); + ret = inode_search_setup(&desc, pathname, false); + if (ret < 0) + { + goto errout; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret >= 0) { /* An inode was found that includes this path and possibly refers to a * mountpoint. */ - inode = desc.node; DEBUGASSERT(inode != NULL); if (desc.relpath[0] == '\0') { - errcode = EEXIST; + ret = -EEXIST; goto errout_with_inode; } @@ -96,14 +98,13 @@ int mkdir(const char *pathname, mode_t mode) { /* The inode is not a mountpoint */ - errcode = ENXIO; + ret = -ENXIO; goto errout_with_inode; } ret = inode_checkpathperm(inode, 0, 0); if (ret < 0) { - errcode = -ret; goto errout_with_inode; } @@ -116,13 +117,12 @@ int mkdir(const char *pathname, mode_t mode) ret = inode->u.i_mops->mkdir(inode, desc.relpath, mode); if (ret < 0) { - errcode = -ret; goto errout_with_inode; } } else { - errcode = ENOSYS; + ret = -ENOSYS; goto errout_with_inode; } @@ -132,7 +132,7 @@ int mkdir(const char *pathname, mode_t mode) #else /* But mountpoints are not supported in this configuration */ - errcode = EEXIST; + ret = -EEXIST; goto errout_with_inode; #endif } @@ -159,21 +159,20 @@ int mkdir(const char *pathname, mode_t mode) if (ret < 0) { - errcode = -ret; goto errout_with_search; } } #else else { - errcode = ENXIO; + ret = -ENXIO; goto errout_with_search; } #endif /* Directory successfully created */ - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_mkdir(pathname); #endif @@ -183,8 +182,10 @@ int mkdir(const char *pathname, mode_t mode) inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); - set_errno(errcode); + inode_search_release(&desc); + +errout: + set_errno(-ret); return ERROR; } diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c index 35518d77fbfba..d4b13b8d4deba 100644 --- a/fs/vfs/fs_open.c +++ b/fs/vfs/fs_open.c @@ -100,15 +100,19 @@ static int file_vopen(FAR struct file *filep, FAR const char *path, /* Get an inode for this file */ - SETUP_SEARCH(&desc, path, (oflags & O_NOFOLLOW) != 0); + ret = inode_search_setup(&desc, path, (oflags & O_NOFOLLOW) != 0); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { #ifdef CONFIG_PSEUDOFS_FILE if ((oflags & O_CREAT) != 0) { - ret = pseudofile_create(&desc.node, path, mode); + ret = pseudofile_create(&inode, path, mode); } #endif @@ -125,7 +129,6 @@ static int file_vopen(FAR struct file *filep, FAR const char *path, /* Get the search results */ - inode = desc.node; DEBUGASSERT(inode != NULL); #ifdef CONFIG_FS_LINKS @@ -161,7 +164,7 @@ static int file_vopen(FAR struct file *filep, FAR const char *path, /* Release the inode reference */ inode_release(inode); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); /* Get the file structure of the opened character driver proxy */ @@ -264,7 +267,7 @@ static int file_vopen(FAR struct file *filep, FAR const char *path, goto errout_with_inode; } - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_open(path, filep->f_oflags); #endif @@ -275,7 +278,7 @@ static int file_vopen(FAR struct file *filep, FAR const char *path, inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/vfs/fs_readlink.c b/fs/vfs/fs_readlink.c index ad301ec46b394..51f09ad18cc0d 100644 --- a/fs/vfs/fs_readlink.c +++ b/fs/vfs/fs_readlink.c @@ -71,7 +71,6 @@ ssize_t readlink(FAR const char *path, FAR char *buf, size_t bufsize) { struct inode_search_s desc; FAR struct inode *node; - int errcode; int ret; DEBUGASSERT(path != NULL && buf != NULL && bufsize > 0); @@ -80,18 +79,18 @@ ssize_t readlink(FAR const char *path, FAR char *buf, size_t bufsize) * symbolic link node. */ - SETUP_SEARCH(&desc, path, true); + ret = inode_search_setup(&desc, path, true); + if (ret < 0) + { + goto errout; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &node); if (ret < 0) { - errcode = -ret; goto errout_with_search; } - /* Get the search results */ - - node = desc.node; DEBUGASSERT(node != NULL); #ifndef CONFIG_DISABLE_MOUNTPOINT @@ -106,13 +105,12 @@ ssize_t readlink(FAR const char *path, FAR char *buf, size_t bufsize) ret = node->u.i_mops->readlink(node, desc.relpath, buf, bufsize); if (ret < 0) { - errcode = -ret; goto errout_with_inode; } } else { - errcode = ENOSYS; + ret = -ENOSYS; goto errout_with_inode; } } @@ -122,7 +120,6 @@ ssize_t readlink(FAR const char *path, FAR char *buf, size_t bufsize) ret = inode_checkpathperm(node, 0, 0); if (ret < 0) { - errcode = -ret; goto errout_with_inode; } @@ -134,7 +131,7 @@ ssize_t readlink(FAR const char *path, FAR char *buf, size_t bufsize) if (!INODE_IS_SOFTLINK(node)) { - errcode = EINVAL; + ret = -EINVAL; goto errout_with_inode; } @@ -146,15 +143,17 @@ ssize_t readlink(FAR const char *path, FAR char *buf, size_t bufsize) /* Release our reference on the inode and return the length */ inode_release(node); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return strlen(buf); errout_with_inode: inode_release(node); errout_with_search: - RELEASE_SEARCH(&desc); - set_errno(errcode); + inode_search_release(&desc); + +errout: + set_errno(-ret); return ERROR; } diff --git a/fs/vfs/fs_rename.c b/fs/vfs/fs_rename.c index 571320420c078..187a07be7d3ef 100644 --- a/fs/vfs/fs_rename.c +++ b/fs/vfs/fs_rename.c @@ -72,7 +72,8 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, { struct inode_search_s newdesc; struct inode_search_s olddesc; - FAR struct inode *newinode; + FAR struct inode *newinode = NULL; + FAR struct inode *oldfound = NULL; FAR char *subdir = NULL; #ifdef CONFIG_FS_NOTIFY bool isdir = INODE_IS_PSEUDODIR(oldinode); @@ -86,7 +87,11 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, inode_lock(); - SETUP_SEARCH(&newdesc, newpath, true); + ret = inode_search_setup(&newdesc, newpath, true); + if (ret < 0) + { + goto errout_with_lock; + } /* Ancestor X_OK was already checked by rename() via * inode_checkpathperm(oldinode, ...). Still require parent W_OK here @@ -96,20 +101,20 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, ret = inode_checkperm(oldparent, W_OK); if (ret < 0) { - goto errout_with_lock; + goto errout_with_newsearch; } /* According to POSIX, any new inode at this path should be removed * first, provided that it is not a directory. */ - ret = inode_search(&newdesc); + ret = inode_search(&newdesc, &newinode); if (ret >= 0) { /* We found it. Get the search results */ - newinode = newdesc.node; DEBUGASSERT(newinode != NULL); + inode_addref(newinode); /* If the old and new inodes are the same, then this is an attempt to * move the directory entry onto itself. Let's not but say we did. @@ -118,7 +123,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, if (oldinode == newinode) { ret = OK; - goto errout_with_lock; + goto errout_with_newinode; } #ifndef CONFIG_DISABLE_MOUNTPOINT @@ -127,7 +132,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, if (INODE_IS_MOUNTPT(newinode)) { ret = -EXDEV; - goto errout_with_lock; + goto errout_with_newinode; } #endif @@ -154,7 +159,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, { subdir = NULL; ret = -ENOMEM; - goto errout_with_lock; + goto errout_with_newinode; } newpath = subdir; @@ -174,7 +179,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, ret = inode_remove(newpath); if (ret < 0 && ret != -EBUSY) { - goto errout_with_lock; + goto errout_with_newinode; } #ifdef CONFIG_FS_NOTIFY @@ -188,24 +193,40 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, * of zero. */ + inode_release(newinode); + newinode = NULL; + ret = inode_reserve(newpath, 0777, &newinode); if (ret < 0) { - goto errout_with_lock; + goto errout_with_newsearch; } /* Re-resolve the source under the same lock before unlinking it. */ - SETUP_SEARCH(&olddesc, oldpath, true); - ret = inode_search(&olddesc); - RELEASE_SEARCH(&olddesc); - if (ret < 0 || olddesc.node != oldinode) + ret = inode_search_setup(&olddesc, oldpath, true); + if (ret < 0) { + goto errout_with_newsearch; + } + + ret = inode_search(&olddesc, &oldfound); + inode_search_release(&olddesc); + if (ret >= 0) + { + inode_addref(oldfound); + } + + if (ret < 0 || oldfound != oldinode) + { + inode_release(oldfound); inode_remove(newpath); ret = -ENOENT; - goto errout_with_lock; + goto errout_with_newsearch; } + inode_release(oldfound); + /* Copy the inode state from the old inode to the newly allocated inode */ newinode->i_child = oldinode->i_child; /* Link to lower level inode */ @@ -248,7 +269,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, /* Remove the new node we just recreated */ inode_remove(newpath); - goto errout_with_lock; + goto errout_with_newsearch; } /* Remove all of the children from the unlinked inode */ @@ -256,9 +277,15 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode, oldinode->i_child = NULL; oldinode->i_parent = NULL; ret = OK; + goto errout_with_newsearch; + +errout_with_newinode: + inode_release(newinode); + +errout_with_newsearch: + inode_search_release(&newdesc); errout_with_lock: - RELEASE_SEARCH(&newdesc); inode_unlock(); #ifdef CONFIG_FS_NOTIFY @@ -315,8 +342,13 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, * mountpoint */ - SETUP_SEARCH(&newdesc, newpath, true); - ret = inode_find(&newdesc); + ret = inode_search_setup(&newdesc, newpath, true); + if (ret < 0) + { + return ret; + } + + ret = inode_find(&newdesc, &newinode); if (ret < 0) { /* There is no mountpoint that includes in this path */ @@ -326,7 +358,6 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, /* Get the search results */ - newinode = newdesc.node; newrelpath = newdesc.relpath; DEBUGASSERT(newinode != NULL && newrelpath != NULL); @@ -512,7 +543,7 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, inode_release(newinode); errout_with_newsearch: - RELEASE_SEARCH(&newdesc); + inode_search_release(&newdesc); if (subdir != NULL) { fs_heap_free(subdir); @@ -553,8 +584,13 @@ int rename(FAR const char *oldpath, FAR const char *newpath) /* Get an inode that includes the oldpath */ - SETUP_SEARCH(&olddesc, oldpath, true); - ret = inode_find(&olddesc); + ret = inode_search_setup(&olddesc, oldpath, true); + if (ret < 0) + { + goto errout; + } + + ret = inode_find(&olddesc, &oldinode); if (ret < 0) { /* There is no inode that includes in this path */ @@ -562,9 +598,6 @@ int rename(FAR const char *oldpath, FAR const char *newpath) goto errout_with_oldsearch; } - /* Get the search results */ - - oldinode = olddesc.node; DEBUGASSERT(oldinode != NULL); ret = inode_checkpathperm(oldinode, 0, 0); @@ -594,7 +627,7 @@ int rename(FAR const char *oldpath, FAR const char *newpath) inode_release(oldinode); errout_with_oldsearch: - RELEASE_SEARCH(&olddesc); + inode_search_release(&olddesc); errout: if (ret < 0) diff --git a/fs/vfs/fs_rmdir.c b/fs/vfs/fs_rmdir.c index f2911085d531e..bcf4dab1a9f40 100644 --- a/fs/vfs/fs_rmdir.c +++ b/fs/vfs/fs_rmdir.c @@ -62,7 +62,6 @@ int rmdir(FAR const char *pathname) { struct inode_search_s desc; FAR struct inode *inode; - int errcode; int ret; /* Get an inode for the directory (or for the mountpoint containing the @@ -70,20 +69,20 @@ int rmdir(FAR const char *pathname) * on the inode if one is found. */ - SETUP_SEARCH(&desc, pathname, true); + ret = inode_search_setup(&desc, pathname, true); + if (ret < 0) + { + goto errout; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { /* There is no inode that includes in this path */ - errcode = -ret; goto errout_with_search; } - /* Get the search results */ - - inode = desc.node; DEBUGASSERT(inode != NULL); #ifndef CONFIG_DISABLE_MOUNTPOINT @@ -94,7 +93,6 @@ int rmdir(FAR const char *pathname) ret = inode_checkpathperm(inode, 0, 0); if (ret < 0) { - errcode = -ret; goto errout_with_inode; } @@ -107,13 +105,12 @@ int rmdir(FAR const char *pathname) ret = inode->u.i_mops->rmdir(inode, desc.relpath); if (ret < 0) { - errcode = -ret; goto errout_with_inode; } } else { - errcode = ENOSYS; + ret = -ENOSYS; goto errout_with_inode; } } @@ -133,7 +130,7 @@ int rmdir(FAR const char *pathname) if (inode->i_child) { - errcode = ENOTEMPTY; + ret = -ENOTEMPTY; goto errout_with_inode; } @@ -149,26 +146,25 @@ int rmdir(FAR const char *pathname) if (ret < 0 && ret != -EBUSY) { - errcode = -ret; goto errout_with_inode; } } else { - errcode = ENOTDIR; + ret = -ENOTDIR; goto errout_with_inode; } #else - { - errcode = ENXIO; - goto errout_with_inode; - } + { + ret = -ENXIO; + goto errout_with_inode; + } #endif /* Successfully removed the directory */ inode_release(inode); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_unlink(pathname); #endif @@ -177,8 +173,9 @@ int rmdir(FAR const char *pathname) errout_with_inode: inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); - set_errno(errcode); + inode_search_release(&desc); +errout: + set_errno(-ret); return ERROR; } diff --git a/fs/vfs/fs_stat.c b/fs/vfs/fs_stat.c index 9eefb401e5edf..f5b7586a341cd 100644 --- a/fs/vfs/fs_stat.c +++ b/fs/vfs/fs_stat.c @@ -88,9 +88,13 @@ static int stat_recursive(FAR const char *path, /* Get an inode for this path */ - SETUP_SEARCH(&desc, path, true); + ret = inode_search_setup(&desc, path, true); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { /* This name does not refer to an inode in the pseudo file system and @@ -102,7 +106,6 @@ static int stat_recursive(FAR const char *path, /* Get the search results */ - inode = desc.node; DEBUGASSERT(inode != NULL); ret = inode_checkpathperm(inode, 0, 0); @@ -153,7 +156,7 @@ static int stat_recursive(FAR const char *path, inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/fs/vfs/fs_statfs.c b/fs/vfs/fs_statfs.c index 70d796c3b5f8b..212ee783a9aba 100644 --- a/fs/vfs/fs_statfs.c +++ b/fs/vfs/fs_statfs.c @@ -93,9 +93,13 @@ int statfs(FAR const char *path, FAR struct statfs *buf) /* Get an inode for this file */ - SETUP_SEARCH(&desc, path, false); + ret = inode_search_setup(&desc, path, false); + if (ret < 0) + { + goto errout; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { /* This name does not refer to a psudeo-inode and there is no @@ -107,7 +111,6 @@ int statfs(FAR const char *path, FAR struct statfs *buf) /* Get the search results */ - inode = desc.node; DEBUGASSERT(inode != NULL); ret = inode_checkpathperm(inode, 0, 0); @@ -154,7 +157,7 @@ int statfs(FAR const char *path, FAR struct statfs *buf) /* Successfully statfs'ed the file */ inode_release(inode); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return OK; /* Failure conditions always set the errno appropriately */ @@ -163,7 +166,7 @@ int statfs(FAR const char *path, FAR struct statfs *buf) inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); errout: set_errno(-ret); diff --git a/fs/vfs/fs_symlink.c b/fs/vfs/fs_symlink.c index 76cac2bad7371..5ced61110e0ec 100644 --- a/fs/vfs/fs_symlink.c +++ b/fs/vfs/fs_symlink.c @@ -81,12 +81,11 @@ int symlink(FAR const char *path1, FAR const char *path2) { struct inode_search_s desc; FAR struct inode *inode = NULL; - int errcode; int ret; if (path1 == NULL) { - errcode = EINVAL; + ret = -EINVAL; goto errout; } @@ -94,9 +93,13 @@ int symlink(FAR const char *path1, FAR const char *path2) * 'path2' does not lie on a mounted volume. */ - SETUP_SEARCH(&desc, path2, false); + ret = inode_search_setup(&desc, path2, false); + if (ret < 0) + { + goto errout; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret >= 0) { /* Something exists at the path2 where we are trying to create the @@ -106,16 +109,15 @@ int symlink(FAR const char *path1, FAR const char *path2) #ifndef CONFIG_DISABLE_MOUNTPOINT /* Check if the inode is a mountpoint. */ - DEBUGASSERT(desc.node != NULL); - if (INODE_IS_MOUNTPT(desc.node)) + DEBUGASSERT(inode != NULL); + if (INODE_IS_MOUNTPT(inode)) { - if (desc.node->u.i_mops && desc.node->u.i_mops->symlink) + if (inode->u.i_mops && inode->u.i_mops->symlink) { - ret = desc.node->u.i_mops->symlink(desc.node, path1, - desc.relpath); + ret = inode->u.i_mops->symlink(inode, path1, + desc.relpath); if (ret < 0) { - errcode = -ret; goto errout_with_inode; } } @@ -123,7 +125,7 @@ int symlink(FAR const char *path1, FAR const char *path2) { /* Symbolic links within this type of fs are not supported */ - errcode = ENOSYS; + ret = -ENOSYS; goto errout_with_inode; } } @@ -132,7 +134,7 @@ int symlink(FAR const char *path1, FAR const char *path2) { /* A node already exists in the pseudofs at 'path1' */ - errcode = EEXIST; + ret = -EEXIST; goto errout_with_inode; } } @@ -149,7 +151,7 @@ int symlink(FAR const char *path1, FAR const char *path2) if (newpath2 == NULL) { - errcode = ENOMEM; + ret = -ENOMEM; goto errout_with_search; } @@ -173,14 +175,13 @@ int symlink(FAR const char *path1, FAR const char *path2) if (ret < 0) { fs_heap_free(newpath2); - errcode = -ret; goto errout_with_search; } } /* Symbolic link successfully created */ - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_create(path2); #endif @@ -190,10 +191,10 @@ int symlink(FAR const char *path1, FAR const char *path2) inode_release(inode); errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); errout: - set_errno(errcode); + set_errno(-ret); return ERROR; } diff --git a/fs/vfs/fs_unlink.c b/fs/vfs/fs_unlink.c index 9d7be5dc545c6..eb890f43a2c3c 100644 --- a/fs/vfs/fs_unlink.c +++ b/fs/vfs/fs_unlink.c @@ -68,9 +68,13 @@ int nx_unlink(FAR const char *pathname) * which may be a symbolic link) */ - SETUP_SEARCH(&desc, pathname, true); + ret = inode_search_setup(&desc, pathname, true); + if (ret < 0) + { + return ret; + } - ret = inode_find(&desc); + ret = inode_find(&desc, &inode); if (ret < 0) { /* There is no inode that includes in this path */ @@ -78,9 +82,6 @@ int nx_unlink(FAR const char *pathname) goto errout_with_search; } - /* Get the search results */ - - inode = desc.node; DEBUGASSERT(inode != NULL); #ifndef CONFIG_DISABLE_MOUNTPOINT @@ -193,7 +194,7 @@ int nx_unlink(FAR const char *pathname) /* Successfully unlinked */ inode_release(inode); - RELEASE_SEARCH(&desc); + inode_search_release(&desc); #ifdef CONFIG_FS_NOTIFY notify_unlink(pathname); #endif @@ -205,7 +206,7 @@ int nx_unlink(FAR const char *pathname) #endif errout_with_search: - RELEASE_SEARCH(&desc); + inode_search_release(&desc); return ret; } diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h index a053490662162..3e7e26f3c94b9 100644 --- a/include/nuttx/sched.h +++ b/include/nuttx/sched.h @@ -558,6 +558,12 @@ struct task_group_s struct fdlist tg_fdlist; /* Maps file descriptor to file */ +#ifdef CONFIG_FS_CHROOT + /* chroot() jail **********************************************************/ + + FAR char *tg_root; /* Absolute jail path, NULL = no jail */ +#endif + /* Virtual memory mapping info ********************************************/ struct mm_map_s tg_mm_map; /* Task group virtual memory mappings */ diff --git a/include/unistd.h b/include/unistd.h index e46dda9d3cb80..c719ff9f952ec 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -428,6 +428,9 @@ unsigned int alarm(unsigned int seconds); int chdir(FAR const char *path); int fchdir(int fd); +#ifdef CONFIG_FS_CHROOT +int chroot(FAR const char *path); +#endif FAR char *getcwd(FAR char *buf, size_t size); FAR char *get_current_dir_name(void); diff --git a/sched/group/group_create.c b/sched/group/group_create.c index 1fb32bf7ff218..ed23dfc966b3f 100644 --- a/sched/group/group_create.c +++ b/sched/group/group_create.c @@ -33,7 +33,6 @@ #include #include -#include #include #include #include @@ -42,6 +41,10 @@ #include "group/group.h" #include "tls/tls.h" +#ifdef CONFIG_FS_CHROOT +# include "../../fs/fs_heap.h" +#endif + /**************************************************************************** * Private Data ****************************************************************************/ @@ -98,6 +101,57 @@ static inline void group_inherit_identity(FAR struct task_group_s *group) # define group_inherit_identity(group) #endif +#ifdef CONFIG_FS_CHROOT +/**************************************************************************** + * Name: group_inherit_chroot + * + * Description: + * Inherit the chroot jail from the parent task group. Kernel threads + * share g_kthread_group and must not inherit a user jail. + * CONFIG_FS_CHROOT is selected in fs/Kconfig. + * + * Input Parameters: + * group - The new task group. + * ttype - The type of the new thread (TCB_FLAG_TTYPE_* value). + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int group_inherit_chroot(FAR struct task_group_s *group, + uint8_t ttype) +{ + FAR struct tcb_s *rtcb; + FAR struct task_group_s *rgroup; + + if (ttype == TCB_FLAG_TTYPE_KERNEL) + { + return OK; + } + + rtcb = this_task(); + rgroup = rtcb->group; + + DEBUGASSERT(group != NULL && rgroup != NULL); + + if (rgroup->tg_root == NULL) + { + return OK; + } + + group->tg_root = fs_heap_strdup(rgroup->tg_root); + if (group->tg_root == NULL) + { + return -ENOMEM; + } + + return OK; +} +#else +# define group_inherit_chroot(group, ttype) (0) +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -190,6 +244,12 @@ int group_allocate(FAR struct tcb_s *tcb, uint8_t ttype) group_inherit_identity(group); + ret = group_inherit_chroot(group, ttype); + if (ret < 0) + { + goto errout_with_group; + } + /* Initialize file descriptors for the TCB */ fdlist_init(&group->tg_fdlist); @@ -219,6 +279,14 @@ int group_allocate(FAR struct tcb_s *tcb, uint8_t ttype) return OK; errout_with_group: +#ifdef CONFIG_FS_CHROOT + if (group->tg_root != NULL) + { + fs_heap_free(group->tg_root); + group->tg_root = NULL; + } +#endif + kmm_free(group); return ret; } diff --git a/sched/group/group_leave.c b/sched/group/group_leave.c index afe62da8c0735..cd7a9e3b5aa1d 100644 --- a/sched/group/group_leave.c +++ b/sched/group/group_leave.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,10 @@ # include #endif +#ifdef CONFIG_FS_CHROOT +# include "../../fs/fs_heap.h" +#endif + #include "environ/environ.h" #include "signal/signal.h" #include "pthread/pthread.h" @@ -104,6 +109,16 @@ static inline void group_release(FAR struct task_group_s *group) fdlist_free(&group->tg_fdlist); +#ifdef CONFIG_FS_CHROOT + /* Drop the chroot jail path */ + + if (group->tg_root != NULL) + { + fs_heap_free(group->tg_root); + group->tg_root = NULL; + } +#endif + /* Release all shared environment variables */ env_release(group); diff --git a/syscall/syscall.csv b/syscall/syscall.csv index 439630d71305c..1b93dcc4d2df9 100644 --- a/syscall/syscall.csv +++ b/syscall/syscall.csv @@ -10,6 +10,7 @@ "boardctl","sys/boardctl.h","defined(CONFIG_BOARDCTL)","int","unsigned int","uintptr_t" "chmod","sys/stat.h","","int","FAR const char *","mode_t" "chown","unistd.h","","int","FAR const char *","uid_t","gid_t" +"chroot","unistd.h","defined(CONFIG_FS_CHROOT)","int","FAR const char *" "clearenv","stdlib.h","!defined(CONFIG_DISABLE_ENVIRON)","int" "clock","time.h","","clock_t" "clock_adjtime","sys/timex.h","defined(CONFIG_CLOCK_ADJTIME)","int","clockid_t","struct timex *"