summaryrefslogtreecommitdiff
path: root/src/fs_uvol.c
blob: 24952cf49589f62a16ecbad447f97ee191d435d9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* fsops_uvol.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
 * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
 * SPDX-License-Identifier: GPL-2.0-only
 */

#include <spawn.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include "apk_context.h"
#include "apk_fs.h"

static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char *arg1, char *arg2)
{
	struct apk_out *out = &ac->out;
	pid_t pid;
	int r, status;
	char *argv[] = { (char*)apk_ctx_get_uvol(ac), action, (char*) volname, arg1, arg2, 0 };
	posix_spawn_file_actions_t act;

	posix_spawn_file_actions_init(&act);
	posix_spawn_file_actions_addclose(&act, STDIN_FILENO);
	r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
	posix_spawn_file_actions_destroy(&act);
	if (r != 0) {
		apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
		return r;
	}
	while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
		apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
		return -APKE_UVOL_ERROR;
	}
	return 0;
}

static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz,
	struct apk_istream *is, apk_progress_cb cb, void *cb_ctx)
{
	struct apk_out *out = &ac->out;
	struct apk_ostream *os;
	pid_t pid;
	int r, status, pipefds[2];
	char *argv[] = { (char*)apk_ctx_get_uvol(ac), "write", (char*) volname, arg1, 0 };
	posix_spawn_file_actions_t act;

	if (pipe2(pipefds, O_CLOEXEC) != 0) return -errno;

	posix_spawn_file_actions_init(&act);
	posix_spawn_file_actions_adddup2(&act, pipefds[0], STDIN_FILENO);
	r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
	posix_spawn_file_actions_destroy(&act);
	if (r != 0) {
		apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
		return r;
	}
	close(pipefds[0]);
	os = apk_ostream_to_fd(pipefds[1]);
	apk_stream_copy(is, os, sz, cb, cb_ctx, 0);
	r = apk_ostream_close(os);
	if (r != 0) {
		if (r >= 0) r = -APKE_UVOL_ERROR;
		apk_err(out, "%s: uvol write error: %s", volname, apk_error_str(r));
		return r;
	}

	while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
		apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
		return -APKE_UVOL_ERROR;
	}

	return 0;
}

static int uvol_dir_create(struct apk_fsdir *d, mode_t mode)
{
	return 0;
}

static int uvol_dir_delete(struct apk_fsdir *d)
{
	return 0;
}

static int uvol_dir_check(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
{
	return 0;
}

static int uvol_dir_update_perms(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
{
	return 0;
}

static int uvol_file_extract(struct apk_ctx *ac, const struct apk_file_info *fi, struct apk_istream *is,
	apk_progress_cb cb, void *cb_ctx, unsigned int extract_flags, apk_blob_t pkgctx)
{
	char size[64];
	const char *uvol_name;
	int r;

	if (IS_ERR(ac->uvol)) return PTR_ERR(ac->uvol);

	uvol_name = strrchr(fi->name, '/');
	uvol_name = uvol_name ? uvol_name + 1 : fi->name;

	snprintf(size, sizeof size, "%ju", (intmax_t) fi->size);
	r = uvol_run(ac, "create", uvol_name, size, "ro");
	if (r != 0) return r;

	r = uvol_extract(ac, uvol_name, size, fi->size, is, cb, cb_ctx);
	if (r == 0 && !pkgctx.ptr)
		r = uvol_run(ac, "up", uvol_name, 0, 0);

	if (r != 0) uvol_run(ac, "remove", uvol_name, 0, 0);

	return r;
}

static int uvol_file_control(struct apk_fsdir *d, apk_blob_t filename, int ctrl)
{
	struct apk_ctx *ac = d->ac;
	struct apk_pathbuilder pb;
	const char *uvol_name;
	int r;

	if (IS_ERR(ac->uvol)) return PTR_ERR(ac->uvol);

	apk_pathbuilder_setb(&pb, filename);
	uvol_name = apk_pathbuilder_cstr(&pb);

	switch (ctrl) {
	case APK_FS_CTRL_COMMIT:
		return uvol_run(ac, "up", uvol_name, 0, 0);
	case APK_FS_CTRL_APKNEW:
	case APK_FS_CTRL_CANCEL:
	case APK_FS_CTRL_DELETE:
		r = uvol_run(ac, "down", uvol_name, 0, 0);
		if (r)
			return r;
		return uvol_run(ac, "remove", uvol_name, 0, 0);
	default:
		return -APKE_UVOL_ERROR;
	}
}

static int uvol_file_digest(struct apk_fsdir *d, apk_blob_t filename, uint8_t alg, struct apk_digest *dgst)
{
	return -APKE_UVOL_ERROR;
}

const struct apk_fsdir_ops fsdir_ops_uvol = {
	.priority = APK_FS_PRIO_UVOL,
	.dir_create = uvol_dir_create,
	.dir_delete = uvol_dir_delete,
	.dir_check = uvol_dir_check,
	.dir_update_perms = uvol_dir_update_perms,
	.file_extract = uvol_file_extract,
	.file_control = uvol_file_control,
	.file_digest = uvol_file_digest,
};