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