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