xref: /freebsd/sbin/hastd/metadata.c (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 #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 
46 int
47 metadata_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);
159 fail:
160 	if (opened_here) {
161 		close(res->hr_localfd);
162 		res->hr_localfd = -1;
163 	}
164 	errno = rerrno;
165 	return (-1);
166 }
167 
168 int
169 metadata_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;
221 end:
222 	free(buf);
223 	nv_free(nv);
224 	return (ret);
225 }
226