summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac8
-rw-r--r--src/opk/opk.h7
-rw-r--r--src/opk/write.c372
3 files changed, 373 insertions, 14 deletions
diff --git a/configure.ac b/configure.ac
index 1b6ac90..687629e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,10 +43,10 @@ test -d "${srcdir}/.git" || CFLAGS="${save_CFLAGS}"
funcs_missing=false
AC_CHECK_FUNCS(
[\
- fclose feof ferror fopen fprintf fputs fread free fwrite \
- localtime malloc memcmp memcpy memset mkdir printf puts \
- snprintf sprintf stat strcmp strcpy strftime strlen strncpy \
- strtol
+ fclose feof ferror fopen fprintf fputs fread free fseek fwrite \
+ getgrgid getpwuid localtime lstat malloc memcmp memcpy memset \
+ mkdir printf puts readlink scandir snprintf sprintf stat \
+ strchr strcmp strcpy strftime strlen strncpy strtol unlink
],
[],
[funcs_missing=true])
diff --git a/src/opk/opk.h b/src/opk/opk.h
index afd39b6..28a6b66 100644
--- a/src/opk/opk.h
+++ b/src/opk/opk.h
@@ -42,11 +42,18 @@ struct opkg_opk_opk {
const char *data_dir;
FILE *file;
char file_buffer[8192];
+ uint64_t mtime;
+ const char *file_name;
+ char *outer_uname;
+ char *outer_gname;
struct opkg_opk_gzip *outer_gzip;
struct opkg_opk_ustar *outer_ustar;
struct opkg_opk_gzip *inner_gzip;
struct opkg_opk_ustar *inner_ustar;
int previously_printed;
+ size_t path_len;
+ char *path;
+ char *temp_file_name;
};
#endif /* OPKG_OPK_OPK_OPK_H_ */
diff --git a/src/opk/write.c b/src/opk/write.c
index 9c710f6..2ef052f 100644
--- a/src/opk/write.c
+++ b/src/opk/write.c
@@ -17,11 +17,15 @@
* along with opkg-opkg. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <dirent.h>
#include <grp.h>
#include <pwd.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "../defs.h"
#include "../dirent.h"
#include "../gzip.h"
@@ -30,6 +34,15 @@
#include "../ustar.h"
#include "opk.h"
+enum _opkg_opk_opk_archive_type {
+ OPKG_OPK_OPK_ARCHIVE_TYPE_CONTROL_ = 0,
+ OPKG_OPK_OPK_ARCHIVE_TYPE_DATA_ = 1,
+};
+static const char *OPKG_OPK_OPK_ARCHIVE_NAMES_[] = {
+ "control.tar.gz",
+ "data.tar.gz",
+};
+
static int
_opkg_opk_opk_source_date_epoch(uint64_t *mtime)
{
@@ -47,26 +60,329 @@ _opkg_opk_opk_source_date_epoch(uint64_t *mtime)
return OPKG_OPK_OK;
}
+static int
+_opkg_opk_opk_write_filter(const struct dirent *de)
+{
+ if (de->d_name[0] == '.') {
+ if (de->d_name[1] == '\0') {
+ return 0;
+ }
+ if (de->d_name[1] == '.' && de->d_name[2] == '\0') {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+_opkg_opk_opk_write_compar(const struct dirent **a, const struct dirent **b)
+{
+ return strcmp((*a)->d_name, (*b)->d_name);
+}
+
+/* path_len excludes '\0' */
+static int
+_opkg_opk_opk_write_dir_read(struct opkg_opk_opk *opk,
+ struct opkg_opk_dirent *dir)
+{
+ int ret;
+ char *child_path;
+ int n;
+ struct dirent **names;
+ int i;
+ struct opkg_opk_dirent child;
+ size_t j;
+ struct stat stat;
+ uid_t uid;
+ char *uname;
+ gid_t gid;
+ char *gname;
+ /* Extra byte (in macro definition) to detect
+ * overflow, not for NUL */
+ char link[OPKG_OPK_USTAR_LINKNAME_SIZE];
+ ssize_t link_len;
+ FILE *fp;
+ char *buffer;
+ size_t size;
+ size_t num_read;
+ size_t tot_read;
+
+ ret = OPKG_OPK_OK;
+
+ child_path = strchr(opk->path, '\0');
+ if (opk->path_len < 2) {
+ fprintf(stderr, "Error: File name \"%s\" too long", opk->path);
+ ret = OPKG_OPK_ERROR;
+ goto out0;
+ }
+ *child_path = '/';
+ ++child_path;
+ *child_path = '\0';
+ --opk->path_len;
+
+ n = scandir(opk->path, &names, _opkg_opk_opk_write_filter,
+ _opkg_opk_opk_write_compar);
+ for (i = 0; i < n; ++i) {
+ child.name = names[i]->d_name;
+ child.parent = dir;
+ for (j = 0; names[i]->d_name[j] != '\0'; ++j) {
+ if (j == opk->path_len) {
+ fprintf(stderr, "Error: File name \"%s\" too "
+ "long", names[i]->d_name);
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ child_path[j] = names[i]->d_name[j];
+ }
+ child_path[j] = '\0';
+ lstat(opk->path, &stat);
+ /* TODO: Ownership override */
+ uid = stat.st_uid;
+ uname = getpwuid(uid)->pw_name;
+ gid = stat.st_gid;
+ gname = getgrgid(gid)->gr_name;
+ if (S_ISDIR(stat.st_mode)) {
+ if (opkg_opk_ustar_write_header(opk->inner_ustar,
+ &child,
+ stat.st_mode & 0777,
+ uid, uname, gid, gname, 0,
+ opk->mtime, 'd', NULL) !=
+ OPKG_OPK_OK) {
+ fputs(_("Error: Failed to write header\n"),
+ stderr);
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ if (_opkg_opk_opk_write_dir_read(opk, &child) !=
+ OPKG_OPK_OK) {
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ } else if (S_ISLNK(stat.st_mode)) {
+ link_len = readlink(opk->path, link,
+ OPKG_OPK_USTAR_LINKNAME_SIZE);
+ if (link_len < 0) {
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ if (link_len == OPKG_OPK_USTAR_LINKNAME_SIZE) {
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ link[link_len] = '\0';
+ if (opkg_opk_ustar_write_header(opk->inner_ustar,
+ &child, 0777,
+ uid, uname, gid, gname, 0,
+ opk->mtime, 'l', link) !=
+ OPKG_OPK_OK) {
+ fputs(_("Error: Failed to write header\n"),
+ stderr);
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ } else if (S_ISCHR(stat.st_mode)) {
+ /* TODO */
+ } else if (S_ISBLK(stat.st_mode)) {
+ /* TODO */
+ } else if (S_ISFIFO(stat.st_mode)) {
+ /* TODO */
+ } else if (S_ISREG(stat.st_mode)) {
+ if (opkg_opk_ustar_write_header(opk->inner_ustar,
+ &child,
+ stat.st_mode & 0777,
+ uid, uname, gid, gname,
+ stat.st_size,
+ opk->mtime, '-', NULL) !=
+ OPKG_OPK_OK) {
+ fputs(_("Error: Failed to write header\n"),
+ stderr);
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ fp = fopen(opk->path, "rb");
+ if (fp == NULL) {
+ fprintf(stderr, _("Error: Failed to open file "
+ "\"%s\"\n"), opk->path);
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ opkg_opk_ustar_get_buffer(opk->inner_ustar,
+ &buffer, &size);
+ tot_read = 0;
+ while ((num_read = fread(buffer, 1, size, fp)) > 0) {
+ opkg_opk_ustar_write_data(opk->inner_ustar,
+ num_read);
+ tot_read += num_read;
+ }
+ if (ferror(fp) != 0) {
+ fputs(_("Error: Error reading file\n"), stderr);
+ fclose(fp);
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ if (fclose(fp) != 0) {
+ fputs(_("Error: Failed to close file\n"),
+ stderr);
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ if ((uintmax_t) tot_read != (uintmax_t) stat.st_size) {
+ fprintf(stderr, _("Error: Expected %jd bytes "
+ "but read %zu\n"),
+ (intmax_t) stat.st_size,
+ num_read);
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ } else {
+ fprintf(stderr, _("Error: Unknown type of file \"%s\"\n"
+ ), opk->path);
+ ret = OPKG_OPK_ERROR;
+ goto out1;
+ }
+ }
+
+ out1:
+ for (i = 0; i < n; ++i) {
+ free(names[i]);
+ }
+ free(names);
+ out0:
+ return ret;
+}
+
+static int
+_opkg_opk_opk_build_inner_archive(struct opkg_opk_opk *opk,
+ enum _opkg_opk_opk_archive_type archive_type)
+{
+ FILE *fp;
+ struct opkg_opk_dirent dirent;
+ size_t written;
+ char *buffer;
+ size_t size;
+ size_t num_read;
+ size_t tot_read;
+
+ /* Initialize inner gzip compressor. */
+ if (sprintf(opk->temp_file_name, "%s~%s", opk->file_name,
+ OPKG_OPK_OPK_ARCHIVE_NAMES_[archive_type]) <= 0)
+ {
+ goto err0;
+ }
+ fp = fopen(opk->temp_file_name, "w+b");
+ if (fp == NULL) {
+ fprintf(stderr, _("Error: Failed to open file \"%s\"\n"),
+ opk->temp_file_name);
+ goto err0;
+ }
+ opk->inner_gzip = opkg_opk_gzip_init_write(fp);
+ if (opk->inner_gzip == NULL) {
+ fputs(_("Error: Failed to initialize compressor\n"), stderr);
+ goto err1;
+ }
+
+ /* Initialize inner ustar archiver. */
+ opk->inner_ustar = opkg_opk_ustar_init(opk->inner_gzip);
+ if (opk->inner_ustar == NULL) {
+ fputs(_("Error: Failed to initialize archiver\n"), stderr);
+ goto err2;
+ }
+
+ /* Write inner archive to temporary file. */
+ dirent.name = ".";
+ dirent.parent = NULL;
+ if (archive_type == OPKG_OPK_OPK_ARCHIVE_TYPE_CONTROL_) {
+ strcpy(opk->path, opk->control_dir);
+ } else {
+ strcpy(opk->path, opk->data_dir);
+ }
+ if (_opkg_opk_opk_write_dir_read(opk, &dirent) != OPKG_OPK_OK) {
+ goto err3;
+ }
+
+ /* Write trailer. */
+ if (opkg_opk_ustar_write_trailer(opk->inner_ustar) != OPKG_OPK_OK) {
+ fputs(_("Error: Failed to write trailer\n"), stderr);
+ goto err3;
+ }
+
+ /* Finish inner archive. */
+ opkg_opk_ustar_free(opk->inner_ustar);
+ opkg_opk_gzip_finish_write(opk->inner_gzip);
+ written = opkg_opk_gzip_written(opk->inner_gzip);
+ opkg_opk_gzip_free(opk->inner_gzip);
+
+ /* Write header in outer archive. */
+ dirent.name = OPKG_OPK_OPK_ARCHIVE_NAMES_[archive_type];
+ dirent.parent = NULL;
+ if (opkg_opk_ustar_write_header(opk->outer_ustar, &dirent, 0644,
+ 0, opk->outer_uname, 0, opk->outer_gname,
+ written, opk->mtime, '-', NULL) !=
+ OPKG_OPK_OK) {
+ fputs(_("Error: Failed to write header\n"), stderr);
+ goto err1;
+ }
+
+ /* Read temporary file into outer archive. */
+ if (fseek(fp, 0L, SEEK_SET) != 0) {
+ fputs(_("Error: Failed to seek in file\n"), stderr);
+ goto err1;
+ }
+ opkg_opk_ustar_get_buffer(opk->outer_ustar, &buffer, &size);
+ tot_read = 0;
+ while ((num_read = fread(buffer, 1, size, fp)) > 0) {
+ opkg_opk_ustar_write_data(opk->outer_ustar, num_read);
+ tot_read += num_read;
+ }
+ if (ferror(fp) != 0) {
+ fputs(_("Error: Error reading file\n"), stderr);
+ fclose(fp);
+ goto err1;
+ }
+ if (fclose(fp) != 0) {
+ fputs(_("Error: Failed to close file\n"), stderr);
+ goto err1;
+ }
+ unlink(opk->temp_file_name);
+ if (tot_read != written) {
+ fprintf(stderr, _("Error: Wrote %lu bytes but read %lu\n"),
+ written, num_read);
+ goto err1;
+ }
+
+ return OPKG_OPK_OK;
+
+ err3:
+ opkg_opk_ustar_free(opk->inner_ustar);
+ err2:
+ opkg_opk_gzip_free(opk->inner_gzip);
+ err1:
+ fclose(fp);
+ err0:
+ return OPKG_OPK_ERROR;
+}
+
int
opkg_opk_opk_write(struct opkg_opk_opk *opk, const char *file_name)
{
int ret;
- uint64_t mtime;
- char *uname;
- char *gname;
struct opkg_opk_dirent dirent;
char *buffer;
size_t size;
+ size_t control_dir_len;
+ size_t data_dir_len;
ret = OPKG_OPK_OK;
- if (_opkg_opk_opk_source_date_epoch(&mtime) == OPKG_OPK_ERROR) {
+ if (_opkg_opk_opk_source_date_epoch(&opk->mtime) == OPKG_OPK_ERROR) {
fputs(_("Error: Missing or invalid SOURCE_DATE_EPOCH\n"),
stderr);
ret = OPKG_OPK_ERROR;
goto out0;
}
+ opk->file_name = file_name;
+
/* Open outer archive. */
opk->file = fopen(file_name, "wb");
if (opk->file == NULL) {
@@ -92,15 +408,15 @@ opkg_opk_opk_write(struct opkg_opk_opk *opk, const char *file_name)
goto out2;
}
- uname = getpwuid(0)->pw_name;
- gname = getgrgid(0)->gr_name;
+ opk->outer_uname = getpwuid(0)->pw_name;
+ opk->outer_gname = getgrgid(0)->gr_name;
/* Write version file. */
dirent.name = "debian-binary";
dirent.parent = NULL;
if (opkg_opk_ustar_write_header(opk->outer_ustar, &dirent, 0644,
- 0, uname, 0, gname, 4, mtime, '-', NULL) !=
- OPKG_OPK_OK) {
+ 0, opk->outer_uname, 0, opk->outer_gname,
+ 4, opk->mtime, '-', NULL) != OPKG_OPK_OK) {
fputs(_("Error: Failed to write header\n"), stderr);
ret = OPKG_OPK_ERROR;
goto out3;
@@ -113,15 +429,51 @@ opkg_opk_opk_write(struct opkg_opk_opk *opk, const char *file_name)
goto out3;
}
- /* TODO: control.tar.gz and data.tar.gz */
+ /* Allocate control and data file path buffer. */
+ opk->path_len = control_dir_len = strlen(opk->control_dir);
+ data_dir_len = strlen(opk->data_dir);
+ if (data_dir_len > opk->path_len) {
+ opk->path_len = data_dir_len;
+ }
+ opk->path_len += 257;
+ opk->path = malloc(opk->path_len);
+ if (opk->path == NULL) {
+ ret = OPKG_OPK_ERROR;
+ goto out3;
+ }
+
+ /* Allocate temporary archive file name buffer. */
+ opk->temp_file_name =
+ malloc(sizeof(file_name) + sizeof("~control.tar.gz"));
+ if (opk->temp_file_name == NULL) {
+ ret = OPKG_OPK_ERROR;
+ goto out4;
+ }
+
+ if (_opkg_opk_opk_build_inner_archive(opk,
+ OPKG_OPK_OPK_ARCHIVE_TYPE_CONTROL_) !=
+ OPKG_OPK_OK) {
+ ret = OPKG_OPK_ERROR;
+ goto out4;
+ }
+ if (_opkg_opk_opk_build_inner_archive(opk,
+ OPKG_OPK_OPK_ARCHIVE_TYPE_DATA_) !=
+ OPKG_OPK_OK) {
+ ret = OPKG_OPK_ERROR;
+ goto out4;
+ }
/* Write trailer. */
if (opkg_opk_ustar_write_trailer(opk->outer_ustar) != OPKG_OPK_OK) {
fputs(_("Error: Failed to write trailer\n"), stderr);
ret = OPKG_OPK_ERROR;
- goto out3;
+ goto out5;
}
+ out5:
+ free(opk->temp_file_name);
+ out4:
+ free(opk->path);
out3:
opkg_opk_ustar_free(opk->outer_ustar);
out2: