summaryrefslogtreecommitdiff
path: root/src/gdbmload.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gdbmload.c')
-rw-r--r--src/gdbmload.c635
1 files changed, 635 insertions, 0 deletions
diff --git a/src/gdbmload.c b/src/gdbmload.c
new file mode 100644
index 0000000..19ee0fb
--- /dev/null
+++ b/src/gdbmload.c
@@ -0,0 +1,635 @@
+/* This file is part of GDBM, the GNU data base manager.
+ Copyright (C) 2011, 2013 Free Software Foundation, Inc.
+
+ GDBM is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GDBM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
+
+# include "autoconf.h"
+# include "gdbmdefs.h"
+# include "gdbm.h"
+# include <sys/types.h>
+# include <pwd.h>
+# include <grp.h>
+
+struct datbuf
+{
+ unsigned char *buffer;
+ size_t size;
+};
+
+struct dump_file
+{
+ FILE *fp;
+ size_t line;
+
+ char *linebuf;
+ size_t lbsize;
+ size_t lblevel;
+
+ char *buffer;
+ size_t bufsize;
+ size_t buflevel;
+
+ size_t parmc;
+
+ struct datbuf data[2];
+ char *header;
+};
+
+static void
+dump_file_free (struct dump_file *file)
+{
+ free (file->linebuf);
+ free (file->buffer);
+ free (file->data[0].buffer);
+ free (file->data[1].buffer);
+ free (file->header);
+}
+
+static const char *
+getparm (const char *buf, const char *parm)
+{
+ if (!buf)
+ return NULL;
+ while (*buf)
+ {
+ const char *p;
+ for (p = parm; *p == *buf; p++, buf++)
+ ;
+ if (*p == 0 && *buf == '=')
+ return buf + 1;
+ buf += strlen (buf) + 1;
+ }
+ return NULL;
+}
+
+static size_t
+get_dump_line (struct dump_file *file)
+{
+ char buf[80];
+
+ if (file->lblevel == 0)
+ {
+ while (fgets (buf, sizeof buf, file->fp))
+ {
+ size_t n = strlen (buf);
+
+ if (buf[n-1] == '\n')
+ {
+ file->line++;
+ --n;
+ }
+
+ if (n + 1 + file->lblevel > file->lbsize)
+ {
+ size_t s = ((file->lblevel + n + _GDBM_MAX_DUMP_LINE_LEN)
+ / _GDBM_MAX_DUMP_LINE_LEN)
+ * _GDBM_MAX_DUMP_LINE_LEN;
+ char *newp = realloc (file->linebuf, s);
+ if (!newp)
+ return GDBM_MALLOC_ERROR;
+ file->linebuf = newp;
+ file->lbsize = s;
+ }
+
+ memcpy (file->linebuf + file->lblevel, buf, n);
+ file->lblevel += n;
+ if (buf[n])
+ {
+ file->linebuf[file->lblevel] = 0;
+ break;
+ }
+ }
+ }
+ return file->lblevel;
+}
+
+static int
+get_data (struct dump_file *file)
+{
+ size_t n;
+
+ file->buflevel = 0;
+ file->parmc = 0;
+
+ while ((n = get_dump_line (file)))
+ {
+ if (file->linebuf[0] == '#')
+ return 0;
+ if (n + file->buflevel > file->bufsize)
+ {
+ size_t s = ((file->buflevel + n + _GDBM_MAX_DUMP_LINE_LEN - 1)
+ / _GDBM_MAX_DUMP_LINE_LEN)
+ * _GDBM_MAX_DUMP_LINE_LEN;
+ char *newp = realloc (file->buffer, s);
+ if (!newp)
+ return GDBM_MALLOC_ERROR;
+ file->buffer = newp;
+ file->bufsize = s;
+ }
+ memcpy (file->buffer + file->buflevel, file->linebuf, n);
+ file->buflevel += n;
+ file->lblevel = 0;
+ }
+ return ferror (file->fp) ? GDBM_FILE_READ_ERROR : 0;
+}
+
+static int
+get_parms (struct dump_file *file)
+{
+ size_t n;
+
+ file->buflevel = 0;
+ file->parmc = 0;
+ while ((n = get_dump_line (file)))
+ {
+ char *p;
+
+ p = file->linebuf;
+ if (*p != '#')
+ return 0;
+ if (n == 0 || *++p != ':')
+ {
+ file->lblevel = 0;
+ continue;
+ }
+ if (--n == 0)
+ {
+ file->lblevel = 0;
+ continue;
+ }
+
+ if (n + 1 + file->buflevel > file->bufsize)
+ {
+ size_t s = ((file->buflevel + n + _GDBM_MAX_DUMP_LINE_LEN)
+ / _GDBM_MAX_DUMP_LINE_LEN)
+ * _GDBM_MAX_DUMP_LINE_LEN;
+ char *newp = realloc (file->buffer, s);
+ if (!newp)
+ return GDBM_MALLOC_ERROR;
+ file->buffer = newp;
+ file->bufsize = s;
+ }
+
+ while (*p)
+ {
+ p++;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p)
+ {
+ while (*p && *p != '=')
+ file->buffer[file->buflevel++] = *p++;
+
+ if (*p == '=')
+ {
+ file->buffer[file->buflevel++] = *p++;
+ if (*p == '"')
+ {
+ p++;
+ while (*p && *p != '"')
+ file->buffer[file->buflevel++] = *p++;
+
+ if (*p == '"')
+ p++;
+ }
+ else
+ {
+ while (!(*p == 0 || *p == ','))
+ file->buffer[file->buflevel++] = *p++;
+ }
+ file->parmc++;
+ file->buffer[file->buflevel++] = 0;
+ }
+ else
+ return GDBM_ILLEGAL_DATA;
+ }
+ else
+ break;
+ }
+ file->lblevel = 0;
+ }
+
+ file->buffer[file->buflevel] = 0;
+
+ return ferror (file->fp) ? GDBM_FILE_READ_ERROR : 0;
+}
+
+int
+get_len (const char *param, size_t *plen)
+{
+ unsigned long n;
+ const char *p = getparm (param, "len");
+ char *end;
+
+ if (!p)
+ return GDBM_ITEM_NOT_FOUND;
+
+ errno = 0;
+ n = strtoul (p, &end, 10);
+ if (*end == 0 && errno == 0)
+ {
+ *plen = n;
+ return 0;
+ }
+
+ return GDBM_ILLEGAL_DATA;
+}
+
+int
+read_record (struct dump_file *file, char *param, int n, datum *dat)
+{
+ int rc;
+ size_t len, consumed_size, decoded_size;
+
+ if (!param)
+ {
+ rc = get_parms (file);
+ if (rc)
+ return rc;
+ if (file->parmc == 0)
+ return GDBM_ITEM_NOT_FOUND;
+ param = file->buffer;
+ }
+ rc = get_len (param, &len);
+ if (rc)
+ return rc;
+ dat->dsize = len; /* FIXME: data type mismatch */
+ rc = get_data (file);
+ if (rc)
+ return rc;
+
+ rc = _gdbm_base64_decode ((unsigned char *)file->buffer, file->buflevel,
+ &file->data[n].buffer, &file->data[n].size,
+ &consumed_size, &decoded_size);
+ if (rc)
+ return rc;
+ if (consumed_size != file->buflevel || decoded_size != len)
+ return GDBM_ILLEGAL_DATA;
+ dat->dptr = (void*) file->data[n].buffer;
+ return 0;
+}
+
+#define META_UID 0x01
+#define META_GID 0x02
+#define META_MODE 0x04
+
+static int
+_set_gdbm_meta_info (GDBM_FILE dbf, char *param, int meta_mask)
+{
+ unsigned long n;
+ uid_t owner_uid;
+ uid_t owner_gid;
+ mode_t mode;
+ int meta_flags = 0;
+ const char *p;
+ char *end;
+ int rc = 0;
+
+ if (!(meta_mask & GDBM_META_MASK_OWNER))
+ {
+ p = getparm (param, "user");
+ if (p)
+ {
+ struct passwd *pw = getpwnam (p);
+ if (pw)
+ {
+ owner_uid = pw->pw_uid;
+ meta_flags |= META_UID;
+ }
+ }
+
+ if (!(meta_flags & META_UID) && (p = getparm (param, "uid")))
+ {
+ errno = 0;
+ n = strtoul (p, &end, 10);
+ if (*end == 0 && errno == 0)
+ {
+ owner_uid = n;
+ meta_flags |= META_UID;
+ }
+ }
+
+ p = getparm (param, "group");
+ if (p)
+ {
+ struct group *gr = getgrnam (p);
+ if (gr)
+ {
+ owner_gid = gr->gr_gid;
+ meta_flags |= META_GID;
+ }
+ }
+ if (!(meta_flags & META_GID) && (p = getparm (param, "gid")))
+ {
+ errno = 0;
+ n = strtoul (p, &end, 10);
+ if (*end == 0 && errno == 0)
+ {
+ owner_gid = n;
+ meta_flags |= META_GID;
+ }
+ }
+ }
+
+ if (!(meta_mask & GDBM_META_MASK_MODE))
+ {
+ p = getparm (param, "mode");
+ if (p)
+ {
+ errno = 0;
+ n = strtoul (p, &end, 8);
+ if (*end == 0 && errno == 0)
+ {
+ mode = n & 0777;
+ meta_flags |= META_MODE;
+ }
+ }
+ }
+
+ if (meta_flags)
+ {
+ int fd = gdbm_fdesc (dbf);
+ if (getuid () == 0 && (meta_flags & (META_UID|META_GID)))
+ {
+ if ((meta_flags & (META_UID|META_GID)) != (META_UID|META_GID))
+ {
+ struct stat st;
+ fstat (fd, &st);
+ if (!(meta_flags & META_UID))
+ owner_uid = st.st_uid;
+ if (!(meta_flags & META_GID))
+ owner_gid = st.st_gid;
+ }
+ if (fchown (fd, owner_uid, owner_gid))
+ {
+ gdbm_errno = GDBM_ERR_FILE_OWNER;
+ rc = 1;
+ }
+ }
+ if ((meta_flags & META_MODE) && fchmod (fd, mode))
+ {
+ gdbm_errno = GDBM_ERR_FILE_OWNER;
+ rc = 1;
+ }
+ }
+ return rc;
+}
+
+int
+_gdbm_load_file (struct dump_file *file, GDBM_FILE dbf, GDBM_FILE *ofp,
+ int replace, int meta_mask)
+{
+ char *param = NULL;
+ int rc;
+ GDBM_FILE tmp = NULL;
+
+ rc = get_parms (file);
+ if (rc)
+ return rc;
+
+ if (file->parmc)
+ {
+ file->header = file->buffer;
+ file->buffer = NULL;
+ file->bufsize = file->buflevel = 0;
+ }
+ else
+ return GDBM_ILLEGAL_DATA;
+
+ if (!dbf)
+ {
+ const char *filename = getparm (file->header, "file");
+ if (!filename)
+ return GDBM_NO_DBNAME;
+ tmp = gdbm_open (filename, 0,
+ replace ? GDBM_WRCREAT : GDBM_NEWDB, 0600, NULL);
+ if (!tmp)
+ return gdbm_errno;
+ dbf = tmp;
+ }
+
+ param = file->header;
+ while (1)
+ {
+ datum key, content;
+ rc = read_record (file, param, 0, &key);
+ if (rc)
+ {
+ if (rc == GDBM_ITEM_NOT_FOUND && feof (file->fp))
+ rc = 0;
+ break;
+ }
+ param = NULL;
+
+ rc = read_record (file, NULL, 1, &content);
+ if (rc)
+ break;
+
+ if (gdbm_store (dbf, key, content, replace))
+ {
+ rc = gdbm_errno;
+ break;
+ }
+ }
+
+ if (rc == 0)
+ {
+ rc = _set_gdbm_meta_info (dbf, file->header, meta_mask);
+ *ofp = dbf;
+ }
+ else if (tmp)
+ gdbm_close (tmp);
+
+ return rc;
+}
+
+static int
+read_bdb_header (struct dump_file *file)
+{
+ char buf[256];
+
+ file->line = 1;
+ if (!fgets (buf, sizeof (buf), file->fp))
+ return -1;
+ if (strcmp (buf, "VERSION=3\n"))
+ return -1;
+ while (fgets (buf, sizeof (buf), file->fp))
+ {
+ ++file->line;
+ if (strcmp (buf, "HEADER=END\n") == 0)
+ return 0;
+ }
+ return -1;
+}
+
+static int
+c2x (int c)
+{
+ static char xdig[] = "0123456789abcdef";
+ char *p = strchr (xdig, c);
+ if (!p)
+ return -1;
+ return p - xdig;
+}
+
+#define DINCR 128
+
+static int
+xdatum_read (FILE *fp, datum *d, size_t *pdmax)
+{
+ int c;
+ size_t dmax = *pdmax;
+
+ d->dsize = 0;
+ while ((c = fgetc (fp)) != EOF && c != '\n')
+ {
+ int t, n;
+
+ t = c2x (c);
+ if (t == -1)
+ return EOF;
+ t <<= 4;
+
+ if ((c = fgetc (fp)) == EOF)
+ break;
+
+ n = c2x (c);
+ if (n == -1)
+ return EOF;
+ t += n;
+
+ if (d->dsize == dmax)
+ {
+ char *np = realloc (d->dptr, dmax + DINCR);
+ if (!np)
+ return GDBM_MALLOC_ERROR;
+ d->dptr = np;
+ dmax += DINCR;
+ }
+ d->dptr[d->dsize++] = t;
+ }
+ *pdmax = dmax;
+ if (c == '\n')
+ return 0;
+ return c;
+}
+
+int
+gdbm_load_bdb_dump (struct dump_file *file, GDBM_FILE dbf, int replace)
+{
+ datum xd[2];
+ size_t xs[2];
+ int rc, c;
+ int i;
+
+ if (read_bdb_header (file))
+ return -1;
+ memset (&xd, 0, sizeof (xd));
+ xs[0] = xs[1] = 0;
+ i = 0;
+ while ((c = fgetc (file->fp)) == ' ')
+ {
+ rc = xdatum_read (file->fp, &xd[i], &xs[i]);
+ if (rc)
+ break;
+ ++file->line;
+
+ if (i == 1)
+ {
+ if (gdbm_store (dbf, xd[0], xd[1], replace))
+ return gdbm_errno;
+ }
+ i = !i;
+ }
+ //FIXME: Read "DATA=END"
+ free (xd[0].dptr);
+ free (xd[1].dptr);
+ if (rc == 0 && i)
+ rc = EOF;
+
+ return rc;
+}
+
+int
+gdbm_load_from_file (GDBM_FILE *pdbf, FILE *fp, int replace,
+ int meta_mask,
+ unsigned long *line)
+{
+ struct dump_file df;
+ int rc;
+
+ if (!pdbf || !fp)
+ return EINVAL;
+
+ /* Guess input file format */
+ rc = fgetc (fp);
+ ungetc (rc, fp);
+ if (rc == '!')
+ {
+ if (line)
+ *line = 0;
+ if (!*pdbf)
+ {
+ gdbm_errno = GDBM_NO_DBNAME;
+ return -1;
+ }
+ if (gdbm_import_from_file (*pdbf, fp, replace) == -1)
+ return -1;
+ return 0;
+ }
+
+ memset (&df, 0, sizeof df);
+ df.fp = fp;
+
+ if (rc == 'V')
+ {
+ if (!*pdbf)
+ {
+ gdbm_errno = GDBM_NO_DBNAME;
+ return -1;
+ }
+ rc = gdbm_load_bdb_dump (&df, *pdbf, replace);
+ }
+ else
+ rc = _gdbm_load_file (&df, *pdbf, pdbf, replace, meta_mask);
+ dump_file_free (&df);
+ if (rc)
+ {
+ if (line)
+ *line = df.line;
+ gdbm_errno = rc;
+ return -1;
+ }
+ return 0;
+}
+
+int
+gdbm_load (GDBM_FILE *pdbf, const char *filename, int replace,
+ int meta_mask,
+ unsigned long *line)
+{
+ FILE *fp;
+ int rc;
+
+ fp = fopen (filename, "r");
+ if (!fp)
+ {
+ gdbm_errno = GDBM_FILE_OPEN_ERROR;
+ return -1;
+ }
+ rc = gdbm_load_from_file (pdbf, fp, replace, meta_mask, line);
+ fclose (fp);
+ return rc;
+}
+