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