1 /*
2  * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
3  * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4  */
5 #include "cmogstored.h"
6 #include "mgmt.h"
7 #include "iov_str.h"
8 #include "digest.h"
9 
10 static char *
get_path(struct iovec * dst,struct mog_mgmt * mgmt,char * buf,bool sdup)11 get_path(struct iovec *dst, struct mog_mgmt *mgmt, char *buf, bool sdup)
12 {
13 	dst->iov_base = buf + mgmt->mark[0];
14 	dst->iov_len = mgmt->mark[1] - mgmt->mark[0];
15 
16 	if (mog_valid_path(dst->iov_base, dst->iov_len)) {
17 		char *path;
18 
19 		if (sdup) {
20 			path = malloc(dst->iov_len + 1);
21 			if (!path) {
22 				struct iovec iov;
23 
24 				IOV_STR(&iov, "ERROR: out-of-memory\r\n");
25 				mog_mgmt_writev(mgmt, &iov, 1);
26 
27 				return NULL;
28 			}
29 
30 			memcpy(path, dst->iov_base, dst->iov_len);
31 		} else {
32 			path = dst->iov_base;
33 		}
34 
35 		path[dst->iov_len] = '\0';
36 		return path;
37 	} else {
38 		struct iovec iov;
39 
40 		IOV_STR(&iov, "ERROR: uri invalid (contains ..)\r\n");
41 		mog_mgmt_writev(mgmt, &iov, 1);
42 		return NULL;
43 	}
44 }
45 
46 /* starts the MD5 request */
mog_mgmt_fn_digest(struct mog_fd * mfd,char * buf)47 void mog_mgmt_fn_digest(struct mog_fd *mfd, char *buf)
48 {
49 	struct mog_mgmt *mgmt = &mfd->as.mgmt;
50 	struct iovec iov[2];
51 	char *path = get_path(iov, mgmt, buf, true);
52 
53 	if (!path) return;
54 
55 	TRACE(CMOGSTORED_MGMT_DIG_START(mfd->fd, mgmt->alg, path));
56 
57 	mgmt->forward = mog_file_open_read(mgmt->svc, path);
58 	if (mgmt->forward) {
59 		struct mog_file *file = &mgmt->forward->as.file;
60 
61 		mog_fadv_noreuse(mgmt->forward->fd, 0, 0 /* ALL */);
62 		mog_fadv_sequential(mgmt->forward->fd, 0, 0 /* ALL */);
63 		mog_digest_init(&file->digest, mgmt->alg);
64 		file->pathlen = iov[0].iov_len;
65 		file->path = path;
66 	} else {
67 		switch (mgmt->alg) {
68 		case GC_MD5:
69 			IOV_STR(&iov[1], " MD5=-1\r\n");
70 			break;
71 		case GC_SHA1:
72 			IOV_STR(&iov[1], " SHA-1=-1\r\n");
73 			break;
74 		default: /* Ragel parser prevents this: */
75 			die("BUG: unhandled algorithm: %d", mgmt->alg);
76 		}
77 		TRACE(CMOGSTORED_MGMT_DIG_DONE(mfd->fd, -1));
78 		mog_mgmt_writev(mgmt, iov, 2);
79 		free(path);
80 	}
81 }
82 
83 /* finishes the MD5 request */
84 #define CLEN(s) (sizeof(s)-1)
85 
mog_mgmt_fn_digest_err(struct mog_fd * mfd)86 void mog_mgmt_fn_digest_err(struct mog_fd *mfd)
87 {
88 	struct mog_mgmt *mgmt = &mfd->as.mgmt;
89 	struct iovec iov[3];
90 	struct mog_fd *file_mfd = mgmt->forward;
91 	struct mog_file *file = &file_mfd->as.file;
92 	long long offset = (long long)lseek(file_mfd->fd, 0, SEEK_CUR);
93 	char buf[sizeof(" at 18446744073709551615 failed\r\n") - 1];
94 
95 	/* offset could be -1 here, but there ain't much we can do */
96 
97 	IOV_STR(iov, "ERR read ");
98 	iov[1].iov_base = file->path;
99 	iov[1].iov_len = file->pathlen;
100 	iov[2].iov_base = buf;
101 	iov[2].iov_len = snprintf(buf, sizeof(buf),
102 	                          " at %lld failed\r\n", offset);
103 	TRACE(CMOGSTORED_MGMT_DIG_DONE(mfd->fd, offset));
104 	mog_mgmt_writev(mgmt, iov, 3);
105 }
106 
107 /* output: "/$PATH MD5=hex\r\n" */
mog_mgmt_fn_digest_emit(struct mog_fd * mfd)108 void mog_mgmt_fn_digest_emit(struct mog_fd *mfd)
109 {
110 	struct mog_mgmt *mgmt = &mfd->as.mgmt;
111 	struct iovec iov[2];
112 	char buf[CLEN(" SHA-1=") + 40 + CLEN("\r\n")];
113 	char *b = buf;
114 	struct mog_fd *file_mfd = mgmt->forward;
115 	struct mog_file *file = &file_mfd->as.file;
116 	size_t len;
117 
118 	iov[0].iov_base = file->path;
119 	iov[0].iov_len = file->pathlen;
120 
121 	/* ugh, clean this up... */
122 	switch (mgmt->alg) {
123 	case GC_MD5:
124 		b = mempcpy(b, " MD5=", CLEN(" MD5="));
125 		len = 32;
126 		iov[1].iov_len = CLEN(" MD5=") + 32 + CLEN("\r\n");
127 		break;
128 	case GC_SHA1:
129 		b = mempcpy(b, " SHA-1=", CLEN(" SHA-1="));
130 		len = 40;
131 		iov[1].iov_len = sizeof(buf);
132 		break;
133 	default:
134 		die("BUG: unhandled algorithm: %d", mgmt->alg);
135 	}
136 
137 	mog_digest_hex(&file->digest, b, len);
138 	b[len] = '\r';
139 	b[len + 1] = '\n';
140 	iov[1].iov_base = buf;
141 	TRACE(CMOGSTORED_MGMT_DIG_DONE(mfd->fd, 0));
142 	mog_mgmt_writev(mgmt, iov, 2);
143 }
144 
145 /*
146  * writes to mgmt fd:
147  *   "URI $SIZE\r\n" on success
148  *   "URI -1\r\n" on failure
149  *   "ERROR: uri invalid (contains ..)\r\n" on invalid paths
150  */
mog_mgmt_fn_size(struct mog_mgmt * mgmt,char * buf)151 void mog_mgmt_fn_size(struct mog_mgmt *mgmt, char *buf)
152 {
153 	struct stat sb;
154 	struct iovec iov[2];
155 	char tmp[sizeof(" 18446744073709551615\r\n") - 1];
156 	char *path = get_path(iov, mgmt, buf, false);
157 
158 	if (!path) return;
159 
160 	if (mog_stat(mgmt->svc, path, &sb) == 0) {
161 		long long size = (long long)sb.st_size;
162 
163 		iov[1].iov_base = tmp;
164 		iov[1].iov_len = snprintf(tmp, sizeof(tmp), " %lld\r\n", size);
165 	} else {
166 		IOV_STR(&iov[1], " -1\r\n");
167 	}
168 
169 	mog_mgmt_writev(mgmt, iov, 2);
170 }
171 
mog_mgmt_fn_blank(struct mog_mgmt * mgmt)172 void mog_mgmt_fn_blank(struct mog_mgmt *mgmt)
173 {
174 	struct iovec iov;
175 
176 	IOV_STR(&iov, "\r\n");
177 	mog_mgmt_writev(mgmt, &iov, 1);
178 }
179 
mog_mgmt_fn_unknown(struct mog_mgmt * mgmt,char * buf)180 void mog_mgmt_fn_unknown(struct mog_mgmt *mgmt, char *buf)
181 {
182 	struct iovec iov[3];
183 
184 	IOV_STR(&iov[0], "ERROR: unknown command: ");
185 	iov[1].iov_base = mgmt->mark[0] + buf;
186 	iov[1].iov_len = mgmt->mark[1] - mgmt->mark[0];
187 	IOV_STR(&iov[2], "\r\n");
188 	mog_mgmt_writev(mgmt, iov, 3);
189 }
190 
mog_mgmt_fn_watch_err(struct mog_mgmt * mgmt)191 void mog_mgmt_fn_watch_err(struct mog_mgmt *mgmt)
192 {
193 	struct iovec iov;
194 
195 	IOV_STR(&iov, "ERR iostat unavailable\r\n");
196 	mog_mgmt_writev(mgmt, &iov, 1);
197 }
198 
mog_mgmt_fn_aio_threads(struct mog_mgmt * mgmt,char * buf)199 void mog_mgmt_fn_aio_threads(struct mog_mgmt *mgmt, char *buf)
200 {
201 	char *end;
202 	unsigned long long nr;
203 	char *nptr = buf + mgmt->mark[0];
204 	char *eor = nptr + mgmt->mark[1] - mgmt->mark[0];
205 
206 	assert((*eor == '\n' || *eor == '\r') && "missing end-of-record");
207 	*eor = 0;
208 
209 	nr = strtoull(nptr, &end, 10);
210 	assert(*end == 0 && "ragel misfed mog_mgmt_fn_set_aio_threads");
211 
212 	if (nr > 0 && nr <= (size_t)INT_MAX)
213 		mog_svc_aio_threads_enqueue(mgmt->svc, (unsigned)nr);
214 
215 	mog_mgmt_fn_blank(mgmt);
216 }
217