1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2009-2010 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * This software was developed by Pawel Jakub Dawidek under sponsorship from
8 * the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: stable/11/sbin/hastd/metadata.c 330449 2018-03-05 07:26:05Z eadler $");
34
35#include <errno.h>
36#include <fcntl.h>
37#include <string.h>
38#include <strings.h>
39#include <unistd.h>
40
41#include <ebuf.h>
42#include <nv.h>
43#include <pjdlog.h>
44#include <subr.h>
45
46#include "metadata.h"
47
48int
49metadata_read(struct hast_resource *res, bool openrw)
50{
51	unsigned char *buf;
52	struct ebuf *eb;
53	struct nv *nv;
54	ssize_t done;
55	const char *str;
56	int rerrno;
57	bool opened_here;
58
59	opened_here = false;
60	rerrno = 0;
61
62	/*
63	 * Is this first metadata_read() call for this resource?
64	 */
65	if (res->hr_localfd == -1) {
66		if (provinfo(res, openrw) == -1) {
67			rerrno = errno;
68			goto fail;
69		}
70		opened_here = true;
71		pjdlog_debug(1, "Obtained info about %s.", res->hr_localpath);
72		if (openrw) {
73			if (flock(res->hr_localfd, LOCK_EX | LOCK_NB) == -1) {
74				rerrno = errno;
75				if (errno == EOPNOTSUPP) {
76					pjdlog_warning("Unable to lock %s (operation not supported), but continuing.",
77					    res->hr_localpath);
78				} else {
79					pjdlog_errno(LOG_ERR,
80					    "Unable to lock %s",
81					    res->hr_localpath);
82					goto fail;
83				}
84			}
85			pjdlog_debug(1, "Locked %s.", res->hr_localpath);
86		}
87	}
88
89	eb = ebuf_alloc(METADATA_SIZE);
90	if (eb == NULL) {
91		rerrno = errno;
92		pjdlog_errno(LOG_ERR,
93		    "Unable to allocate memory to read metadata");
94		goto fail;
95	}
96	if (ebuf_add_tail(eb, NULL, METADATA_SIZE) == -1) {
97		rerrno = errno;
98		pjdlog_errno(LOG_ERR,
99		    "Unable to allocate memory to read metadata");
100		ebuf_free(eb);
101		goto fail;
102	}
103	buf = ebuf_data(eb, NULL);
104	PJDLOG_ASSERT(buf != NULL);
105	done = pread(res->hr_localfd, buf, METADATA_SIZE, 0);
106	if (done == -1 || done != METADATA_SIZE) {
107		rerrno = errno;
108		pjdlog_errno(LOG_ERR, "Unable to read metadata");
109		ebuf_free(eb);
110		goto fail;
111	}
112	nv = nv_ntoh(eb);
113	if (nv == NULL) {
114		rerrno = errno;
115		pjdlog_errno(LOG_ERR, "Metadata read from %s is invalid",
116		    res->hr_localpath);
117		ebuf_free(eb);
118		goto fail;
119	}
120
121	str = nv_get_string(nv, "resource");
122	if (str != NULL && strcmp(str, res->hr_name) != 0) {
123		pjdlog_error("Provider %s is not part of resource %s.",
124		    res->hr_localpath, res->hr_name);
125		nv_free(nv);
126		goto fail;
127	}
128
129	res->hr_datasize = nv_get_uint64(nv, "datasize");
130	res->hr_extentsize = (int)nv_get_uint32(nv, "extentsize");
131	res->hr_keepdirty = (int)nv_get_uint32(nv, "keepdirty");
132	res->hr_localoff = nv_get_uint64(nv, "offset");
133	res->hr_resuid = nv_get_uint64(nv, "resuid");
134	if (res->hr_role != HAST_ROLE_PRIMARY) {
135		/* Secondary or init role. */
136		res->hr_secondary_localcnt = nv_get_uint64(nv, "localcnt");
137		res->hr_secondary_remotecnt = nv_get_uint64(nv, "remotecnt");
138	}
139	if (res->hr_role != HAST_ROLE_SECONDARY) {
140		/* Primary or init role. */
141		res->hr_primary_localcnt = nv_get_uint64(nv, "localcnt");
142		res->hr_primary_remotecnt = nv_get_uint64(nv, "remotecnt");
143	}
144	str = nv_get_string(nv, "prevrole");
145	if (str != NULL) {
146		if (strcmp(str, "primary") == 0)
147			res->hr_previous_role = HAST_ROLE_PRIMARY;
148		else if (strcmp(str, "secondary") == 0)
149			res->hr_previous_role = HAST_ROLE_SECONDARY;
150	}
151
152	if (nv_error(nv) != 0) {
153		errno = rerrno = nv_error(nv);
154		pjdlog_errno(LOG_ERR, "Unable to read metadata from %s",
155		    res->hr_localpath);
156		nv_free(nv);
157		goto fail;
158	}
159	nv_free(nv);
160	return (0);
161fail:
162	if (opened_here) {
163		close(res->hr_localfd);
164		res->hr_localfd = -1;
165	}
166	errno = rerrno;
167	return (-1);
168}
169
170int
171metadata_write(struct hast_resource *res)
172{
173	struct ebuf *eb;
174	struct nv *nv;
175	unsigned char *buf, *ptr;
176	size_t size;
177	ssize_t done;
178	int ret;
179
180	buf = calloc(1, METADATA_SIZE);
181	if (buf == NULL) {
182		pjdlog_error("Unable to allocate %zu bytes for metadata.",
183		    (size_t)METADATA_SIZE);
184		return (-1);
185	}
186
187	ret = -1;
188
189	nv = nv_alloc();
190	nv_add_string(nv, res->hr_name, "resource");
191	nv_add_uint64(nv, (uint64_t)res->hr_datasize, "datasize");
192	nv_add_uint32(nv, (uint32_t)res->hr_extentsize, "extentsize");
193	nv_add_uint32(nv, (uint32_t)res->hr_keepdirty, "keepdirty");
194	nv_add_uint64(nv, (uint64_t)res->hr_localoff, "offset");
195	nv_add_uint64(nv, res->hr_resuid, "resuid");
196	if (res->hr_role == HAST_ROLE_PRIMARY ||
197	    res->hr_role == HAST_ROLE_INIT) {
198		nv_add_uint64(nv, res->hr_primary_localcnt, "localcnt");
199		nv_add_uint64(nv, res->hr_primary_remotecnt, "remotecnt");
200	} else /* if (res->hr_role == HAST_ROLE_SECONDARY) */ {
201		PJDLOG_ASSERT(res->hr_role == HAST_ROLE_SECONDARY);
202		nv_add_uint64(nv, res->hr_secondary_localcnt, "localcnt");
203		nv_add_uint64(nv, res->hr_secondary_remotecnt, "remotecnt");
204	}
205	nv_add_string(nv, role2str(res->hr_role), "prevrole");
206	if (nv_error(nv) != 0) {
207		pjdlog_error("Unable to create metadata.");
208		goto end;
209	}
210	res->hr_previous_role = res->hr_role;
211	eb = nv_hton(nv);
212	PJDLOG_ASSERT(eb != NULL);
213	ptr = ebuf_data(eb, &size);
214	PJDLOG_ASSERT(ptr != NULL);
215	PJDLOG_ASSERT(size < METADATA_SIZE);
216	bcopy(ptr, buf, size);
217	done = pwrite(res->hr_localfd, buf, METADATA_SIZE, 0);
218	if (done == -1 || done != METADATA_SIZE) {
219		pjdlog_errno(LOG_ERR, "Unable to write metadata");
220		goto end;
221	}
222	ret = 0;
223end:
224	free(buf);
225	nv_free(nv);
226	return (ret);
227}
228