xref: /titanic_41/usr/src/cmd/fs.d/cachefs/fsck/dlog_ck.c (revision 7a286c471efbab8562f7655a82931904703fffe0)
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
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
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
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