| git.druid.rocks | index | druid520 | q | src/ | fstree.c |
src/fstree.c
#include "config.h"
#ifdef OS_NETBSD_C
#define _NETBSD_SOURCE 1
#endif
#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include "q.h"
/* walk a prefix, recording only WHICH paths exist (a plain presence
* list) -- the manifest diff only needs presence, so there's no type,
* mtime, symlink-target, or device bookkeeping. appends into an
* in-memory snap rather than a stream. */
void
walktree(struct snap* s, const char* base, const char* rel)
{
DIR* d;
struct dirent* e;
char full[MAXPATH];
char nrel[MAXPATH];
struct stat st;
if(pathjoin(full, sizeof(full), base, rel) == 0)
{
die("path too long");
return;
}
d = opendir(full);
if(d == NULL)
{
return;
}
e = readdir(d);
while(e != NULL)
{
if(strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0)
{
if(rel[0] != '\0')
{
if(pathjoin(nrel, sizeof(nrel), rel, e->d_name) == 0)
{
die("path too long");
return;
}
}
else
{
size_t dn = strlen(e->d_name);
if(dn > sizeof(nrel) - 1)
{
die("path too long");
return;
}
memcpy(nrel, e->d_name, dn);
nrel[dn] = '\0';
}
if(pathjoin(full, sizeof(full), base, nrel) == 0)
{
die("path too long");
return;
}
if(lstat(full, &st) == 0)
{
snap_add(s, nrel);
/* descend only real directories, never symlinks -- lstat
* reports the link itself, so S_ISDIR is false for a
* symlink-to-directory and the walk can't loop. */
if(S_ISDIR(st.st_mode))
{
walktree(s, base, nrel);
}
}
}
e = readdir(d);
}
closedir(d);
}
/* best-effort recursive remove: a leftover file from a too-long path is
* a small harm next to blocking the whole removal. falls back to unlink
* when the path isn't a directory. */
void
rmtree(const char* path)
{
DIR* d;
struct dirent* e;
char sub[MAXPATH];
struct stat st;
d = opendir(path);
if(d == NULL)
{
unlink(path);
return;
}
e = readdir(d);
while(e != NULL)
{
if(strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0
&& pathjoin(sub, sizeof(sub), path, e->d_name) != 0)
{
if(lstat(sub, &st) == 0)
{
if(S_ISDIR(st.st_mode))
{
rmtree(sub);
}
else
{
unlink(sub);
}
}
}
e = readdir(d);
}
closedir(d);
rmdir(path);
}
/* mkdir -p, leaf included: creates every component of path (itself a
* directory, not a file whose ancestors need making). EEXIST is the
* common case (an ancestor already there); anything else is fatal. */
void
makedirs(const char* path)
{
char buf[MAXPATH];
size_t n = strlen(path);
size_t i;
if(n >= sizeof(buf))
{
die("path too long");
return;
}
memcpy(buf, path, n + 1);
for(i = 1; i < n; i = i + 1)
{
if(buf[i] == '/')
{
buf[i] = '\0';
if(mkdir(buf, 0755) != 0 && errno != EEXIST)
{
die("failed creating directory");
return;
}
buf[i] = '/';
}
}
if(mkdir(buf, 0755) != 0 && errno != EEXIST)
{
die("failed creating directory");
return;
}
}