DavHau / nix-portable

Nix - Static, Permissionless, Installation-free, Pre-configured
MIT License
788 stars 29 forks source link

rm: cannot remove '.nix-portable/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-something/foo/bar': Permission denied #33

Open ShamrockLee opened 2 years ago

ShamrockLee commented 2 years ago

Reproduction step:

  1. Set NP_LOCATION on ram-based file system.
    NP_LOCATION="$TMPDIR" nix-portable bla bla bla
  2. rm -rf .nix-portable
  3. See many rm cannot remove '.nix-portable/store/...': permission denied error.
rrbutani commented 2 years ago

@ShamrockLee Does running chmod -R 777 .nix-portable and then running rm -rf .nix-portable work for you?

Some directories in the nix store do not have the writable bit set; rm -f will delete "read only" files that are owned by your user but will not delete "read only" directories (afaik), hence the error. Because the folders are owned by your user you have permissions to run chmod.

ShamrockLee commented 2 years ago

@rrbutani It works! Thanks a lot!

It would be helpful to document this as a "way to clean-up" and a workaround of #25.

milahu commented 1 month ago

maybe run chmod -R +w after every extraction or build

doing chmod -R +w too early in the build process would break CA derivations

related nix sources ...

src/libutil/tarfile.cc ```cc static void extract_archive(TarArchive & archive, const Path & destDir) { int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_SECURE_NODOTDOT; for (;;) { struct archive_entry * entry; int r = archive_read_next_header(archive.archive, &entry); if (r == ARCHIVE_EOF) break; auto name = archive_entry_pathname(entry); if (!name) throw Error("cannot get archive member name: %s", archive_error_string(archive.archive)); if (r == ARCHIVE_WARN) warn(archive_error_string(archive.archive)); else archive.check(r); archive_entry_copy_pathname(entry, (destDir + "/" + name).c_str()); // sources can and do contain dirs with no rx bits if (archive_entry_filetype(entry) == AE_IFDIR && (archive_entry_mode(entry) & 0500) != 0500) archive_entry_set_mode(entry, archive_entry_mode(entry) | 0500); // Patch hardlink path const char * original_hardlink = archive_entry_hardlink(entry); if (original_hardlink) { archive_entry_copy_hardlink(entry, (destDir + "/" + original_hardlink).c_str()); } archive.check(archive_read_extract(archive.archive, entry, flags)); } archive.close(); } ``` src/libstore/posix-fs-canonicalise.cc ``` static void canonicaliseTimestampAndPermissions(const Path & path, const struct stat & st) { if (!S_ISLNK(st.st_mode)) { /* Mask out all type related bits. */ mode_t mode = st.st_mode & ~S_IFMT; if (mode != 0444 && mode != 0555) { mode = (st.st_mode & S_IFMT) | 0444 | (st.st_mode & S_IXUSR ? 0111 : 0); if (chmod(path.c_str(), mode) == -1) throw SysError("changing mode of '%1%' to %2$o", path, mode); } } ``` src/libstore/local-store.cc ```cc /* To improve purity, users may want to make the Nix store a read-only bind mount. So make the Nix store writable for this process. */ void LocalStore::makeStoreWritable() { #if __linux__ if (!isRootUser()) return; /* Check if /nix/store is on a read-only mount. */ struct statvfs stat; if (statvfs(realStoreDir.get().c_str(), &stat) != 0) throw SysError("getting info about the Nix store mount point"); if (stat.f_flag & ST_RDONLY) { if (mount(0, realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1) throw SysError("remounting %1% writable", realStoreDir); } #endif } ``` doc/manual/src/language/derivations.md ``` - After the build, Nix sets the last-modified timestamp on all files in the build result to 1 (00:00:01 1/1/1970 UTC), sets the group to the default group, and sets the mode of the file to 0444 or 0555 (i.e., read-only, with execute permission enabled if the file was originally executable). ```