add some comments
[enigma2.git] / lib / base / nconfig.cpp
1 // this is nconfig 0.92, a bit modified
2 #define NO_MAP_SHARED
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <sys/stat.h>
6 #include <lib/base/nconfig.h>
7
8 #include <fcntl.h>
9 #include <string.h>
10 #include <memory.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13 #include <sched.h>
14
15 #include <sys/mman.h>
16
17 #include <limits.h>
18
19 #ifndef PAGESIZE
20 #ifdef PAGE_SIZE
21 #define PAGESIZE PAGE_SIZE
22 #else
23 #define PAGESIZE 4096
24 #endif
25 #endif
26
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])
32
33 inline unsigned NConfig::crc(const char *d, unsigned len)
34 {
35         unsigned ret = 0;
36         unsigned l = len / sizeof(unsigned);
37
38         while (l) {
39                 ret += *(const unsigned *)d;
40                 ret = (ret << 3) & (ret >> 29);
41                 l--;
42                 d += sizeof(unsigned);
43         }
44         return ret;
45 }
46
47 NConfig::NConfig(int protect)
48 {
49         fd = -1;
50         cname = fname = data = NULL;
51         sb = NULL;
52         cdir = NULL;
53         chunks = NULL;
54         revision = update = lsize = omode = 0;
55         olck = 0;
56         lock = NC_L_NONE;
57         careful = protect;
58 }
59
60 NConfig::~NConfig()
61 {
62         close();
63         free(fname);
64 }
65
66 void NConfig::close()
67 {
68         free(cname);
69         cname = NULL;
70         if (fd > -1) {
71 #ifdef NO_MAP_SHARED
72                 if (data) {
73                         int size=sb->size;
74                         char *buffer=new char[size];
75                         memcpy(buffer, data, size);
76                         munmap(data, size);
77                         data = NULL;
78                         ::lseek(fd, 0, SEEK_SET);
79                         ::write(fd, buffer, size);
80                         delete[] buffer;
81                 }
82 #endif
83                 ::close(fd);
84                 fd = -1;
85         }
86         if (data) {
87                 munmap(data, sb->size);
88                 data = NULL;
89         }
90 }
91
92 void NConfig::flush()
93 {
94         close();
95         open(omode);
96 }
97
98 int NConfig::setName(const char *name)
99 {
100         if (!name)
101                 return NC_ERR_NVAL;
102         if (fd > -1)
103                 return NC_ERR_PERM;
104         free(fname);
105         fname = strdup(name);
106         return NC_ERR_OK;
107 }
108
109 int NConfig::createNew(unsigned resize, unsigned dirent, unsigned mchunks)
110 {
111         if (fd > -1)
112                 return NC_ERR_NVAL;
113         if (!access(fname, F_OK))
114                 return NC_ERR_PERM;
115
116         int ff;
117         if ((ff = ::open(fname, O_WRONLY | O_CREAT, 0600)) == -1)
118                 return NC_ERR_NFILE;
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;
126
127         write(ff, &bsb, sizeof(bsb));
128         write(ff, &bde, sizeof(bde));
129         write(ff, "/", 2);
130
131         lseek(ff, sizeof(unsigned)-2, SEEK_CUR);
132         unsigned cl = sizeof(nc_chunk_s)*mchunks+sizeof(unsigned);
133         write(ff, &cl, sizeof(unsigned));
134
135         bcm.offset = bsb.chunk + sizeof(struct nc_chunk_s)*mchunks;
136         bcm.size = bsb.size - bcm.offset;
137
138         write(ff, &bcm, sizeof(bcm));
139
140         lseek(ff, bsb.size-1, SEEK_SET);
141         write(ff, "", 1);
142         ::close(ff);
143         return NC_ERR_OK;
144 }
145         
146
147 int NConfig::open(int how)
148 {
149         if (!fname)
150                 return NC_ERR_NFILE;
151         if (how != NC_O_RO && how != NC_O_RW)
152                 return NC_ERR_TYPE;
153         if (fd > -1)
154                 close();
155
156         int ff;
157         if ((ff = ::open(fname, how)) == -1)
158                 return NC_ERR_PERM;
159
160         struct stat sbuf;
161         fstat(ff, &sbuf);
162
163         if (!sbuf.st_size)
164                 return NC_ERR_CORRUPT;
165
166 #ifdef NO_MAP_SHARED
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) {
168 #else
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) {
170 #endif
171                 ::close(ff);
172                 return NC_ERR_NMEM;
173         }
174         if (memcmp(((struct nc_sb_s *) data)->magic, NC_SB_MAGIC, 4)) {
175                 munmap(data, sbuf.st_size);
176                 ::close(ff);
177                 return NC_ERR_CORRUPT;
178         }
179         fd = ff;
180         omode = how;
181         sb = SB;
182         lsize = 0;
183         cname = strdup("/");
184
185         lockFile(NC_L_RO, TRUE);
186         rdir = DE(sb->root);
187         unLockFile();
188         return NC_ERR_OK;
189 }
190
191 void NConfig::expand(unsigned toadd)
192 {
193         unsigned nsize = sb->size + toadd;
194         lseek(fd, nsize-1, SEEK_SET);
195         write(fd, "", 1);
196         _remap(sb->size, nsize);
197         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);
202 #endif
203 }
204
205 unsigned NConfig::getChunk(unsigned s)
206 {
207         int lst = -1;
208
209         // Make sure we get aligned data
210         s = alignSize(s) + sizeof(unsigned);
211
212 #ifdef NC_DEBUG_ALLOC
213         fprintf(stderr, "Taking %u (total %u)\n", s, sb->chunk_ttl);
214 #endif
215
216         do {
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
222                                 if (diff > 0)
223                                         fprintf(stderr, "Rejected chunk %d (%u:%u)\n", c, chunks[c].offset, chunks[c].size);
224 #endif
225                                 right = c - 1;
226                                 continue;
227                         }
228                         lst = c;
229                         if (!diff)
230                                 break;
231                         left = c + 1;
232                 }
233                 if (lst < 0) {
234                         unsigned ll = (s / (sb->size_inc*PAGESIZE) + 1) * PAGESIZE * sb->size_inc;
235                         // we don't have a suitable chunk
236                         expand(ll);
237                         // insert new chunk into list (always succeeds)
238                         *(unsigned *)(data+sb->size-ll) = ll;
239                         fast_free(sb->size-ll+sizeof(unsigned));
240                 }
241         } while (lst < 0);
242
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); 
247 #endif
248
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;
253         CS(best) = s;
254
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;
259                 chunks[i].size = b;
260                 b = chunks[lst].offset;
261                 chunks[lst].offset = chunks[i].offset;
262                 chunks[i].offset = b;
263                 lst = i;
264         }
265
266 #ifdef NC_DEBUG_ALLOC
267         fprintf(stderr, "Returned %u:%u\n", best, CS(best));
268 #endif
269         return best;
270 }
271
272 void NConfig::freeChunk(unsigned where)
273 {
274 #ifdef NC_DEBUG_ALLOC
275         fprintf(stderr, "Free chunk: %u\n", CS(where));
276 #endif
277         if (chunks[sb->chunk_ttl-2].size) {
278 #ifdef NC_DEBUG_ALLOC
279                 fprintf(stderr, "Last slot available.\n");
280 #endif
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;
286                 fast_free(f);
287         }
288         fast_free(where);
289 }
290
291 inline unsigned NConfig::alignSize(unsigned s)
292 {
293         unsigned of = s % sizeof(unsigned);
294         return of ? s + sizeof(unsigned) - of : s;
295 }
296
297 void NConfig::delKey(const char *name)
298 {
299         _delKey(name, NULL, TRUE);
300 }
301
302 void NConfig::_delKey(const char *name, struct nc_de_s *p, int tosort)
303 {
304         if (fd < 0)
305                 return;
306         lockFile(NC_L_RW);
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);
313                                 if (dd->type)
314                                         _delKey(data+dd->name, nd, FALSE);
315                                 nd = DE(ndo);
316                         }
317                 sb->modtime++;
318                 freeChunk(nd->offset);
319                 freeChunk(DE(ndo)->name);
320                 nd = DE(ndo);
321                 struct nc_de_s *parent = DE(nd->parent);
322                 memset(nd, 0, sizeof(struct nc_de_s));
323                 // keep parent directory sorted
324                 if (tosort) {
325                         unsigned i = 0;
326                         while (i < parent->pages && IDE(parent, i) != nd)
327                                  i++;
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;
332                 }
333         }
334         unLockFile();
335 }
336
337 int NConfig::_setKey(const char *name, const unsigned t, const char *value, const unsigned len)
338 {
339         if (fd < 0)
340                 return NC_ERR_NFILE;
341         if (omode != NC_O_RW)
342                 return NC_ERR_RDONLY;
343         lockFile(NC_L_RW);
344         struct nc_de_s *nd = getDirEnt(name);
345 #ifdef NC_DEBUG_INSERT
346         fprintf(stderr, "Found DE %p\n", nd);
347 #endif
348         if (!nd) {
349                 struct nc_de_s *sd = *name == '/' ? rdir : cdir;
350                 char *parse = canonize(name), *p = parse;
351         
352                 while ((nd = getDirEnt(p, sd)))
353                         if (nd->type == NC_DIR) {
354                                 sd = nd;
355                                 p += strlen(p)+1;
356                         } else {
357                                 free(parse);
358                                 unLockFile();
359                                 return NC_ERR_PERM;
360                         }
361
362                 size_t pl = 0;
363                 struct nc_de_s ds;
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);
369                         ds.type = NC_DIR;
370 #ifdef NC_DEBUG_INSERT
371                         fprintf(stderr, "Insertion parent 2: %p\n", DE(sdo));
372 #endif
373                         // FIXME: crc calculation
374                         sdo = ((char *)insert(sdo, &ds)) - data;
375                         p += pl;
376                 }
377                 ds.type = t;
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));
382 #endif
383                 nd = insert(sdo, &ds);
384                 sb->modtime++;
385                 free(parse);
386         } else
387                 if (nd->type != t) {
388                         unLockFile();
389                         return NC_ERR_TYPE;
390                 }
391         unsigned ndo = ((char *)nd) - data;
392         if (t != NC_DIR) {
393                 if (value) {
394                         if (nd->offset && CS(nd->offset)-sizeof(unsigned) < len) {
395                                 freeChunk(nd->offset);
396                                 nd = DE(ndo);
397                                 nd->offset = 0;
398                         }
399                         if (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);
403                                         CS(off) = trim;
404                                         CS(nd->offset) -= trim;
405                                         freeChunk(off);
406                                         nd = DE(ndo);
407                                 }
408                         } else {
409                                 unsigned off = getChunk(len);
410                                 nd = DE(ndo);
411                                 nd->offset = off;
412                         }
413                         memcpy(data+nd->offset, value, len);
414                         nd->pages = len;
415                 } else
416                         if (nd->offset) {
417                                 freeChunk(nd->offset);
418                                 DE(ndo)->offset = 0;
419                         }
420         } else
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);
427                         }
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;
432                         }
433                 }
434         unLockFile();
435 #ifdef NC_DEBUG_INSERT
436         fprintf(stderr, "%p\n", cdir);
437 #endif
438         return NC_ERR_OK;
439 }
440
441 char *NConfig::getName(const struct nc_de_s *w)
442 {
443         if (w == rdir)
444                 return strdup("/");
445         char *parent = getName(DE(w->parent));
446         unsigned l1 = strlen(parent);
447         unsigned l2 = strlen(data+w->name)+1;
448
449         parent = (char *) realloc(parent, l1 + l2 + (l1 == 1 ? 0 : 1));
450         if (l1 != 1) {
451                 memcpy(parent+l1, "/", 2);
452                 l1++;
453         }
454         memcpy(parent+l1, data+w->name, l2);
455         return parent;
456 }
457
458 int NConfig::chDir(const char *name)
459 {
460         if (fd < 0)
461                 return NC_ERR_NFILE;
462         lockFile(NC_L_RO);
463
464         int ret = NC_ERR_OK;
465         struct nc_de_s *nd = getDirEnt(name);
466         if (nd) {
467                 if (nd->type == NC_DIR) {
468                         cdir = nd;
469                         free(cname);
470                         cname = getName(cdir);
471                 } else
472                         ret = NC_ERR_NDIR;
473         } else
474                 ret = NC_ERR_NEXIST;
475         unLockFile();
476         return ret;
477 }
478
479 const char *NConfig::pwDir()
480 {
481         if (fd < 0)
482                 return NULL;
483         lockFile(NC_L_RO);
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)
491                         strcat(r, "/");
492                 strcat(r, ret);
493                 free(ret);
494                 ret = r;
495                 l = DE(l->parent);
496         }
497         unLockFile();
498         return ret;
499 }
500
501 struct nc_de_s *NConfig::getDirEnt(const char *name, struct nc_de_s *cc)
502 {
503         struct nc_de_s *ret = cc ? cc : ((*name == '/') ? rdir : cdir);
504         char *c = canonize(name), *can;
505
506         if (!(can = c))
507                 return ret;
508         while (*c) {
509                 if (!strcmp(c, ".."))
510                         ret = DE(ret->parent);
511                 else
512                         if (strcmp(c, ".")) {
513                                 struct nc_de_s *re = ret;
514                                 int left = 0, right = ret->pages-1, p, r;
515
516                                 ret = NULL;
517                                 while (left <= right) {
518                                         p = (left + right) / 2;
519                                         r = strcmp(c, data+IDE(re, p)->name);
520                                         if (r < 0) {
521                                                 left = p + 1;
522                                                 continue;
523                                         }
524                                         if (!r) {
525                                                 ret = IDE(re, p);
526                                                 break;
527                                         }
528                                         right = p - 1;
529                                 }
530                         }
531                 c += strlen(c)+1;
532                 if (!ret || (*c && ret->type != NC_DIR)) {
533                         ret = NULL;
534                         break;
535                 }
536         }
537         free(can);
538         return ret;
539 }
540
541 char *NConfig::canonize(const char *name)
542 {
543         if (*name == '/')
544                 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++)
549                 if (ret[j] == '/')
550                         ret[j] = 0;
551         return ret;
552 }
553
554 struct nc_de_s *NConfig::insert(unsigned where, struct nc_de_s *what)
555 {
556 #ifdef NC_DEBUG_INSERT
557         fprintf(stderr, "Insertion: %s %d\n", data+what->name, what->type);
558 #endif
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));
562                 w = DE(where);
563                 if (w->offset) {
564                         memcpy(data+a, data+w->offset, w->pages*sizeof(unsigned));
565                         freeChunk(w->offset);
566                         w = DE(where);
567                 }
568                 w->offset = a;
569                 for (unsigned ha = 0; ha<sb->ent_inc; ha++) {
570                         unsigned off = getChunk(sizeof(struct nc_de_s));
571                         w = DE(where);
572                         ((unsigned *)(data+w->offset))[w->pages] = off;
573                         w->pages++;
574                 }
575         }
576         int i = 0, l = 0, r = w->pages - 1, c;
577         while (l <= r) {
578                 c = (l + r) / 2;
579                 if (!IDE(w, c)->type || strcmp(data+what->name, data+IDE(w, c)->name) > 0) {
580                         i = c;
581                         r = c - 1;
582                 } else
583                         l = c + 1;
584         }       
585
586 #ifdef NC_DEBUG_INSERT
587         fprintf(stderr, "Insertion to slot %u (%s)\n", i, data+what->name);
588 #endif
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));
594         sb->modtime++;
595         return (struct nc_de_s *)ret;
596 }
597
598 void NConfig::status()
599 {
600         if (fd < 0)
601                 return;
602         lockFile(NC_L_RO);
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;
610                         cnt++;
611                 }
612         unLockFile();
613         fprintf(stderr, "Free:\t%u in %u chunk%s\n", ttl, cnt, cnt > 1 ? "s" : "");
614         if (cnt > 0)
615                 fprintf(stderr, "Min:\t%u\nAvg:\t%u\nMax:\t%u\n", low, ttl / cnt, hi);
616 }
617         
618 struct nc_ls_s *NConfig::ls(const char *name)
619 {
620         if (fd < 0)
621                 return NULL;
622         lockFile(NC_L_RO);
623
624         struct nc_ls_s *rt = NULL;
625         unsigned count = 0;
626         struct nc_de_s *de = NULL;
627         struct nc_de_s *ds = name ? getDirEnt(name) : cdir;
628
629         if (ds && ds->type == NC_DIR) {
630                 for (unsigned i=0; i<ds->pages; i++) {
631                         de = IDE(ds, 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;
638                         }
639                 }
640         }
641         unLockFile();
642         return rt;
643 }
644
645 void NConfig::fast_free(unsigned offset)
646 {
647         unsigned s = CS(offset), i = 0;
648         offset -= sizeof(unsigned);
649
650         while (1) {
651                 if (!chunks[i].size) {
652 #ifdef NC_DEBUG_ALLOC
653                         fprintf(stderr, "Inserting %u:%u to %u\n", offset, s, i);
654 #endif
655                         chunks[i].offset = offset;
656                         chunks[i].size = s;
657                         break;
658                 }
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);
662 #endif
663                         chunks[i].offset -= s;
664                         chunks[i].size += s;
665                         break;
666                 }
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);
670 #endif
671                         chunks[i].size += s;
672                         break;
673                 }
674                 i++;
675         }
676
677         // Keep the array sorted
678         while (i && chunks[i].size > chunks[i-1].size) {
679                 unsigned b = chunks[i].size;
680                 unsigned j = i - 1;
681                 chunks[i].size = chunks[j].size;
682                 chunks[j].size = b;
683                 b = chunks[i].offset;
684                 chunks[i].offset = chunks[j].offset;
685                 chunks[j].offset = b;
686                 i = j;
687         }
688 }
689
690 int NConfig::renameKey(const char *oldname, const char *newname)
691 {
692         if (fd < 0)
693                 return NC_ERR_NFILE;
694         if (omode != NC_O_RW)
695                 return NC_ERR_RDONLY;
696         lockFile(NC_L_RW);
697         int ret = NC_ERR_OK;
698         struct nc_de_s *parent, *nd = getDirEnt(newname);
699         if (nd) {
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)) {
705                                 freeChunk(nd->name);
706                                 off = getChunk(len);
707                                 DE(ndo)->name = off;
708                                 nd = DE(ndo);
709                         }
710                         memcpy(data+nd->name, newname, len);
711                         parent = DE(nd->parent);
712                         for (pos = 0; pos < parent->pages && IDE(parent, pos) != nd; pos++)
713                                 ;
714                         for (i = pos; i>=0 && i<parent->pages; i += inc)
715                                 if (strcmp(data+IDE(parent, i)->name, newname) != inc)
716                                         break;
717                         if (inc == -1)
718                                 memmove(((unsigned *)(data+parent->offset))+i+1,
719                                         ((unsigned *)(data+parent->offset))+i,
720                                         sizeof(unsigned)*(pos - i));
721                         else
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;
726                         sb->modtime++;
727                 } else
728                         ret = NC_ERR_NEXIST;
729         } else
730                 ret = NC_ERR_PERM;
731         unLockFile();
732         return NC_ERR_OK;
733 }
734
735 int NConfig::createDir(const char *name, unsigned entries)
736 {
737         return _setKey(name, NC_DIR, NULL, entries);
738 }
739
740 int NConfig::setKey(const char *name, const unsigned long long value)
741 {
742         return _setKey(name, NC_UINT, (const char *)&value, sizeof(value));
743 }
744
745 int NConfig::setKey(const char *name, const unsigned value)
746 {
747         unsigned long long b = value;
748         return _setKey(name, NC_UINT, (const char *)&b, sizeof(b));
749 }
750
751 int NConfig::setKey(const char *name, const signed long long value)
752 {
753         return _setKey(name, NC_INT, (const char *)&value, sizeof(value));
754 }
755
756 int NConfig::setKey(const char *name, const int value)
757 {
758         signed long long b = value;
759         return _setKey(name, NC_INT, (const char *)&b, sizeof(b));
760 }
761
762 int NConfig::setKey(const char *name, const char *value)
763 {
764         return _setKey(name, NC_STRING, value, strlen(value)+1);
765 }
766
767 int NConfig::setKey(const char *name, const long double value)
768 {
769         return _setKey(name, NC_DOUBLE, (const char *)&value, sizeof(value));
770 }
771
772 int NConfig::setKey(const char *name, const double value)
773 {
774         long double b = value;
775         return _setKey(name, NC_DOUBLE, (const char *)&b, sizeof(b));
776 }
777
778 int NConfig::setKey(const char *name, const char *value, const unsigned len)
779 {
780         if (!value && len)
781                 return NC_ERR_NVAL;
782         if (!len)
783                 return _setKey(name, NC_RAW, NULL, 0);
784         return _setKey(name, NC_RAW, value, len);
785 }
786
787 int NConfig::getKey(const char *name, unsigned long long &value)
788 {
789         if (fd < 0)
790                 return NC_ERR_NFILE;
791         lockFile(NC_L_RO);
792         int ret = NC_ERR_OK;
793         struct nc_de_s *k = getDirEnt(name);
794         if (k) {
795                 if (k->type == NC_UINT) {
796                         memcpy(&value, data+k->offset, sizeof(value));
797                 } else
798                         ret = NC_ERR_TYPE;
799         } else
800                 ret = NC_ERR_NEXIST;
801         unLockFile();
802         return ret;
803 }
804
805 int NConfig::getKey(const char *name, unsigned &value)
806 {
807         if (fd < 0)
808                 return NC_ERR_NFILE;
809         lockFile(NC_L_RO);
810         int ret = NC_ERR_OK;
811         struct nc_de_s *k = getDirEnt(name);
812         if (k) {
813                 if (k->type == NC_UINT) {
814                         unsigned long long b;
815                         memcpy(&b, data+k->offset, sizeof(b));
816                         value = b;
817                 } else
818                         ret = NC_ERR_TYPE;
819         } else
820                 ret = NC_ERR_NEXIST;
821         unLockFile();
822         return ret;
823 }
824
825 int NConfig::getKey(const char *name, long double &value)
826 {
827         if (fd < 0)
828                 return NC_ERR_NFILE;
829         lockFile(NC_L_RO);
830         int ret = NC_ERR_OK;
831         struct nc_de_s *k = getDirEnt(name);
832         if (k) {
833                 if (k->type == NC_DOUBLE) {
834                         memcpy(&value, data+k->offset, sizeof(value));
835                 } else
836                         ret = NC_ERR_TYPE;
837         } else
838                 ret = NC_ERR_NEXIST;
839         unLockFile();
840         return ret;
841 }
842
843 int NConfig::getKey(const char *name, double &value)
844 {
845         if (fd < 0)
846                 return NC_ERR_NFILE;
847         lockFile(NC_L_RO);
848         int ret = NC_ERR_OK;
849         struct nc_de_s *k = getDirEnt(name);
850         if (k) {
851                 if (k->type == NC_DOUBLE) {
852                         long double b;
853                         memcpy(&b, data+k->offset, sizeof(b));
854                         value = b;
855                 } else
856                         ret = NC_ERR_TYPE;
857         } else
858                 ret = NC_ERR_NEXIST;
859         unLockFile();
860         return ret;
861 }
862
863 int NConfig::getKey(const char *name, signed long long &value)
864 {
865         if (fd < 0)
866                 return NC_ERR_NFILE;
867         lockFile(NC_L_RO);
868         int ret = NC_ERR_OK;
869         struct nc_de_s *k = getDirEnt(name);
870         if (k) {
871                 if (k->type == NC_INT) {
872                         memcpy(&value, data+k->offset, sizeof(value));
873                 } else
874                         ret = NC_ERR_TYPE;
875         } else
876                 ret = NC_ERR_NEXIST;
877         unLockFile();
878         return ret;
879 }
880
881 int NConfig::getKey(const char *name, int &value)
882 {
883         if (fd < 0)
884                 return NC_ERR_NFILE;
885         lockFile(NC_L_RO);
886         int ret = NC_ERR_OK;
887         struct nc_de_s *k = getDirEnt(name);
888         if (k) {
889                 if (k->type == NC_INT) {
890                         signed long long b;
891                         memcpy(&b, data+k->offset, sizeof(b));
892                         value = b;
893                 } else
894                         ret = NC_ERR_TYPE;
895         } else
896                 ret = NC_ERR_NEXIST;
897         unLockFile();
898         return ret;
899 }
900
901 int NConfig::getKey(const char *name, char *&value)
902 {
903         if (fd < 0)
904                 return NC_ERR_NFILE;
905         lockFile(NC_L_RO);
906         int ret = NC_ERR_OK;
907         struct nc_de_s *k = getDirEnt(name);
908         if (k) {
909                 if (k->type == NC_STRING) {
910                         if (k->offset) {
911                                 if (!(value = strdup(data+k->offset)))
912                                         ret = NC_ERR_NMEM;
913                         } else
914                                 value = NULL;
915                 } else
916                         ret = NC_ERR_TYPE;
917         } else
918                 ret = NC_ERR_NEXIST;
919         unLockFile();
920         return ret;
921 }
922
923 int NConfig::getKey(const char *name, char *&value, unsigned &len)
924 {
925         if (fd < 0)
926                 return NC_ERR_NFILE;
927         lockFile(NC_L_RO);
928         int ret = NC_ERR_OK;
929         struct nc_de_s *k = getDirEnt(name);
930         if (k) {
931                 if (k->type == NC_RAW) {
932                         if (k->offset) {
933                                 len = k->pages;
934                                 value = (char *)malloc(len);
935                                 memcpy(value, data+k->offset, len);
936                         } else {
937                                 len = 0;
938                                 value = NULL;
939                         }
940                 } else
941                         ret = NC_ERR_TYPE;
942         } else
943                 ret = NC_ERR_NEXIST;
944         unLockFile();
945         return ret;
946 }
947
948 void NConfig::lockFile(int type, int force)
949 {
950 #ifdef NC_DEBUG_LOCK
951         fprintf(stderr, "Lock called type=%d force=%d lock=%d olck=%u\n", type, force, lock, olck);
952 #endif
953         if (lock == NC_L_RO && type == NC_L_RW) {
954                 fprintf(stderr, "Lock promotion is not possible.\n");
955                 abort();
956         }
957         if (lock != NC_L_NONE) {
958                 olck++;
959                 return;
960         }
961         
962         struct flock flc = { type == NC_L_RW ? F_WRLCK : F_RDLCK, SEEK_SET, 0, 0, 0 };
963         while (fcntl(fd, F_SETLKW, &flc)) {
964                 sched_yield();
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;
968         }
969
970 #ifdef NC_DEBUG_LOCK
971         fprintf(stderr, "Locked %u %u %s\n", sb->modtime, update, force ? "forced." : "");
972 #endif
973         if (careful && type == NC_L_RW)
974                 mprotect(data, sb->size, PROT_READ | PROT_WRITE);
975         lock = type;
976         olck = 0;
977         if (sb->modtime != update || force) {
978                 // refresh memory mapping
979                 if (lsize != sb->size) {
980                         _remap(lsize, sb->size);
981                         lsize = sb->size;
982                         chunks = CM(sb->chunk);
983                 }
984                 cdir = getDirEnt(cname);
985                 update = sb->modtime;
986         }
987 }
988
989 void NConfig::unLockFile()
990 {
991 #ifdef NC_DEBUG_LOCK
992         fprintf(stderr, "UnLock called lock=%u olck=%u\n", lock, olck);
993 #endif
994         if (olck) {
995                 olck--;
996                 return;
997         }
998         if (lock == NC_L_NONE)
999                 return;
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);
1004 #endif
1005         if (careful)
1006                 mprotect(data, sb->size, PROT_READ);
1007         fcntl(fd, F_SETLK, &flc);
1008         lock = NC_L_NONE;
1009         olck = 0;
1010 }
1011
1012 void NConfig::_remap(const size_t osize, const size_t nsize)
1013 {
1014         data = (char *) mremap(data, osize, nsize, 1);
1015         if (data == MAP_FAILED) {
1016                 perror("mremap");
1017                 abort();
1018         }
1019         sb = SB;
1020         rdir = DE(sb->root);
1021 }
1022
1023 char * NConfig::version()
1024 {
1025         return strdup("EliteDVB registry");
1026 }
1027