#include "attr.hpp"
#include <fcntl.h>
bool operator<(const struct timespec& a, const struct timespec& b) {
if (!a.tv_nsec || !b.tv_nsec) return a.tv_sec < b.tv_sec; // e.g. sshfs does not provide nanoseconds
return a.tv_sec < b.tv_sec || (a.tv_sec == b.tv_sec && a.tv_nsec < b.tv_nsec);
}
bool operator>(const struct timespec& a, const struct timespec& b) {
if (!a.tv_nsec || !b.tv_nsec) return a.tv_sec > b.tv_sec;
return a.tv_sec > b.tv_sec || (a.tv_sec == b.tv_sec && a.tv_nsec > b.tv_nsec);
}
bool operator==(const struct timespec& a, const struct timespec& b) {
if (!a.tv_nsec || !b.tv_nsec) return a.tv_sec == b.tv_sec;
return a.tv_sec == b.tv_sec && a.tv_nsec == b.tv_nsec;
}
bool operator!=(const struct timespec& a, const struct timespec& b) {
if (!a.tv_nsec || !b.tv_nsec) return a.tv_sec != b.tv_sec;
return a.tv_sec != b.tv_sec || a.tv_nsec != b.tv_nsec;
}
Attr* Attr::getInst(Attr* inst) {
if (!inst->is_reg() && !inst->is_dir()) {
LOG("no dir/reg");
delete inst;
return NULL;
}
#if 0
// for localizing on remote machine with getpwnam/getgrnam
struct passwd* pw = getpwuid(inst->ss.st_uid);
struct group* gr = getgrgid(inst->ss.st_gid);
if (!pw || !gr) {
LOG("getpwuid(%d)/getgrgid(%d)", inst->ss.st_uid, inst->ss.st_gid);
delete inst;
return NULL;
}
inst->uname = strdup(pw->pw_name);
inst->gname = strdup(gr->gr_name);
#endif
return inst;
}
Attr* Attr::getInst(const struct stat& s) {
Attr* inst = new Attr();
inst->ss = s;
return getInst(inst);
}
Attr* Attr::getInst(int fd) {
Attr* inst = new Attr();
if (fstat(fd, &inst->ss) != 0) {
LOG_ERRNO("fstat(%d)", fd);
delete inst;
return NULL;
}
return getInst(inst);
}
Attr* Attr::getInst(int dfd, const char* fn) {
Attr* inst = new Attr();
if (fstatat(dfd, fn, &inst->ss, AT_SYMLINK_NOFOLLOW) != 0) {
LOG_ERRNO("fstatat(%d,%s)", dfd, fn);
delete inst;
return NULL;
}
return getInst(inst);
}
bool Attr::apply_to(int fd) const {
if (fchmod(fd, ss.st_mode) == -1) { // what first? chmod/chown?
LOG_ERRNO("fchmod(%d)", fd);
return false;
}
// TODO: translate string user/group to local ids if possible
//if (fchown(fd, ss.st_uid, ss.st_gid) == -1) {
// LOG_ERRNO("fchown(%d)", fd);
// return false;
//}
if (futimens(fd, &ss.st_atim) == -1) { // actually timespec[2] for atime and mtime - cannot change ctime (will be now as we set the mtime)
LOG_ERRNO("futimens(%d)", fd);
return false;
}
return true;
}
Attr::merge_t Attr::cmp(const Attr* o) const {
if (is_reg() != o->is_reg()) {
return FAIL;
}
if (is_reg()) { // ignore mtime for dirs
if (mtime() > o->mtime()) {
if (size() != o->size()) {
return FULL_THIS; // assume fully newer in any case
} else {
return FULL_OR_ATTR_THIS; // might be a copy that only requires updating attrs
}
} else if (mtime() < o->mtime()) {
if (size() != o->size()) {
return FULL_THAT; // assume fully older in any case
} else {
return FULL_OR_ATTR_THAT;
}
} else if (size() != o->size()) {
return FULL_CONFLICT; // same name, type, mtime but different size?
} else {
// file has same mod time and size - can now only be different attrs
}
}
if (mode() == o->mode() && uid() == o->uid() && gid() == o->gid()) {
return SAME; // ignore ctime here? might verify this using hashes?
} else {
if (ctime() > o->ctime() && (mtime() == o->mtime() || mtime() > o->mtime())) { // ctime will be updated as when when mtime changes, but we check mtime here as well for safety
return ATTR_THIS;
} else if (ctime() < o->ctime() && (mtime() == o->mtime() || mtime() < o->mtime())) {
return ATTR_THAT;
} else {
return ATTR_CONFLICT;
}
}
}