xref: /freebsd/sbin/hastd/metadata.c (revision aa0a1e58)
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 <assert.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <unistd.h>
39 
40 #include <ebuf.h>
41 #include <nv.h>
42 #include <pjdlog.h>
43 #include <subr.h>
44 
45 #include "metadata.h"
46 
47 int
48 metadata_read(struct hast_resource *res, bool openrw)
49 {
50 	unsigned char *buf;
51 	struct ebuf *eb;
52 	struct nv *nv;
53 	ssize_t done;
54 	const char *str;
55 	int rerrno;
56 	bool opened_here;
57 
58 	opened_here = false;
59 	rerrno = 0;
60 
61 	/*
62 	 * Is this first metadata_read() call for this resource?
63 	 */
64 	if (res->hr_localfd == -1) {
65 		if (provinfo(res, openrw) < 0) {
66 			rerrno = errno;
67 			goto fail;
68 		}
69 		opened_here = true;
70 		pjdlog_debug(1, "Obtained info about %s.", res->hr_localpath);
71 		if (openrw) {
72 			if (flock(res->hr_localfd, LOCK_EX | LOCK_NB) < 0) {
73 				rerrno = errno;
74 				if (errno == EOPNOTSUPP) {
75 					pjdlog_warning("Unable to lock %s (operation not supported), but continuing.",
76 					    res->hr_localpath);
77 				} else {
78 					pjdlog_errno(LOG_ERR,
79 					    "Unable to lock %s",
80 					    res->hr_localpath);
81 					goto fail;
82 				}
83 			}
84 			pjdlog_debug(1, "Locked %s.", res->hr_localpath);
85 		}
86 	}
87 
88 	eb = ebuf_alloc(METADATA_SIZE);
89 	if (eb == NULL) {
90 		rerrno = errno;
91 		pjdlog_errno(LOG_ERR,
92 		    "Unable to allocate memory to read metadata");
93 		goto fail;
94 	}
95 	if (ebuf_add_tail(eb, NULL, METADATA_SIZE) < 0) {
96 		rerrno = errno;
97 		pjdlog_errno(LOG_ERR,
98 		    "Unable to allocate memory to read metadata");
99 		ebuf_free(eb);
100 		goto fail;
101 	}
102 	buf = ebuf_data(eb, NULL);
103 	assert(buf != NULL);
104 	done = pread(res->hr_localfd, buf, METADATA_SIZE, 0);
105 	if (done < 0 || done != METADATA_SIZE) {
106 		rerrno = errno;
107 		pjdlog_errno(LOG_ERR, "Unable to read metadata");
108 		ebuf_free(eb);
109 		goto fail;
110 	}
111 	nv = nv_ntoh(eb);
112 	if (nv == NULL) {
113 		rerrno = errno;
114 		pjdlog_errno(LOG_ERR, "Metadata read from %s is invalid",
115 		    res->hr_localpath);
116 		ebuf_free(eb);
117 		goto fail;
118 	}
119 
120 	str = nv_get_string(nv, "resource");
121 	if (str != NULL && strcmp(str, res->hr_name) != 0) {
122 		pjdlog_error("Provider %s is not part of resource %s.",
123 		    res->hr_localpath, res->hr_name);
124 		nv_free(nv);
125 		goto fail;
126 	}
127 
128 	res->hr_datasize = nv_get_uint64(nv, "datasize");
129 	res->hr_extentsize = (int)nv_get_uint32(nv, "extentsize");
130 	res->hr_keepdirty = (int)nv_get_uint32(nv, "keepdirty");
131 	res->hr_localoff = nv_get_uint64(nv, "offset");
132 	res->hr_resuid = nv_get_uint64(nv, "resuid");
133 	if (res->hr_role != HAST_ROLE_PRIMARY) {
134 		/* Secondary or init role. */
135 		res->hr_secondary_localcnt = nv_get_uint64(nv, "localcnt");
136 		res->hr_secondary_remotecnt = nv_get_uint64(nv, "remotecnt");
137 	}
138 	if (res->hr_role != HAST_ROLE_SECONDARY) {
139 		/* Primary or init role. */
140 		res->hr_primary_localcnt = nv_get_uint64(nv, "localcnt");
141 		res->hr_primary_remotecnt = nv_get_uint64(nv, "remotecnt");
142 	}
143 	str = nv_get_string(nv, "prevrole");
144 	if (str != NULL) {
145 		if (strcmp(str, "primary") == 0)
146 			res->hr_previous_role = HAST_ROLE_PRIMARY;
147 		else if (strcmp(str, "secondary") == 0)
148 			res->hr_previous_role = HAST_ROLE_SECONDARY;
149 	}
150 
151 	if (nv_error(nv) != 0) {
152 		errno = rerrno = nv_error(nv);
153 		pjdlog_errno(LOG_ERR, "Unable to read metadata from %s",
154 		    res->hr_localpath);
155 		nv_free(nv);
156 		goto fail;
157 	}
158 	nv_free(nv);
159 	return (0);
160 fail:
161 	if (opened_here) {
162 		close(res->hr_localfd);
163 		res->hr_localfd = -1;
164 	}
165 	errno = rerrno;
166 	return (-1);
167 }
168 
169 int
170 metadata_write(struct hast_resource *res)
171 {
172 	struct ebuf *eb;
173 	struct nv *nv;
174 	unsigned char *buf, *ptr;
175 	size_t size;
176 	ssize_t done;
177 	int ret;
178 
179 	buf = calloc(1, METADATA_SIZE);
180 	if (buf == NULL) {
181 		pjdlog_error("Unable to allocate %zu bytes for metadata.",
182 		    (size_t)METADATA_SIZE);
183 		return (-1);
184 	}
185 
186 	ret = -1;
187 
188 	nv = nv_alloc();
189 	nv_add_string(nv, res->hr_name, "resource");
190 	nv_add_uint64(nv, (uint64_t)res->hr_datasize, "datasize");
191 	nv_add_uint32(nv, (uint32_t)res->hr_extentsize, "extentsize");
192 	nv_add_uint32(nv, (uint32_t)res->hr_keepdirty, "keepdirty");
193 	nv_add_uint64(nv, (uint64_t)res->hr_localoff, "offset");
194 	nv_add_uint64(nv, res->hr_resuid, "resuid");
195 	if (res->hr_role == HAST_ROLE_PRIMARY ||
196 	    res->hr_role == HAST_ROLE_INIT) {
197 		nv_add_uint64(nv, res->hr_primary_localcnt, "localcnt");
198 		nv_add_uint64(nv, res->hr_primary_remotecnt, "remotecnt");
199 	} else /* if (res->hr_role == HAST_ROLE_SECONDARY) */ {
200 		assert(res->hr_role == HAST_ROLE_SECONDARY);
201 		nv_add_uint64(nv, res->hr_secondary_localcnt, "localcnt");
202 		nv_add_uint64(nv, res->hr_secondary_remotecnt, "remotecnt");
203 	}
204 	nv_add_string(nv, role2str(res->hr_role), "prevrole");
205 	if (nv_error(nv) != 0) {
206 		pjdlog_error("Unable to create metadata.");
207 		goto end;
208 	}
209 	res->hr_previous_role = res->hr_role;
210 	eb = nv_hton(nv);
211 	assert(eb != NULL);
212 	ptr = ebuf_data(eb, &size);
213 	assert(ptr != NULL);
214 	assert(size < METADATA_SIZE);
215 	bcopy(ptr, buf, size);
216 	done = pwrite(res->hr_localfd, buf, METADATA_SIZE, 0);
217 	if (done < 0 || done != METADATA_SIZE) {
218 		pjdlog_errno(LOG_ERR, "Unable to write metadata");
219 		goto end;
220 	}
221 	ret = 0;
222 end:
223 	free(buf);
224 	nv_free(nv);
225 	return (ret);
226 }
227