1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 1996-2002 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <sys/types.h>
30 #include <stdio.h>
31 #include <stddef.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <errno.h>
37
38 #include <sys/fs/cachefs_fs.h>
39 #include <sys/fs/cachefs_dlog.h>
40
41 /* forward references */
42 static int create_mapfile(char *fname, int size);
43
44 int
dlog_ck(char * dir_path,ino64_t * maxlocalfilenop)45 dlog_ck(char *dir_path, ino64_t *maxlocalfilenop)
46 {
47 int err;
48 int n;
49 char dlog_path[MAXPATHLEN];
50 char dmap_path[MAXPATHLEN];
51 struct stat64 statinfo;
52 int fd;
53 int dlog_version;
54 off_t offset;
55 struct cfs_dlog_entry buf;
56 int max_seq_num;
57 int ent_count = 0;
58 ino64_t fileno, maxlocalfileno;
59
60 if (maxlocalfilenop)
61 *maxlocalfilenop = 0LL;
62
63 n = strlen(dir_path) + strlen(CACHEFS_DLOG_FILE) + 2;
64 if (n > MAXPATHLEN) {
65 pr_err(gettext("%s/%s: path too long"),
66 dir_path, CACHEFS_DLOG_FILE);
67 return (-1);
68 }
69 sprintf(dlog_path, "%s/%s", dir_path, CACHEFS_DLOG_FILE);
70
71 n = strlen(dir_path) + strlen(CACHEFS_DMAP_FILE) + 2;
72 if (n > MAXPATHLEN) {
73 pr_err(gettext("%s/%s: path too long"),
74 dir_path, CACHEFS_DMAP_FILE);
75 return (-1);
76 }
77 sprintf(dmap_path, "%s/%s", dir_path, CACHEFS_DMAP_FILE);
78
79 err = lstat64(dlog_path, &statinfo);
80 if (err < 0) {
81 if (errno == ENOENT)
82 (void) unlink(dmap_path);
83 /*
84 * No disconnect log(dlog) file exists to check
85 */
86 return (0);
87 }
88
89 /* this file will be <2GB */
90 fd = open(dlog_path, O_RDWR);
91 if (fd < 0) {
92 pr_err(gettext("can't open %s"), dlog_path);
93 return (-2);
94 }
95 err = read(fd, &dlog_version, sizeof (dlog_version));
96 if (err != sizeof (dlog_version)) {
97 pr_err(gettext("can't read %s"), dlog_path);
98 (void) close(fd);
99 return (-3);
100 }
101 if (dlog_version != CFS_DLOG_VERSION) {
102 pr_err(gettext(
103 "unknown version number in %s"), dlog_path);
104 (void) close(fd);
105 return (-4);
106 }
107
108 offset = sizeof (dlog_version);
109 max_seq_num = 0;
110 maxlocalfileno = 0LL;
111 while (offset < (off_t)statinfo.st_size) {
112 err = (int) lseek(fd, offset, SEEK_SET);
113 if (err == -1) {
114 pr_err(gettext("can't lseek %s"), dlog_path);
115 (void) close(fd);
116 return (-5);
117 }
118
119 err = read(fd, &buf, sizeof (buf));
120 if (err < 0) {
121 pr_err(gettext("can't read %s"), dlog_path);
122 (void) close(fd);
123 return (-6);
124 }
125 ++ent_count;
126 if (buf.dl_op == CFS_DLOG_TRAILER) {
127 goto out;
128 }
129 if ((buf.dl_len & 3) == 0) {
130 /*
131 * Record length must be on a word boundary and
132 * fit into the correct size range.
133 */
134 if ((buf.dl_len < sizeof (int)) ||
135 (buf.dl_len > CFS_DLOG_ENTRY_MAXSIZE)) {
136 goto out;
137 }
138 /*
139 * Make sure length does not point beyond end of
140 * file
141 */
142 if ((offset + (off_t)buf.dl_len) >
143 (off_t)statinfo.st_size) {
144 goto out;
145 }
146 } else {
147 goto out;
148 }
149
150 /* make sure the valid field is reasonable */
151 switch (buf.dl_valid) {
152 case CFS_DLOG_VAL_CRASH:
153 case CFS_DLOG_VAL_COMMITTED:
154 case CFS_DLOG_VAL_ERROR:
155 case CFS_DLOG_VAL_PROCESSED:
156 break;
157 default:
158 goto out;
159 }
160
161 /* make sure the operation field is reasonable */
162 fileno = 0LL;
163 switch (buf.dl_op) {
164 case CFS_DLOG_CREATE:
165 fileno = buf.dl_u.dl_create.dl_new_cid.cid_fileno;
166 break;
167 case CFS_DLOG_REMOVE:
168 break;
169 case CFS_DLOG_LINK:
170 break;
171 case CFS_DLOG_RENAME:
172 break;
173 case CFS_DLOG_MKDIR:
174 fileno = buf.dl_u.dl_mkdir.dl_child_cid.cid_fileno;
175 break;
176 case CFS_DLOG_RMDIR:
177 break;
178 case CFS_DLOG_SYMLINK:
179 fileno = buf.dl_u.dl_symlink.dl_child_cid.cid_fileno;
180 break;
181 case CFS_DLOG_SETATTR:
182 break;
183 case CFS_DLOG_SETSECATTR:
184 break;
185 case CFS_DLOG_MODIFIED:
186 break;
187 case CFS_DLOG_MAPFID:
188 break;
189 default:
190 goto out;
191 }
192
193 /* track the largest local fileno used */
194 if (maxlocalfileno < fileno)
195 maxlocalfileno = fileno;
196
197 /* track the largest sequence number used */
198 if (max_seq_num < buf.dl_seq) {
199 max_seq_num = buf.dl_seq;
200 }
201
202 offset += buf.dl_len;
203 }
204
205 out:
206 if ((buf.dl_op != CFS_DLOG_TRAILER) ||
207 (buf.dl_len != sizeof (struct cfs_dlog_trailer)) ||
208 (buf.dl_valid != CFS_DLOG_VAL_COMMITTED) ||
209 ((offset + (off_t)buf.dl_len) != (off_t)statinfo.st_size)) {
210 ftruncate(fd, offset);
211 buf.dl_len = sizeof (struct cfs_dlog_trailer);
212 buf.dl_op = CFS_DLOG_TRAILER;
213 buf.dl_valid = CFS_DLOG_VAL_COMMITTED;
214 buf.dl_seq = max_seq_num + 1;
215 if (wrdlog(fd, &buf, buf.dl_len, offset) != 0) {
216 (void) close(fd);
217 return (-7);
218 }
219 }
220
221 if (fsync(fd) == -1) {
222 pr_err(gettext("Cannot sync %s"), dlog_path);
223 (void) close(fd);
224 return (-8);
225 }
226 (void) close(fd); /* ignore return since fsync() successful */
227
228 /* check to see that mapfile exists; if not, create it. */
229 if (access(dmap_path, F_OK) != 0) {
230 /* XXX ent_count is a very high upper bound */
231 if (create_mapfile(dmap_path,
232 ent_count * sizeof (struct cfs_dlog_mapping_space)) != 0) {
233 return (-9);
234 }
235 }
236
237 if (maxlocalfilenop)
238 *maxlocalfilenop = maxlocalfileno;
239 return (0);
240 }
241
242 int
wrdlog(int fd,char * buf,int len,off_t offset)243 wrdlog(int fd, char * buf, int len, off_t offset)
244 {
245 int err;
246
247 err = lseek(fd, offset, SEEK_SET);
248 if (err < 0) {
249 return (-1);
250 }
251
252 err = write(fd, buf, len);
253 if (err != len) {
254 return (-2);
255 }
256
257 return (0);
258 }
259
260 static int
create_mapfile(char * fname,int size)261 create_mapfile(char *fname, int size)
262 {
263 char buffy[BUFSIZ];
264 int fd, rc, wsize;
265
266 /* this file will be <2GB */
267 fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
268 if (fd < 0)
269 return (errno);
270
271 memset(buffy, '\0', sizeof (buffy));
272 while (size > 0) {
273 wsize = (size > sizeof (buffy)) ? sizeof (buffy) : size;
274 if (write(fd, buffy, wsize) != wsize) {
275 rc = errno;
276 (void) close(fd);
277 (void) unlink(fname);
278 return (rc);
279 }
280 size -= wsize;
281 }
282
283 if (fsync(fd) != 0) {
284 rc = errno;
285 (void) close(fd);
286 (void) unlink(fname);
287 return (rc);
288 }
289 (void) close(fd);
290
291 return (0);
292 }
293