TODO: Test this with non-AT_FDCWD values.
| 811 | |
| 812 | // TODO: Test this with non-AT_FDCWD values. |
| 813 | int __syscall_unlinkat(int dirfd, const char* path, int flags) { |
| 814 | if (flags & ~AT_REMOVEDIR) { |
| 815 | // TODO: Test this case. |
| 816 | return -EINVAL; |
| 817 | } |
| 818 | // It is invalid for rmdir paths to end in ".", but we need to distinguish |
| 819 | // this case from the case of `parseParent` returning (root, '.') when parsing |
| 820 | // "/", so we need to find the invalid "/." manually. |
| 821 | if (flags == AT_REMOVEDIR) { |
| 822 | std::string_view p(path); |
| 823 | // Ignore trailing '/'. |
| 824 | while (!p.empty() && p.back() == '/') { |
| 825 | p.remove_suffix(1); |
| 826 | } |
| 827 | if (p.size() >= 2 && p.substr(p.size() - 2) == std::string_view("/.")) { |
| 828 | return -EINVAL; |
| 829 | } |
| 830 | } |
| 831 | auto parsed = path::parseParent(path, dirfd); |
| 832 | if (auto err = parsed.getError()) { |
| 833 | return err; |
| 834 | } |
| 835 | auto& [parent, childNameView] = parsed.getParentChild(); |
| 836 | std::string childName(childNameView); |
| 837 | auto lockedParent = parent->locked(); |
| 838 | auto file = lockedParent.getChild(childName); |
| 839 | if (!file) { |
| 840 | return -ENOENT; |
| 841 | } |
| 842 | // Disallow removing the root directory, even if it is empty. |
| 843 | if (file == wasmFS.getRootDirectory()) { |
| 844 | return -EBUSY; |
| 845 | } |
| 846 | |
| 847 | auto lockedFile = file->locked(); |
| 848 | if (auto dir = file->dynCast<Directory>()) { |
| 849 | if (flags != AT_REMOVEDIR) { |
| 850 | return -EISDIR; |
| 851 | } |
| 852 | // A directory can only be removed if it has no entries. |
| 853 | if (dir->locked().getNumEntries() > 0) { |
| 854 | return -ENOTEMPTY; |
| 855 | } |
| 856 | } else { |
| 857 | // A normal file or symlink. |
| 858 | if (flags == AT_REMOVEDIR) { |
| 859 | return -ENOTDIR; |
| 860 | } |
| 861 | } |
| 862 | |
| 863 | // Cannot unlink/rmdir if the parent dir doesn't have write permissions. |
| 864 | if (!(lockedParent.getMode() & WASMFS_PERM_WRITE)) { |
| 865 | return -EACCES; |
| 866 | } |
| 867 | |
| 868 | // Input is valid, perform the unlink. |
| 869 | return lockedParent.removeChild(childName); |
| 870 | } |
no test coverage detected