this->dirtyUsers_ = false;
this->dirtyGroups_ = false;
- vfsReload();
+ vfsLoad();
}
-void VfsAuthn::vfsReload() throw (DmException) {
- std::string entrypp;
- std::ifstream *f;
- const char *entry;
- unsigned int uid, gid, banned;
- int pos, ret, n;
+static void csv_users_load_entry(void *ctx, const Extensible& values) {
+ std::vector<UserInfo> *uis = (std::vector<UserInfo> *)ctx;
UserInfo ui;
+
+ ui.name = values.getString("name");
+ ui.copy(values);
+ ui.erase("name");
+ uis->push_back(ui);
+}
+
+
+
+static void csv_groups_load_entry(void *ctx, const Extensible& values) {
+ std::vector<GroupInfo> *gis = (std::vector<GroupInfo> *)ctx;
GroupInfo gi;
+ gi.name = values.getString("name");
+ gi.copy(values);
+ gi.erase("name");
+ gis->push_back(gi);
+}
+
+
+
+void VfsAuthn::vfsLoad() throw (DmException) {
+ // users
this->users_.clear();
this->uids_.clear();
- f = new std::ifstream((this->prefix_ + VFS_USERS_FILE).c_str());
- if (f->is_open()) {
- n = 0;
- while (std::getline(*f, entrypp)) {
- n++;
- entry = entrypp.c_str();
- ret = sscanf(entry, "%u%*[ \t]%u%*[ \t]%n", &uid, &banned, &pos);
- if (ret != 2) {
- f->close();
- delete f;
- vfsThrow(ret == EOF ? DMLITE_SYSERR(EINVAL) : errno, "parsing users file failed at line %d (%s)", n, entry);
- }
- //printf("entry: %u %u '%s'\n", uid, banned, entry + pos);
-
- ui.clear();
- ui.name = entry + pos;
- ui["uid"] = uid;
- ui["banned"] = banned ? 1 : 0;
- this->users_.push_back(ui);
- // no check - we're OK with multiple users on the same uid
- this->uids_.insert(uid);
+ try {
+ csvLoad(VFS_USERS_FILE, (void *)&this->users_, this->userFields_, csv_users_load_entry);
+ } catch (DmException& e) {
+ switch (e.code()) {
+ case DMLITE_SYSERR(EIO):
+ break;
+ default:
+ vfsThrow(e.code(), std::string("error parsing users file ") + e.what());
+ break;
}
- f->close();
}
- delete f;
+ for (size_t i = 0; i < this->users_.size(); i++)
+ this->uids_.insert(this->users_[i].getUnsigned("uid"));
+ // groups
this->groups_.clear();
this->gids_.clear();
- f = new std::ifstream((this->prefix_ + VFS_GROUPS_FILE).c_str());
- if (f->is_open()) {
- n = 0;
- while (std::getline(*f, entrypp)) {
- n++;
- entry = entrypp.c_str();
- ret = sscanf(entry, "%u%*[ \t]%u%*[ \t]%n", &gid, &banned, &pos);
- if (ret != 2) {
- f->close();
- delete f;
- vfsThrow(ret == EOF ? DMLITE_SYSERR(EINVAL) : errno, "parsing groups file failed at line %d (%s)", n, entry);
- }
- //printf("entry: %u %u '%s'\n", gid, banned, entry + pos);
-
- gi.clear();
- gi.name = entry + pos;
- gi["gid"] = gid;
- gi["banned"] = banned ? 1 : 0;
- this->groups_.push_back(gi);
- // no check - we're OK with multiple groups on the same gid
- this->gids_.insert(gid);
+ try {
+ csvLoad(VFS_GROUPS_FILE, (void *)&this->groups_, this->groupFields_, csv_groups_load_entry);
+ } catch (DmException& e) {
+ switch (e.code()) {
+ case DMLITE_SYSERR(EIO):
+ break;
+ default:
+ vfsThrow(e.code(), std::string("error parsing groups file ") + e.what());
+ break;
}
- f->close();
}
- delete f;
+ for (size_t i = 0; i < this->groups_.size(); i++)
+ this->gids_.insert(this->groups_[i].getUnsigned("gid"));
}
-void VfsAuthn::vfsSaveUsers() throw (DmException) {
- std::ofstream f;
- std::string newName, name;
+static bool csv_users_save_entry(void *ctx, size_t n, Extensible& values) {
+ std::vector<UserInfo> *uis = (std::vector<UserInfo> *)ctx;
+
+ if (n < uis->size()) {
+ values.copy((*uis)[n]);
+ values["name"] = (*uis)[n].name;
+ return true;
+ }
+
+ return false;
+}
+
+
+void VfsAuthn::vfsSaveUsers() throw (DmException) {
if (this->noSync_) {
this->dirtyUsers_ = true;
return;
}
- name = this->prefix_ + VFS_USERS_FILE;
- newName = this->prefix_ + ".new" + VFS_USERS_FILE;
-
- f.open(newName.c_str());
- if (!f) {
+ try {
+ csvSave(VFS_USERS_FILE, (void *)&this->users_, this->userFields_, csv_users_save_entry);
+ } catch (DmException& e) {
log(LOG_NOTICE, "could not create new version of users file in '%s'", this->prefix_.c_str());
this->dirtyUsers_ = true;
return;
- }
-
- for (size_t i = 0; i < this->users_.size(); i++) {
- f << this->users_[i].getUnsigned("uid") << "\t";
- f << this->users_[i].getUnsigned("banned") << "\t";
- f << this->users_[i].name << "\n";
}
- f.close();
- wrapCall(rename(newName.c_str(), name.c_str()), "could not save users file");
-
this->dirtyUsers_ = false;
}
-void VfsAuthn::vfsSaveGroups() throw (DmException) {
- std::ofstream f;
- std::string newName, name;
+static bool csv_groups_save_entry(void *ctx, size_t n, Extensible& values) {
+ std::vector<GroupInfo> *gis = (std::vector<GroupInfo> *)ctx;
+
+ if (n < gis->size()) {
+ values.copy((*gis)[n]);
+ values["name"] = (*gis)[n].name;
+ return true;
+ }
+
+ return false;
+}
+
+
+void VfsAuthn::vfsSaveGroups() throw (DmException) {
if (this->noSync_) {
this->dirtyGroups_ = true;
return;
}
- name = this->prefix_ + VFS_GROUPS_FILE;
- newName = this->prefix_ + ".new" + VFS_GROUPS_FILE;
-
- f.open(newName.c_str());
- if (!f) {
+ try {
+ csvSave(VFS_GROUPS_FILE, (void *)&this->groups_, this->groupFields_, csv_groups_save_entry);
+ } catch (DmException& e) {
log(LOG_NOTICE, "could not create new version of groups file in '%s'", this->prefix_.c_str());
this->dirtyGroups_ = true;
return;
}
- for (size_t i = 0; i < this->groups_.size(); i++) {
- f << this->groups_[i].getUnsigned("gid") << "\t";
- f << this->groups_[i].getUnsigned("banned") << "\t";
- f << this->groups_[i].name << "\n";
+ this->dirtyGroups_ = false;
+}
+
+
+
+void VfsAuthn::csvLoad(std::string fileName, void *ctx, std::set<std::string>& keySet, csv_load_entry_f *callback) throw (DmException) {
+ std::ifstream f;
+ std::string line;
+ size_t i, n;
+ char *s, *value, *ptr;
+ std::vector<std::string> keys;
+ dmlite::Extensible values;
+
+ f.open((this->prefix_ + fileName).c_str());
+ if (!f)
+ throw DmException(DMLITE_SYSERR(EIO), "can't open file");
+
+ // header
+ if (!std::getline(f, line))
+ return;
+ ptr = s = strdup(line.c_str());
+ value = strsep(&ptr, "\t");
+ while (value) {
+ keys.push_back(value);
+ value = strsep(&ptr, "\t");
+ }
+ free(s);
+
+ // name column always in the end
+ if (keys.back() != "name")
+ throw DmException(DMLITE_SYSERR(EINVAL), "at line 1 - missing 'name' column (%s)", line.c_str());
+
+ keySet.clear();
+ for (size_t i = 0; i < keys.size(); i++)
+ keySet.insert(keys[i]);
+
+ // data
+ n = 1;
+ while (std::getline(f, line)) {
+ n++;
+
+ values.clear();
+ ptr = s = strdup(line.c_str());
+ value = strsep(&ptr, "\t");
+ for (i = 0; i < keys.size() && value; i++) {
+ if (value[0] != '\0')
+ values[keys[i]] = std::string(value);
+ value = strsep(&ptr, "\t");
+ }
+ free(s);
+
+ if (i != keys.size())
+ throw DmException(DMLITE_SYSERR(EINVAL), "at line %d - missing columns (%s)", n, line.c_str());
+
+ (*callback)(ctx, values);
}
+}
- f.close();
- wrapCall(rename(newName.c_str(), name.c_str()), "could not save groups file");
- this->dirtyGroups_ = false;
+
+void VfsAuthn::csvSave(std::string fileName, void *ctx, std::set<std::string>& keySet, csv_save_entry_f *callback) throw (DmException) {
+ std::ofstream f;
+ std::string newPath, path;
+ size_t n;
+ std::vector<std::string> keys;
+ dmlite::Extensible values;
+
+ path = this->prefix_ + fileName;
+ newPath = this->prefix_ + ".new" + fileName;
+
+ f.open(newPath.c_str());
+ if (!f)
+ throw DmException(DMLITE_SYSERR(EIO), "could create file '%s'", newPath.c_str());
+
+ // name column always in the end
+ for (std::set<std::string>::iterator it = keySet.begin(); it != keySet.end(); it++)
+ if (*it == "name") continue;
+ else keys.push_back(*it);
+ keys.push_back("name");
+
+ // header
+ for (size_t i = 0; i < keys.size(); i++) {
+ if (i) f << "\t";
+ f << keys[i];
+ }
+ f << std::endl;
+
+ // data
+ n = 0;
+ while ((*callback)(ctx, n, values)) {
+ for (size_t i = 0; i < keys.size(); i++) {
+ if (i) f << "\t";
+ f << values.getString(keys[i]);
+ }
+ f << std::endl;
+ values.clear();
+ n++;
+ }
+
+ f.close();
+ if (rename(newPath.c_str(), path.c_str()) == -1)
+ throw DmException(errno, "could not save file '%s'", path.c_str());
}