add changelog
[platform/upstream/gdbm.git] / src / gdbmload.c
1 /* This file is part of GDBM, the GNU data base manager.
2    Copyright (C) 2011, 2013 Free Software Foundation, Inc.
3
4    GDBM is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8
9    GDBM is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with GDBM. If not, see <http://www.gnu.org/licenses/>.   */
16
17 # include "autoconf.h"
18 # include "gdbmdefs.h"
19 # include "gdbm.h"
20 # include <sys/types.h>
21 # include <pwd.h>
22 # include <grp.h>
23
24 struct datbuf
25 {
26   unsigned char *buffer;
27   size_t size;
28 };
29
30 struct dump_file
31 {
32   FILE *fp;
33   size_t line;
34
35   char *linebuf;
36   size_t lbsize;
37   size_t lblevel;
38   
39   char *buffer;
40   size_t bufsize;
41   size_t buflevel;
42
43   size_t parmc;
44
45   struct datbuf data[2];
46   char *header;
47 };
48
49 static void
50 dump_file_free (struct dump_file *file)
51 {
52   free (file->linebuf);
53   free (file->buffer);
54   free (file->data[0].buffer);
55   free (file->data[1].buffer);
56   free (file->header);
57 }
58
59 static const char *
60 getparm (const char *buf, const char *parm)
61 {
62   if (!buf)
63     return NULL;
64   while (*buf)
65     {
66       const char *p;
67       for (p = parm; *p == *buf; p++, buf++)
68         ;
69       if (*p == 0 && *buf == '=')
70         return buf + 1;
71       buf += strlen (buf) + 1;
72     }
73   return NULL;
74 }
75
76 static size_t
77 get_dump_line (struct dump_file *file)
78 {
79   char buf[80];
80   
81   if (file->lblevel == 0)
82     {
83       while (fgets (buf, sizeof buf, file->fp))
84         {
85           size_t n = strlen (buf);
86           
87           if (buf[n-1] == '\n')
88             {
89               file->line++;
90               --n;
91             }
92           
93           if (n + 1 + file->lblevel > file->lbsize)
94             {
95               size_t s = ((file->lblevel + n + _GDBM_MAX_DUMP_LINE_LEN)
96                           / _GDBM_MAX_DUMP_LINE_LEN)
97                           * _GDBM_MAX_DUMP_LINE_LEN;
98               char *newp = realloc (file->linebuf, s);
99               if (!newp)
100                 return GDBM_MALLOC_ERROR;
101               file->linebuf = newp;
102               file->lbsize = s;
103             }
104           
105           memcpy (file->linebuf + file->lblevel, buf, n);
106           file->lblevel += n;
107           if (buf[n])
108             {
109               file->linebuf[file->lblevel] = 0;
110               break;
111             }
112         }
113     }
114   return file->lblevel;
115 }
116
117 static int
118 get_data (struct dump_file *file)
119 {
120   size_t n;
121
122   file->buflevel = 0;
123   file->parmc = 0;
124   
125   while ((n = get_dump_line (file)))
126     {
127       if (file->linebuf[0] == '#')
128         return 0;
129       if (n + file->buflevel > file->bufsize)
130         {
131           size_t s = ((file->buflevel + n + _GDBM_MAX_DUMP_LINE_LEN - 1)
132                       / _GDBM_MAX_DUMP_LINE_LEN)
133                       * _GDBM_MAX_DUMP_LINE_LEN;
134           char *newp = realloc (file->buffer, s);
135           if (!newp)
136             return GDBM_MALLOC_ERROR;
137           file->buffer = newp;
138           file->bufsize = s;
139         }
140       memcpy (file->buffer + file->buflevel, file->linebuf, n);
141       file->buflevel += n;
142       file->lblevel = 0;
143     }
144   return ferror (file->fp) ? GDBM_FILE_READ_ERROR : 0;
145 }
146
147 static int
148 get_parms (struct dump_file *file)
149 {
150   size_t n;
151
152   file->buflevel = 0;
153   file->parmc = 0;
154   while ((n = get_dump_line (file)))
155     {
156       char *p;
157
158       p = file->linebuf;
159       if (*p != '#')
160         return 0;
161       if (n == 0 || *++p != ':')
162         {
163           file->lblevel = 0;
164           continue;
165         }
166       if (--n == 0)
167         {
168           file->lblevel = 0;
169           continue;
170         }
171       
172       if (n + 1 + file->buflevel > file->bufsize)
173         {
174           size_t s = ((file->buflevel + n + _GDBM_MAX_DUMP_LINE_LEN)
175                       / _GDBM_MAX_DUMP_LINE_LEN)
176                       * _GDBM_MAX_DUMP_LINE_LEN;
177           char *newp = realloc (file->buffer, s);
178           if (!newp)
179             return GDBM_MALLOC_ERROR;
180           file->buffer = newp;
181           file->bufsize = s;
182         }
183
184       while (*p)
185         {
186           p++;
187           while (*p == ' ' || *p == '\t')
188             p++;
189           if (*p)
190             {
191               while (*p && *p != '=')
192                 file->buffer[file->buflevel++] = *p++;
193               
194               if (*p == '=')
195                 {
196                   file->buffer[file->buflevel++] = *p++;
197                   if (*p == '"')
198                     {
199                       p++;
200                       while (*p && *p != '"')
201                         file->buffer[file->buflevel++] = *p++;
202
203                       if (*p == '"')
204                         p++;
205                     }
206                   else
207                     {
208                       while (!(*p == 0 || *p == ','))
209                         file->buffer[file->buflevel++] = *p++;
210                     }
211                   file->parmc++;
212                   file->buffer[file->buflevel++] = 0;
213                 }
214               else
215                 return GDBM_ILLEGAL_DATA;
216             }
217           else
218             break;
219         }
220       file->lblevel = 0;
221     }
222
223   file->buffer[file->buflevel] = 0;
224   
225   return ferror (file->fp) ? GDBM_FILE_READ_ERROR : 0;
226 }
227
228 int
229 get_len (const char *param, size_t *plen)
230 {
231   unsigned long n;
232   const char *p = getparm (param, "len");
233   char *end;
234   
235   if (!p)
236     return GDBM_ITEM_NOT_FOUND;
237
238   errno = 0;
239   n = strtoul (p, &end, 10);
240   if (*end == 0 && errno == 0)
241     {
242       *plen = n;
243       return 0;
244     }
245
246   return GDBM_ILLEGAL_DATA;
247 }
248
249 int
250 read_record (struct dump_file *file, char *param, int n, datum *dat)
251 {
252   int rc;
253   size_t len, consumed_size, decoded_size;
254
255   if (!param)
256     {
257       rc = get_parms (file);
258       if (rc)
259         return rc;
260       if (file->parmc == 0)
261         return GDBM_ITEM_NOT_FOUND;
262       param = file->buffer;
263     }
264   rc = get_len (param, &len);
265   if (rc)
266     return rc;
267   dat->dsize = len; /* FIXME: data type mismatch */
268   rc = get_data (file);
269   if (rc)
270     return rc;
271
272   rc = _gdbm_base64_decode ((unsigned char *)file->buffer, file->buflevel,
273                             &file->data[n].buffer, &file->data[n].size,
274                             &consumed_size, &decoded_size);
275   if (rc)
276     return rc;
277   if (consumed_size != file->buflevel || decoded_size != len)
278     return GDBM_ILLEGAL_DATA;
279   dat->dptr = (void*) file->data[n].buffer;
280   return 0;
281 }
282
283 #define META_UID  0x01
284 #define META_GID  0x02
285 #define META_MODE 0x04
286
287 static int
288 _set_gdbm_meta_info (GDBM_FILE dbf, char *param, int meta_mask)
289 {
290   unsigned long n;
291   uid_t owner_uid;
292   uid_t owner_gid;
293   mode_t mode;
294   int meta_flags = 0;
295   const char *p;
296   char *end;
297   int rc = 0;
298
299   if (!(meta_mask & GDBM_META_MASK_OWNER))
300     {
301       p = getparm (param, "user");
302       if (p)
303         {
304           struct passwd *pw = getpwnam (p);
305           if (pw)
306             {
307               owner_uid = pw->pw_uid;
308               meta_flags |= META_UID;
309             }
310         }
311
312       if (!(meta_flags & META_UID) && (p = getparm (param, "uid")))
313         {
314           errno = 0;
315           n = strtoul (p, &end, 10);
316           if (*end == 0 && errno == 0)
317             {
318               owner_uid = n;
319               meta_flags |= META_UID;
320             }
321         }
322
323       p = getparm (param, "group");
324       if (p)
325         {
326           struct group *gr = getgrnam (p);
327           if (gr)
328             {
329               owner_gid = gr->gr_gid;
330               meta_flags |= META_GID;
331             }
332         }
333       if (!(meta_flags & META_GID) && (p = getparm (param, "gid")))
334         {
335           errno = 0;
336           n = strtoul (p, &end, 10);
337           if (*end == 0 && errno == 0)
338             {
339               owner_gid = n;
340               meta_flags |= META_GID;
341             }
342         }
343     }
344   
345   if (!(meta_mask & GDBM_META_MASK_MODE))
346     {
347       p = getparm (param, "mode");
348       if (p)
349         {
350           errno = 0;
351           n = strtoul (p, &end, 8);
352           if (*end == 0 && errno == 0)
353             {
354               mode = n & 0777;
355               meta_flags |= META_MODE;
356             }
357         }
358     }
359   
360   if (meta_flags)
361     {
362       int fd = gdbm_fdesc (dbf);
363       if (getuid () == 0 && (meta_flags & (META_UID|META_GID)))
364         {
365           if ((meta_flags & (META_UID|META_GID)) != (META_UID|META_GID))
366             {
367               struct stat st;
368               fstat (fd, &st);
369               if (!(meta_flags & META_UID))
370                 owner_uid = st.st_uid;
371               if (!(meta_flags & META_GID))
372                 owner_gid = st.st_gid;
373             }
374           if (fchown (fd, owner_uid, owner_gid))
375             {
376               gdbm_errno = GDBM_ERR_FILE_OWNER;
377               rc = 1;
378             }
379         }
380       if ((meta_flags & META_MODE) && fchmod (fd, mode))
381         {
382           gdbm_errno = GDBM_ERR_FILE_OWNER;
383           rc = 1;
384         }
385     }
386   return rc;
387 }
388
389 int
390 _gdbm_load_file (struct dump_file *file, GDBM_FILE dbf, GDBM_FILE *ofp,
391                  int replace, int meta_mask)
392 {
393   char *param = NULL;
394   int rc;
395   GDBM_FILE tmp = NULL;
396   
397   rc = get_parms (file);
398   if (rc)
399     return rc;
400   
401   if (file->parmc)
402     {
403       file->header = file->buffer;
404       file->buffer = NULL;
405       file->bufsize = file->buflevel = 0;
406     }
407   else
408     return GDBM_ILLEGAL_DATA;
409
410   if (!dbf)
411     {
412       const char *filename = getparm (file->header, "file");
413       if (!filename)
414         return GDBM_NO_DBNAME;
415       tmp = gdbm_open (filename, 0,
416                        replace ? GDBM_WRCREAT : GDBM_NEWDB, 0600, NULL);
417       if (!tmp)
418         return gdbm_errno;
419       dbf = tmp;
420     }
421   
422   param = file->header;
423   while (1)
424     {
425       datum key, content;
426       rc = read_record (file, param, 0, &key);
427       if (rc)
428         {
429           if (rc == GDBM_ITEM_NOT_FOUND && feof (file->fp))
430             rc = 0;
431           break;
432         }
433       param = NULL;
434
435       rc = read_record (file, NULL, 1, &content);
436       if (rc)
437         break;
438       
439       if (gdbm_store (dbf, key, content, replace))
440         {
441           rc = gdbm_errno;
442           break;
443         }
444     }
445
446   if (rc == 0)
447     {
448       rc = _set_gdbm_meta_info (dbf, file->header, meta_mask);
449       *ofp = dbf;
450     }
451   else if (tmp)
452     gdbm_close (tmp);
453     
454   return rc;
455 }
456
457 static int
458 read_bdb_header (struct dump_file *file)
459 {    
460   char buf[256];
461   
462   file->line = 1;
463   if (!fgets (buf, sizeof (buf), file->fp))
464     return -1;
465   if (strcmp (buf, "VERSION=3\n"))
466     return -1;
467   while (fgets (buf, sizeof (buf), file->fp))
468     {
469       ++file->line;
470       if (strcmp (buf, "HEADER=END\n") == 0)
471         return 0;
472     }
473   return -1;
474 }
475
476 static int
477 c2x (int c)
478 {
479   static char xdig[] = "0123456789abcdef";
480   char *p = strchr (xdig, c);
481   if (!p)
482     return -1;
483   return p - xdig;
484
485
486 #define DINCR 128
487
488 static int
489 xdatum_read (FILE *fp, datum *d, size_t *pdmax)
490 {
491   int c;
492   size_t dmax = *pdmax;
493   
494   d->dsize = 0;
495   while ((c = fgetc (fp)) != EOF && c != '\n')
496     {
497       int t, n;
498       
499       t = c2x (c);
500       if (t == -1)
501         return EOF;
502       t <<= 4;
503
504       if ((c = fgetc (fp)) == EOF)
505         break;
506     
507       n = c2x (c);
508       if (n == -1)
509         return EOF;
510       t += n;
511
512       if (d->dsize == dmax)
513         {
514           char *np = realloc (d->dptr, dmax + DINCR);
515           if (!np)
516             return GDBM_MALLOC_ERROR;
517           d->dptr = np;
518           dmax += DINCR;
519         }
520       d->dptr[d->dsize++] = t;
521     }
522   *pdmax = dmax;
523   if (c == '\n')
524     return 0;
525   return c;
526 }
527
528 int
529 gdbm_load_bdb_dump (struct dump_file *file, GDBM_FILE dbf, int replace)
530 {
531   datum xd[2];
532   size_t xs[2];
533   int rc, c;
534   int i;
535   
536   if (read_bdb_header (file))
537     return -1;
538   memset (&xd, 0, sizeof (xd));
539   xs[0] = xs[1] = 0;
540   i = 0;
541   while ((c = fgetc (file->fp)) == ' ')
542     {
543       rc = xdatum_read (file->fp, &xd[i], &xs[i]);
544       if (rc)
545         break;
546       ++file->line;
547
548       if (i == 1)
549         {
550           if (gdbm_store (dbf, xd[0], xd[1], replace))
551             return gdbm_errno;
552         }
553       i = !i;
554     }
555   //FIXME: Read "DATA=END"
556   free (xd[0].dptr);
557   free (xd[1].dptr);
558   if (rc == 0 && i)
559     rc = EOF;
560     
561   return rc;
562 }
563
564 int
565 gdbm_load_from_file (GDBM_FILE *pdbf, FILE *fp, int replace,
566                      int meta_mask,
567                      unsigned long *line)
568 {
569   struct dump_file df;
570   int rc;
571
572   if (!pdbf || !fp)
573     return EINVAL;
574
575   /* Guess input file format */
576   rc = fgetc (fp);
577   ungetc (rc, fp);
578   if (rc == '!')
579     {
580       if (line)
581         *line = 0;
582       if (!*pdbf)
583         {
584           gdbm_errno = GDBM_NO_DBNAME;
585           return -1;
586         }
587       if (gdbm_import_from_file (*pdbf, fp, replace) == -1)
588         return -1;
589       return 0;
590     }
591
592   memset (&df, 0, sizeof df);
593   df.fp = fp;
594
595   if (rc == 'V')
596     {
597       if (!*pdbf)
598         {
599           gdbm_errno = GDBM_NO_DBNAME;
600           return -1;
601         }
602       rc = gdbm_load_bdb_dump (&df, *pdbf, replace);
603     }
604   else
605     rc = _gdbm_load_file (&df, *pdbf, pdbf, replace, meta_mask);
606   dump_file_free (&df);
607   if (rc)
608     {
609       if (line)
610         *line = df.line;
611       gdbm_errno = rc;
612       return -1;
613     }
614   return 0;
615 }
616
617 int
618 gdbm_load (GDBM_FILE *pdbf, const char *filename, int replace,
619            int meta_mask,
620            unsigned long *line)
621 {
622   FILE *fp;
623   int rc;
624   
625   fp = fopen (filename, "r");
626   if (!fp)
627     {
628       gdbm_errno = GDBM_FILE_OPEN_ERROR;
629       return -1;
630     }
631   rc = gdbm_load_from_file (pdbf, fp, replace, meta_mask, line);
632   fclose (fp);
633   return rc;
634 }
635