Merge pull request #3541 from alyssais/gcdos
Fix long paths permanently breaking GC
This commit is contained in:
commit
ee754f0f41
1 changed files with 43 additions and 11 deletions
|
@ -268,16 +268,13 @@ bool isLink(const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DirEntries readDirectory(const Path & path)
|
DirEntries readDirectory(DIR *dir, const Path & path)
|
||||||
{
|
{
|
||||||
DirEntries entries;
|
DirEntries entries;
|
||||||
entries.reserve(64);
|
entries.reserve(64);
|
||||||
|
|
||||||
AutoCloseDir dir(opendir(path.c_str()));
|
|
||||||
if (!dir) throw SysError(format("opening directory '%1%'") % path);
|
|
||||||
|
|
||||||
struct dirent * dirent;
|
struct dirent * dirent;
|
||||||
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
|
while (errno = 0, dirent = readdir(dir)) { /* sic */
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
string name = dirent->d_name;
|
string name = dirent->d_name;
|
||||||
if (name == "." || name == "..") continue;
|
if (name == "." || name == "..") continue;
|
||||||
|
@ -294,6 +291,14 @@ DirEntries readDirectory(const Path & path)
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DirEntries readDirectory(const Path & path)
|
||||||
|
{
|
||||||
|
AutoCloseDir dir(opendir(path.c_str()));
|
||||||
|
if (!dir) throw SysError(format("opening directory '%1%'") % path);
|
||||||
|
|
||||||
|
return readDirectory(dir.get(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned char getFileType(const Path & path)
|
unsigned char getFileType(const Path & path)
|
||||||
{
|
{
|
||||||
|
@ -389,12 +394,14 @@ void writeLine(int fd, string s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _deletePath(const Path & path, unsigned long long & bytesFreed)
|
static void _deletePath(int parentfd, const Path & path, unsigned long long & bytesFreed)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
|
string name(baseNameOf(path));
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st) == -1) {
|
if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||||
if (errno == ENOENT) return;
|
if (errno == ENOENT) return;
|
||||||
throw SysError(format("getting status of '%1%'") % path);
|
throw SysError(format("getting status of '%1%'") % path);
|
||||||
}
|
}
|
||||||
|
@ -406,20 +413,45 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
|
||||||
/* Make the directory accessible. */
|
/* Make the directory accessible. */
|
||||||
const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR;
|
const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR;
|
||||||
if ((st.st_mode & PERM_MASK) != PERM_MASK) {
|
if ((st.st_mode & PERM_MASK) != PERM_MASK) {
|
||||||
if (chmod(path.c_str(), st.st_mode | PERM_MASK) == -1)
|
if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1)
|
||||||
throw SysError(format("chmod '%1%'") % path);
|
throw SysError(format("chmod '%1%'") % path);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & i : readDirectory(path))
|
int fd = openat(parentfd, path.c_str(), O_RDONLY);
|
||||||
_deletePath(path + "/" + i.name, bytesFreed);
|
if (!fd)
|
||||||
|
throw SysError(format("opening directory '%1%'") % path);
|
||||||
|
AutoCloseDir dir(fdopendir(fd));
|
||||||
|
if (!dir)
|
||||||
|
throw SysError(format("opening directory '%1%'") % path);
|
||||||
|
for (auto & i : readDirectory(dir.get(), path))
|
||||||
|
_deletePath(dirfd(dir.get()), path + "/" + i.name, bytesFreed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remove(path.c_str()) == -1) {
|
int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0;
|
||||||
|
if (unlinkat(parentfd, name.c_str(), flags) == -1) {
|
||||||
if (errno == ENOENT) return;
|
if (errno == ENOENT) return;
|
||||||
throw SysError(format("cannot unlink '%1%'") % path);
|
throw SysError(format("cannot unlink '%1%'") % path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _deletePath(const Path & path, unsigned long long & bytesFreed)
|
||||||
|
{
|
||||||
|
Path dir = dirOf(path);
|
||||||
|
if (dir == "")
|
||||||
|
dir = "/";
|
||||||
|
|
||||||
|
AutoCloseFD dirfd(open(dir.c_str(), O_RDONLY));
|
||||||
|
if (!dirfd) {
|
||||||
|
// This really shouldn't fail silently, but it's left this way
|
||||||
|
// for backwards compatibility.
|
||||||
|
if (errno == ENOENT) return;
|
||||||
|
|
||||||
|
throw SysError(format("opening directory '%1%'") % path);
|
||||||
|
}
|
||||||
|
|
||||||
|
_deletePath(dirfd.get(), path, bytesFreed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void deletePath(const Path & path)
|
void deletePath(const Path & path)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue