diff usr/src/cmd/fs.d/cachefs/fsck/dlog_ck.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/cachefs/fsck/dlog_ck.c	Tue Jun 02 18:56:50 2009 +0900
@@ -0,0 +1,292 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1996-2002 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"@(#)dlog_ck.c	1.13	05/06/08 SMI"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/fs/cachefs_fs.h>
+#include <sys/fs/cachefs_dlog.h>
+
+/* forward references */
+static int create_mapfile(char *fname, int size);
+
+int
+dlog_ck(char *dir_path, ino64_t *maxlocalfilenop)
+{
+	int err;
+	int n;
+	char dlog_path[MAXPATHLEN];
+	char dmap_path[MAXPATHLEN];
+	struct stat64 statinfo;
+	int fd;
+	int dlog_version;
+	off_t offset;
+	struct cfs_dlog_entry buf;
+	int max_seq_num;
+	int ent_count = 0;
+	ino64_t fileno, maxlocalfileno;
+
+	if (maxlocalfilenop)
+		*maxlocalfilenop = 0LL;
+
+	n = strlen(dir_path) + strlen(CACHEFS_DLOG_FILE) + 2;
+	if (n > MAXPATHLEN) {
+		pr_err(gettext("%s/%s: path too long"),
+		    dir_path, CACHEFS_DLOG_FILE);
+		return (-1);
+	}
+	sprintf(dlog_path, "%s/%s", dir_path, CACHEFS_DLOG_FILE);
+
+	n = strlen(dir_path) + strlen(CACHEFS_DMAP_FILE) + 2;
+	if (n > MAXPATHLEN) {
+		pr_err(gettext("%s/%s: path too long"),
+		    dir_path, CACHEFS_DMAP_FILE);
+		return (-1);
+	}
+	sprintf(dmap_path, "%s/%s", dir_path, CACHEFS_DMAP_FILE);
+
+	err = lstat64(dlog_path, &statinfo);
+	if (err < 0) {
+		if (errno == ENOENT)
+			(void) unlink(dmap_path);
+		/*
+		 * No disconnect log(dlog) file exists to check
+		 */
+		return (0);
+	}
+
+	/* this file will be <2GB */
+	fd = open(dlog_path, O_RDWR);
+	if (fd < 0) {
+		pr_err(gettext("can't open %s"), dlog_path);
+		return (-2);
+	}
+	err = read(fd, &dlog_version, sizeof (dlog_version));
+	if (err != sizeof (dlog_version)) {
+		pr_err(gettext("can't read %s"), dlog_path);
+		(void) close(fd);
+		return (-3);
+	}
+	if (dlog_version != CFS_DLOG_VERSION) {
+		pr_err(gettext(
+		    "unknown version number in %s"), dlog_path);
+		(void) close(fd);
+		return (-4);
+	}
+
+	offset = sizeof (dlog_version);
+	max_seq_num = 0;
+	maxlocalfileno = 0LL;
+	while (offset < (off_t)statinfo.st_size) {
+		err = (int) lseek(fd, offset, SEEK_SET);
+		if (err == -1) {
+			pr_err(gettext("can't lseek %s"), dlog_path);
+			(void) close(fd);
+			return (-5);
+		}
+
+		err = read(fd, &buf, sizeof (buf));
+		if (err < 0) {
+			pr_err(gettext("can't read %s"), dlog_path);
+			(void) close(fd);
+			return (-6);
+		}
+		++ent_count;
+		if (buf.dl_op ==  CFS_DLOG_TRAILER) {
+			goto out;
+		}
+		if ((buf.dl_len & 3) == 0) {
+			/*
+			 * Record length must be on a word boundary and
+			 * fit into the correct size range.
+			 */
+			if ((buf.dl_len < sizeof (int)) ||
+			    (buf.dl_len > CFS_DLOG_ENTRY_MAXSIZE)) {
+				goto out;
+			}
+			/*
+			 * Make sure length does not point beyond end of
+			 * file
+			 */
+			if ((offset + (off_t)buf.dl_len) >
+			    (off_t)statinfo.st_size) {
+				goto out;
+			}
+		} else {
+			goto out;
+		}
+
+		/* make sure the valid field is reasonable */
+		switch (buf.dl_valid) {
+		case CFS_DLOG_VAL_CRASH:
+		case CFS_DLOG_VAL_COMMITTED:
+		case CFS_DLOG_VAL_ERROR:
+		case CFS_DLOG_VAL_PROCESSED:
+			break;
+		default:
+			goto out;
+		}
+
+		/* make sure the operation field is reasonable */
+		fileno = 0LL;
+		switch (buf.dl_op) {
+		case CFS_DLOG_CREATE:
+			fileno = buf.dl_u.dl_create.dl_new_cid.cid_fileno;
+			break;
+		case CFS_DLOG_REMOVE:
+			break;
+		case CFS_DLOG_LINK:
+			break;
+		case CFS_DLOG_RENAME:
+			break;
+		case CFS_DLOG_MKDIR:
+			fileno = buf.dl_u.dl_mkdir.dl_child_cid.cid_fileno;
+			break;
+		case CFS_DLOG_RMDIR:
+			break;
+		case CFS_DLOG_SYMLINK:
+			fileno = buf.dl_u.dl_symlink.dl_child_cid.cid_fileno;
+			break;
+		case CFS_DLOG_SETATTR:
+			break;
+		case CFS_DLOG_SETSECATTR:
+			break;
+		case CFS_DLOG_MODIFIED:
+			break;
+		case CFS_DLOG_MAPFID:
+			break;
+		default:
+			goto out;
+		}
+
+		/* track the largest local fileno used */
+		if (maxlocalfileno < fileno)
+			maxlocalfileno = fileno;
+
+		/* track the largest sequence number used */
+		if (max_seq_num < buf.dl_seq) {
+			max_seq_num = buf.dl_seq;
+		}
+
+		offset += buf.dl_len;
+	}
+
+out:
+	if ((buf.dl_op != CFS_DLOG_TRAILER) ||
+	    (buf.dl_len != sizeof (struct cfs_dlog_trailer)) ||
+	    (buf.dl_valid != CFS_DLOG_VAL_COMMITTED) ||
+	    ((offset + (off_t)buf.dl_len) != (off_t)statinfo.st_size)) {
+		ftruncate(fd, offset);
+		buf.dl_len = sizeof (struct cfs_dlog_trailer);
+		buf.dl_op = CFS_DLOG_TRAILER;
+		buf.dl_valid = CFS_DLOG_VAL_COMMITTED;
+		buf.dl_seq = max_seq_num + 1;
+		if (wrdlog(fd, &buf,  buf.dl_len, offset) != 0) {
+			(void) close(fd);
+			return (-7);
+		}
+	}
+
+	if (fsync(fd) == -1) {
+		pr_err(gettext("Cannot sync %s"), dlog_path);
+		(void) close(fd);
+		return (-8);
+	}
+	(void) close(fd); /* ignore return since fsync() successful */
+
+	/* check to see that mapfile exists; if not, create it. */
+	if (access(dmap_path, F_OK) != 0) {
+		/* XXX ent_count is a very high upper bound */
+		if (create_mapfile(dmap_path,
+		    ent_count * sizeof (struct cfs_dlog_mapping_space)) != 0) {
+			return (-9);
+		}
+	}
+
+	if (maxlocalfilenop)
+		*maxlocalfilenop = maxlocalfileno;
+	return (0);
+}
+
+int
+wrdlog(int fd, char * buf, int len, off_t offset)
+{
+	int err;
+
+	err = lseek(fd, offset, SEEK_SET);
+	if (err < 0) {
+		return (-1);
+	}
+
+	err = write(fd, buf, len);
+	if (err != len) {
+		return (-2);
+	}
+
+	return (0);
+}
+
+static int
+create_mapfile(char *fname, int size)
+{
+	char buffy[BUFSIZ];
+	int fd, rc, wsize;
+
+	/* this file will be <2GB */
+	fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+	if (fd < 0)
+		return (errno);
+
+	memset(buffy, '\0', sizeof (buffy));
+	while (size > 0) {
+		wsize = (size > sizeof (buffy)) ? sizeof (buffy) : size;
+		if (write(fd, buffy, wsize) != wsize) {
+			rc = errno;
+			(void) close(fd);
+			(void) unlink(fname);
+			return (rc);
+		}
+		size -= wsize;
+	}
+
+	if (fsync(fd) != 0) {
+		rc = errno;
+		(void) close(fd);
+		(void) unlink(fname);
+		return (rc);
+	}
+	(void) close(fd);
+
+	return (0);
+}