1 // this is nconfig 0.92, a bit modified
6 #include <lib/base/nconfig.h>
11 #include <sys/types.h>
21 #define PAGESIZE PAGE_SIZE
27 #define SB ((struct nc_sb_s *) data)
28 #define CM(x) ((struct nc_chunk_s *) (data+(x)))
29 #define DE(x) ((struct nc_de_s *) (data+(x)))
30 #define IDE(x, y) (DE(((unsigned *) (data+(x)->offset))[(y)]))
31 #define CS(x) (((unsigned *) (data+(x)))[-1])
33 inline unsigned NConfig::crc(const char *d, unsigned len)
36 unsigned l = len / sizeof(unsigned);
39 ret += *(const unsigned *)d;
40 ret = (ret << 3) & (ret >> 29);
42 d += sizeof(unsigned);
47 NConfig::NConfig(int protect)
50 cname = fname = data = NULL;
54 revision = update = lsize = omode = 0;
74 char *buffer=new char[size];
75 memcpy(buffer, data, size);
78 ::lseek(fd, 0, SEEK_SET);
79 ::write(fd, buffer, size);
87 munmap(data, sb->size);
98 int NConfig::setName(const char *name)
105 fname = strdup(name);
109 int NConfig::createNew(unsigned resize, unsigned dirent, unsigned mchunks)
113 if (!access(fname, F_OK))
117 if ((ff = ::open(fname, O_WRONLY | O_CREAT, 0600)) == -1)
119 struct nc_sb_s bsb = {NC_SB_MAGIC, resize*PAGESIZE,
120 dirent, mchunks, resize, mchunks,
121 sizeof(struct nc_sb_s)+sizeof(struct nc_de_s)+2*sizeof(unsigned),
122 sizeof(struct nc_sb_s), 0};
123 struct nc_de_s bde = {sizeof(struct nc_sb_s)+sizeof(struct nc_de_s),
124 NC_DIR, sizeof(struct nc_sb_s), 0, 0, 0};
125 struct nc_chunk_s bcm;
127 write(ff, &bsb, sizeof(bsb));
128 write(ff, &bde, sizeof(bde));
131 lseek(ff, sizeof(unsigned)-2, SEEK_CUR);
132 unsigned cl = sizeof(nc_chunk_s)*mchunks+sizeof(unsigned);
133 write(ff, &cl, sizeof(unsigned));
135 bcm.offset = bsb.chunk + sizeof(struct nc_chunk_s)*mchunks;
136 bcm.size = bsb.size - bcm.offset;
138 write(ff, &bcm, sizeof(bcm));
140 lseek(ff, bsb.size-1, SEEK_SET);
147 int NConfig::open(int how)
151 if (how != NC_O_RO && how != NC_O_RW)
157 if ((ff = ::open(fname, how)) == -1)
164 return NC_ERR_CORRUPT;
167 if ((data = (char *) mmap(NULL, sbuf.st_size, how == NC_O_RO ? PROT_READ : (PROT_READ|PROT_WRITE), MAP_PRIVATE, ff, 0)) == MAP_FAILED) {
169 if ((data = (char *) mmap(NULL, sbuf.st_size, how == NC_O_RO ? PROT_READ : (PROT_READ|PROT_WRITE), MAP_SHARED, ff, 0)) == MAP_FAILED) {
174 if (memcmp(((struct nc_sb_s *) data)->magic, NC_SB_MAGIC, 4)) {
175 munmap(data, sbuf.st_size);
177 return NC_ERR_CORRUPT;
185 lockFile(NC_L_RO, TRUE);
191 void NConfig::expand(unsigned toadd)
193 unsigned nsize = sb->size + toadd;
194 lseek(fd, nsize-1, SEEK_SET);
196 _remap(sb->size, nsize);
198 cdir = getDirEnt(cname);
199 chunks = CM(sb->chunk);
200 #ifdef NC_DEBUG_ALLOC
201 fprintf(stderr, "Expanded from %u to %u\n", nsize-toadd, nsize);
205 unsigned NConfig::getChunk(unsigned s)
209 // Make sure we get aligned data
210 s = alignSize(s) + sizeof(unsigned);
212 #ifdef NC_DEBUG_ALLOC
213 fprintf(stderr, "Taking %u (total %u)\n", s, sb->chunk_ttl);
217 int left = 0, right = sb->chunk_ttl - 1, c;
218 while (left <= right) {
219 int diff = chunks[c = (left + right) / 2].size - s;
220 if (diff < 0 || diff == sizeof(unsigned)) {
221 #ifdef NC_DEBUG_ALLOC
223 fprintf(stderr, "Rejected chunk %d (%u:%u)\n", c, chunks[c].offset, chunks[c].size);
234 unsigned ll = (s / (sb->size_inc*PAGESIZE) + 1) * PAGESIZE * sb->size_inc;
235 // we don't have a suitable chunk
237 // insert new chunk into list (always succeeds)
238 *(unsigned *)(data+sb->size-ll) = ll;
239 fast_free(sb->size-ll+sizeof(unsigned));
243 #ifdef NC_DEBUG_ALLOC
244 fprintf(stderr, "haluz 7: off = %u size = %u\n", chunks[7].offset, chunks[7].size);
245 fprintf(stderr, "Got %u chunk (pos %d), taking %u\n", chunks[lst].size, lst, s);
246 fprintf(stderr, "chunk (%u:%u)\n", chunks[lst].offset, chunks[lst].size);
249 unsigned best = chunks[lst].offset+sizeof(unsigned);
250 memset(data+best, 0, s-sizeof(unsigned));
251 chunks[lst].size -= s;
252 chunks[lst].offset += s;
255 while (lst < ((signed)sb->chunk_ttl - 1) && chunks[lst].size < chunks[lst+1].size) {
256 unsigned b = chunks[lst].size;
257 unsigned i = lst + 1;
258 chunks[lst].size = chunks[i].size;
260 b = chunks[lst].offset;
261 chunks[lst].offset = chunks[i].offset;
262 chunks[i].offset = b;
266 #ifdef NC_DEBUG_ALLOC
267 fprintf(stderr, "Returned %u:%u\n", best, CS(best));
272 void NConfig::freeChunk(unsigned where)
274 #ifdef NC_DEBUG_ALLOC
275 fprintf(stderr, "Free chunk: %u\n", CS(where));
277 if (chunks[sb->chunk_ttl-2].size) {
278 #ifdef NC_DEBUG_ALLOC
279 fprintf(stderr, "Last slot available.\n");
281 unsigned n = getChunk((sb->chunk_ttl+sb->chunk_inc)*sizeof(struct nc_chunk_s));
282 unsigned f = sb->chunk;
283 memcpy(data+n, chunks, (sb->chunk_ttl-1)*sizeof(struct nc_chunk_s));
284 chunks = CM(sb->chunk = n);
285 sb->chunk_ttl += sb->chunk_inc;
291 inline unsigned NConfig::alignSize(unsigned s)
293 unsigned of = s % sizeof(unsigned);
294 return of ? s + sizeof(unsigned) - of : s;
297 void NConfig::delKey(const char *name)
299 _delKey(name, NULL, TRUE);
302 void NConfig::_delKey(const char *name, struct nc_de_s *p, int tosort)
307 struct nc_de_s *nd = getDirEnt(name, p);
308 if (nd && nd != rdir && nd != cdir) {
309 unsigned ndo = ((char *)nd) - data;
310 if (nd->type == NC_DIR)
311 for (unsigned i=0; i<DE(ndo)->pages; i++) {
312 struct nc_de_s *dd = IDE(nd, i);
314 _delKey(data+dd->name, nd, FALSE);
318 freeChunk(nd->offset);
319 freeChunk(DE(ndo)->name);
321 struct nc_de_s *parent = DE(nd->parent);
322 memset(nd, 0, sizeof(struct nc_de_s));
323 // keep parent directory sorted
326 while (i < parent->pages && IDE(parent, i) != nd)
328 memmove(((unsigned *)(data+parent->offset))+i,
329 ((unsigned *)(data+parent->offset))+i+1,
330 sizeof(unsigned)*(parent->pages-i-1));
331 ((unsigned *)(data+parent->offset))[parent->pages-1] = ndo;
337 int NConfig::_setKey(const char *name, const unsigned t, const char *value, const unsigned len)
341 if (omode != NC_O_RW)
342 return NC_ERR_RDONLY;
344 struct nc_de_s *nd = getDirEnt(name);
345 #ifdef NC_DEBUG_INSERT
346 fprintf(stderr, "Found DE %p\n", nd);
349 struct nc_de_s *sd = *name == '/' ? rdir : cdir;
350 char *parse = canonize(name), *p = parse;
352 while ((nd = getDirEnt(p, sd)))
353 if (nd->type == NC_DIR) {
364 unsigned sdo = ((char *)sd) - data;
365 while (*(p+(pl = strlen(p)+1))) {
366 ds.pages = ds.offset = 0;
367 ds.name = getChunk(pl);
368 memcpy(data+ds.name, p, pl);
370 #ifdef NC_DEBUG_INSERT
371 fprintf(stderr, "Insertion parent 2: %p\n", DE(sdo));
373 // FIXME: crc calculation
374 sdo = ((char *)insert(sdo, &ds)) - data;
378 memcpy(data+(ds.name = getChunk(pl)), p, pl);
379 ds.pages = ds.offset = 0;
380 #ifdef NC_DEBUG_INSERT
381 fprintf(stderr, "Insertion parent 1: %p\n", DE(sdo));
383 nd = insert(sdo, &ds);
391 unsigned ndo = ((char *)nd) - data;
394 if (nd->offset && CS(nd->offset)-sizeof(unsigned) < len) {
395 freeChunk(nd->offset);
400 if (CS(nd->offset)-sizeof(unsigned) > alignSize(len)+sizeof(unsigned)) {
401 unsigned trim = CS(nd->offset) - alignSize(len) - sizeof(unsigned);
402 unsigned off = nd->offset + alignSize(len) + sizeof(unsigned);
404 CS(nd->offset) -= trim;
409 unsigned off = getChunk(len);
413 memcpy(data+nd->offset, value, len);
417 freeChunk(nd->offset);
421 // Preallocate pages for directory
422 if (len > nd->pages) {
423 unsigned off = getChunk(sizeof(unsigned)*len);
424 if (DE(ndo)->offset) {
425 memcpy(data+off, data+DE(ndo)->offset, sizeof(unsigned)*(DE(ndo)->pages));
426 freeChunk(DE(ndo)->offset);
428 DE(ndo)->offset = off;
429 for (unsigned al = len - DE(ndo)->pages; al; al--) {
430 off = getChunk(sizeof(struct nc_de_s));
431 ((unsigned *)(data+DE(ndo)->offset))[DE(ndo)->pages++] = off;
435 #ifdef NC_DEBUG_INSERT
436 fprintf(stderr, "%p\n", cdir);
441 char *NConfig::getName(const struct nc_de_s *w)
445 char *parent = getName(DE(w->parent));
446 unsigned l1 = strlen(parent);
447 unsigned l2 = strlen(data+w->name)+1;
449 parent = (char *) realloc(parent, l1 + l2 + (l1 == 1 ? 0 : 1));
451 memcpy(parent+l1, "/", 2);
454 memcpy(parent+l1, data+w->name, l2);
458 int NConfig::chDir(const char *name)
465 struct nc_de_s *nd = getDirEnt(name);
467 if (nd->type == NC_DIR) {
470 cname = getName(cdir);
479 const char *NConfig::pwDir()
484 struct nc_de_s *l = cdir;
485 char * ret = strdup(data+l->name);
486 while (DE(l->parent) != l) {
487 unsigned len = CS(l->name);
488 char *r = (char *) malloc(strlen(ret) + len + 2);
489 memcpy(r, data+l->name, len);
490 if (*ret != '/' && DE(l->parent) != rdir)
501 struct nc_de_s *NConfig::getDirEnt(const char *name, struct nc_de_s *cc)
503 struct nc_de_s *ret = cc ? cc : ((*name == '/') ? rdir : cdir);
504 char *c = canonize(name), *can;
509 if (!strcmp(c, ".."))
510 ret = DE(ret->parent);
512 if (strcmp(c, ".")) {
513 struct nc_de_s *re = ret;
514 int left = 0, right = ret->pages-1, p, r;
517 while (left <= right) {
518 p = (left + right) / 2;
519 r = strcmp(c, data+IDE(re, p)->name);
532 if (!ret || (*c && ret->type != NC_DIR)) {
541 char *NConfig::canonize(const char *name)
545 size_t i = strlen(name);
546 char *ret = (char *)calloc(1, i+3);
547 memcpy(ret, name, i);
548 for (size_t j=0; j<i; j++)
554 struct nc_de_s *NConfig::insert(unsigned where, struct nc_de_s *what)
556 #ifdef NC_DEBUG_INSERT
557 fprintf(stderr, "Insertion: %s %d\n", data+what->name, what->type);
559 struct nc_de_s *w = DE(where);
560 if (!DE(where)->offset || IDE(w, w->pages-1)->type) {
561 unsigned a = getChunk((w->pages+sb->ent_inc)*sizeof(unsigned));
564 memcpy(data+a, data+w->offset, w->pages*sizeof(unsigned));
565 freeChunk(w->offset);
569 for (unsigned ha = 0; ha<sb->ent_inc; ha++) {
570 unsigned off = getChunk(sizeof(struct nc_de_s));
572 ((unsigned *)(data+w->offset))[w->pages] = off;
576 int i = 0, l = 0, r = w->pages - 1, c;
579 if (!IDE(w, c)->type || strcmp(data+what->name, data+IDE(w, c)->name) > 0) {
586 #ifdef NC_DEBUG_INSERT
587 fprintf(stderr, "Insertion to slot %u (%s)\n", i, data+what->name);
589 what->parent = where;
590 unsigned to = ((unsigned *)(data+w->offset))[w->pages-1];
591 memmove(((unsigned *)(data+w->offset))+i+1, ((unsigned *)(data+w->offset))+i, sizeof(unsigned)*(w->pages-i-1));
592 ((unsigned *)(data+w->offset))[i] = to;
593 void *ret = memcpy(DE(to), what, sizeof(struct nc_de_s));
595 return (struct nc_de_s *)ret;
598 void NConfig::status()
603 fprintf(stderr, "Size:\t%u\n", sb->size);
604 unsigned low=0, hi=chunks[0].size, cnt=0, ttl=0;
605 for (unsigned i=0; i<sb->chunk_ttl; i++)
606 if (chunks[i].size > 0) {
607 if (!low || low > chunks[i].size)
608 low = chunks[i].size;
609 ttl += chunks[i].size;
613 fprintf(stderr, "Free:\t%u in %u chunk%s\n", ttl, cnt, cnt > 1 ? "s" : "");
615 fprintf(stderr, "Min:\t%u\nAvg:\t%u\nMax:\t%u\n", low, ttl / cnt, hi);
618 struct nc_ls_s *NConfig::ls(const char *name)
624 struct nc_ls_s *rt = NULL;
626 struct nc_de_s *de = NULL;
627 struct nc_de_s *ds = name ? getDirEnt(name) : cdir;
629 if (ds && ds->type == NC_DIR) {
630 for (unsigned i=0; i<ds->pages; i++) {
632 if (de->type && de->name) {
633 rt = (struct nc_ls_s *) realloc(rt, (count+2)*sizeof(nc_ls_s));
634 rt[count].type = de->type;
635 rt[count].name = strdup(data+de->name);
636 rt[++count].type = 0;
637 rt[count].name = NULL;
645 void NConfig::fast_free(unsigned offset)
647 unsigned s = CS(offset), i = 0;
648 offset -= sizeof(unsigned);
651 if (!chunks[i].size) {
652 #ifdef NC_DEBUG_ALLOC
653 fprintf(stderr, "Inserting %u:%u to %u\n", offset, s, i);
655 chunks[i].offset = offset;
659 if (chunks[i].offset == offset + s) {
660 #ifdef NC_DEBUG_ALLOC
661 fprintf(stderr, "Prepending %u:%u to %u (%u:%u)\n", offset, s, i, chunks[i].offset, chunks[i].size);
663 chunks[i].offset -= s;
667 if (offset == chunks[i].offset + chunks[i].size) {
668 #ifdef NC_DEBUG_ALLOC
669 fprintf(stderr, "Appending %u:%u to %u (%u:%u)\n", offset, s, i, chunks[i].offset, chunks[i].size);
677 // Keep the array sorted
678 while (i && chunks[i].size > chunks[i-1].size) {
679 unsigned b = chunks[i].size;
681 chunks[i].size = chunks[j].size;
683 b = chunks[i].offset;
684 chunks[i].offset = chunks[j].offset;
685 chunks[j].offset = b;
690 int NConfig::renameKey(const char *oldname, const char *newname)
694 if (omode != NC_O_RW)
695 return NC_ERR_RDONLY;
698 struct nc_de_s *parent, *nd = getDirEnt(newname);
700 if ((nd = getDirEnt(oldname))) {
701 size_t len = strlen(newname)+1;
702 int inc = strcmp(oldname, newname);
703 unsigned i, off, pos, ndo = ((char *)nd) - data;
704 if (alignSize(len) != CS(nd->name)) {
710 memcpy(data+nd->name, newname, len);
711 parent = DE(nd->parent);
712 for (pos = 0; pos < parent->pages && IDE(parent, pos) != nd; pos++)
714 for (i = pos; i>=0 && i<parent->pages; i += inc)
715 if (strcmp(data+IDE(parent, i)->name, newname) != inc)
718 memmove(((unsigned *)(data+parent->offset))+i+1,
719 ((unsigned *)(data+parent->offset))+i,
720 sizeof(unsigned)*(pos - i));
722 memmove(((unsigned *)(data+parent->offset))+pos,
723 ((unsigned *)(data+parent->offset))+pos+1,
724 sizeof(unsigned)*(i-pos));
725 ((unsigned *)(data+parent->offset))[i] = ndo;
735 int NConfig::createDir(const char *name, unsigned entries)
737 return _setKey(name, NC_DIR, NULL, entries);
740 int NConfig::setKey(const char *name, const unsigned long long value)
742 return _setKey(name, NC_UINT, (const char *)&value, sizeof(value));
745 int NConfig::setKey(const char *name, const unsigned value)
747 unsigned long long b = value;
748 return _setKey(name, NC_UINT, (const char *)&b, sizeof(b));
751 int NConfig::setKey(const char *name, const signed long long value)
753 return _setKey(name, NC_INT, (const char *)&value, sizeof(value));
756 int NConfig::setKey(const char *name, const int value)
758 signed long long b = value;
759 return _setKey(name, NC_INT, (const char *)&b, sizeof(b));
762 int NConfig::setKey(const char *name, const char *value)
764 return _setKey(name, NC_STRING, value, strlen(value)+1);
767 int NConfig::setKey(const char *name, const long double value)
769 return _setKey(name, NC_DOUBLE, (const char *)&value, sizeof(value));
772 int NConfig::setKey(const char *name, const double value)
774 long double b = value;
775 return _setKey(name, NC_DOUBLE, (const char *)&b, sizeof(b));
778 int NConfig::setKey(const char *name, const char *value, const unsigned len)
783 return _setKey(name, NC_RAW, NULL, 0);
784 return _setKey(name, NC_RAW, value, len);
787 int NConfig::getKey(const char *name, unsigned long long &value)
793 struct nc_de_s *k = getDirEnt(name);
795 if (k->type == NC_UINT) {
796 memcpy(&value, data+k->offset, sizeof(value));
805 int NConfig::getKey(const char *name, unsigned &value)
811 struct nc_de_s *k = getDirEnt(name);
813 if (k->type == NC_UINT) {
814 unsigned long long b;
815 memcpy(&b, data+k->offset, sizeof(b));
825 int NConfig::getKey(const char *name, long double &value)
831 struct nc_de_s *k = getDirEnt(name);
833 if (k->type == NC_DOUBLE) {
834 memcpy(&value, data+k->offset, sizeof(value));
843 int NConfig::getKey(const char *name, double &value)
849 struct nc_de_s *k = getDirEnt(name);
851 if (k->type == NC_DOUBLE) {
853 memcpy(&b, data+k->offset, sizeof(b));
863 int NConfig::getKey(const char *name, signed long long &value)
869 struct nc_de_s *k = getDirEnt(name);
871 if (k->type == NC_INT) {
872 memcpy(&value, data+k->offset, sizeof(value));
881 int NConfig::getKey(const char *name, int &value)
887 struct nc_de_s *k = getDirEnt(name);
889 if (k->type == NC_INT) {
891 memcpy(&b, data+k->offset, sizeof(b));
901 int NConfig::getKey(const char *name, char *&value)
907 struct nc_de_s *k = getDirEnt(name);
909 if (k->type == NC_STRING) {
911 if (!(value = strdup(data+k->offset)))
923 int NConfig::getKey(const char *name, char *&value, unsigned &len)
929 struct nc_de_s *k = getDirEnt(name);
931 if (k->type == NC_RAW) {
934 value = (char *)malloc(len);
935 memcpy(value, data+k->offset, len);
948 void NConfig::lockFile(int type, int force)
951 fprintf(stderr, "Lock called type=%d force=%d lock=%d olck=%u\n", type, force, lock, olck);
953 if (lock == NC_L_RO && type == NC_L_RW) {
954 fprintf(stderr, "Lock promotion is not possible.\n");
957 if (lock != NC_L_NONE) {
962 struct flock flc = { type == NC_L_RW ? F_WRLCK : F_RDLCK, SEEK_SET, 0, 0, 0 };
963 while (fcntl(fd, F_SETLKW, &flc)) {
965 flc.l_type = type == NC_L_RW ? F_WRLCK : F_RDLCK;
966 flc.l_whence = SEEK_SET;
967 flc.l_len = flc.l_start = 0;
971 fprintf(stderr, "Locked %u %u %s\n", sb->modtime, update, force ? "forced." : "");
973 if (careful && type == NC_L_RW)
974 mprotect(data, sb->size, PROT_READ | PROT_WRITE);
977 if (sb->modtime != update || force) {
978 // refresh memory mapping
979 if (lsize != sb->size) {
980 _remap(lsize, sb->size);
982 chunks = CM(sb->chunk);
984 cdir = getDirEnt(cname);
985 update = sb->modtime;
989 void NConfig::unLockFile()
992 fprintf(stderr, "UnLock called lock=%u olck=%u\n", lock, olck);
998 if (lock == NC_L_NONE)
1000 struct flock flc = {F_UNLCK, SEEK_SET, 0, 0, 0 };
1001 update = sb->modtime;
1002 #ifdef NC_DEBUG_LOCK
1003 fprintf(stderr, "Unlock %u\n", update);
1006 mprotect(data, sb->size, PROT_READ);
1007 fcntl(fd, F_SETLK, &flc);
1012 void NConfig::_remap(const size_t osize, const size_t nsize)
1014 data = (char *) mremap(data, osize, nsize, 1);
1015 if (data == MAP_FAILED) {
1020 rdir = DE(sb->root);
1023 char * NConfig::version()
1025 return strdup("EliteDVB registry");