Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 26 Jul 2017 15:29:56 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r321525 - in stable/11/sys: boot/zfs cddl/boot/zfs
Message-ID:  <201707261529.v6QFTv4U043105@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Wed Jul 26 15:29:56 2017
New Revision: 321525
URL: https://svnweb.freebsd.org/changeset/base/321525

Log:
  MFC r314112 (by tsoome): loader: update symlink support in zfs reader
  
  As the current zfs file system is providing symlink via system attributes,
  need to update the code accordingly.
  
  Note, as the zfsboot code does not free the memory at this time, the
  object list will put some stress on the boot2 heap, eventually we should
  address the issue.
  
  Differential Revision:  https://reviews.freebsd.org/D9706

Modified:
  stable/11/sys/boot/zfs/zfsimpl.c
  stable/11/sys/cddl/boot/zfs/zfsimpl.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/boot/zfs/zfsimpl.c
==============================================================================
--- stable/11/sys/boot/zfs/zfsimpl.c	Wed Jul 26 15:24:17 2017	(r321524)
+++ stable/11/sys/boot/zfs/zfsimpl.c	Wed Jul 26 15:29:56 2017	(r321525)
@@ -2142,6 +2142,61 @@ zfs_dnode_stat(const spa_t *spa, dnode_phys_t *dn, str
 	return (0);
 }
 
+static int
+zfs_dnode_readlink(const spa_t *spa, dnode_phys_t *dn, char *path, size_t psize)
+{
+	int rc = 0;
+
+	if (dn->dn_bonustype == DMU_OT_SA) {
+		sa_hdr_phys_t *sahdrp = NULL;
+		size_t size = 0;
+		void *buf = NULL;
+		int hdrsize;
+		char *p;
+
+		if (dn->dn_bonuslen != 0)
+			sahdrp = (sa_hdr_phys_t *)DN_BONUS(dn);
+		else {
+			blkptr_t *bp;
+
+			if ((dn->dn_flags & DNODE_FLAG_SPILL_BLKPTR) == 0)
+				return (EIO);
+			bp = &dn->dn_spill;
+
+			size = BP_GET_LSIZE(bp);
+			buf = zfs_alloc(size);
+			rc = zio_read(spa, bp, buf);
+			if (rc != 0) {
+				zfs_free(buf, size);
+				return (rc);
+			}
+			sahdrp = buf;
+		}
+		hdrsize = SA_HDR_SIZE(sahdrp);
+		p = (char *)((uintptr_t)sahdrp + hdrsize + SA_SYMLINK_OFFSET);
+		memcpy(path, p, psize);
+		if (buf != NULL)
+			zfs_free(buf, size);
+		return (0);
+	}
+	/*
+	 * Second test is purely to silence bogus compiler
+	 * warning about accessing past the end of dn_bonus.
+	 */
+	if (psize + sizeof(znode_phys_t) <= dn->dn_bonuslen &&
+	    sizeof(znode_phys_t) <= sizeof(dn->dn_bonus)) {
+		memcpy(path, &dn->dn_bonus[sizeof(znode_phys_t)], psize);
+	} else {
+		rc = dnode_read(spa, dn, 0, path, psize);
+	}
+	return (rc);
+}
+
+struct obj_list {
+	uint64_t		objnum;
+	STAILQ_ENTRY(obj_list)	entry;
+};
+
 /*
  * Lookup a file and return its dnode.
  */
@@ -2149,7 +2204,7 @@ static int
 zfs_lookup(const struct zfsmount *mount, const char *upath, dnode_phys_t *dnode)
 {
 	int rc;
-	uint64_t objnum, rootnum, parentnum;
+	uint64_t objnum;
 	const spa_t *spa;
 	dnode_phys_t dn;
 	const char *p, *q;
@@ -2157,6 +2212,8 @@ zfs_lookup(const struct zfsmount *mount, const char *u
 	char path[1024];
 	int symlinks_followed = 0;
 	struct stat sb;
+	struct obj_list *entry;
+	STAILQ_HEAD(, obj_list) on_cache = STAILQ_HEAD_INITIALIZER(on_cache);
 
 	spa = mount->spa;
 	if (mount->objset.os_type != DMU_OST_ZFS) {
@@ -2165,102 +2222,145 @@ zfs_lookup(const struct zfsmount *mount, const char *u
 		return (EIO);
 	}
 
+	if ((entry = malloc(sizeof(struct obj_list))) == NULL)
+		return (ENOMEM);
+
 	/*
 	 * Get the root directory dnode.
 	 */
 	rc = objset_get_dnode(spa, &mount->objset, MASTER_NODE_OBJ, &dn);
-	if (rc)
+	if (rc) {
+		free(entry);
 		return (rc);
+	}
 
 	rc = zap_lookup(spa, &dn, ZFS_ROOT_OBJ, &rootnum);
-	if (rc)
+	if (rc) {
+		free(entry);
 		return (rc);
+	}
+	entry->objnum = objnum;
+	STAILQ_INSERT_HEAD(&on_cache, entry, entry);
 
-	rc = objset_get_dnode(spa, &mount->objset, rootnum, &dn);
-	if (rc)
-		return (rc);
+	rc = objset_get_dnode(spa, &mount->objset, objnum, &dn);
+	if (rc != 0)
+		goto done;
 
-	objnum = rootnum;
 	p = upath;
 	while (p && *p) {
+		rc = objset_get_dnode(spa, &mount->objset, objnum, &dn);
+		if (rc != 0)
+			goto done;
+
 		while (*p == '/')
 			p++;
-		if (!*p)
+		if (*p == '\0')
 			break;
-		q = strchr(p, '/');
-		if (q) {
-			memcpy(element, p, q - p);
-			element[q - p] = 0;
-			p = q;
-		} else {
-			strcpy(element, p);
-			p = NULL;
+		q = p;
+		while (*q != '\0' && *q != '/')
+			q++;
+
+		/* skip dot */
+		if (p + 1 == q && p[0] == '.') {
+			p++;
+			continue;
 		}
+		/* double dot */
+		if (p + 2 == q && p[0] == '.' && p[1] == '.') {
+			p += 2;
+			if (STAILQ_FIRST(&on_cache) ==
+			    STAILQ_LAST(&on_cache, obj_list, entry)) {
+				rc = ENOENT;
+				goto done;
+			}
+			entry = STAILQ_FIRST(&on_cache);
+			STAILQ_REMOVE_HEAD(&on_cache, entry);
+			free(entry);
+			objnum = (STAILQ_FIRST(&on_cache))->objnum;
+			continue;
+		}
+		if (q - p + 1 > sizeof(element)) {
+			rc = ENAMETOOLONG;
+			goto done;
+		}
+		memcpy(element, p, q - p);
+		element[q - p] = 0;
+		p = q;
 
-		rc = zfs_dnode_stat(spa, &dn, &sb);
-		if (rc)
-			return (rc);
-		if (!S_ISDIR(sb.st_mode))
-			return (ENOTDIR);
+		if ((rc = zfs_dnode_stat(spa, &dn, &sb)) != 0)
+			goto done;
+		if (!S_ISDIR(sb.st_mode)) {
+			rc = ENOTDIR;
+			goto done;
+		}
 
-		parentnum = objnum;
 		rc = zap_lookup(spa, &dn, element, &objnum);
 		if (rc)
-			return (rc);
+			goto done;
 		objnum = ZFS_DIRENT_OBJ(objnum);
 
+		if ((entry = malloc(sizeof(struct obj_list))) == NULL) {
+			rc = ENOMEM;
+			goto done;
+		}
+		entry->objnum = objnum;
+		STAILQ_INSERT_HEAD(&on_cache, entry, entry);
 		rc = objset_get_dnode(spa, &mount->objset, objnum, &dn);
 		if (rc)
-			return (rc);
+			goto done;
 
 		/*
 		 * Check for symlink.
 		 */
 		rc = zfs_dnode_stat(spa, &dn, &sb);
 		if (rc)
-			return (rc);
+			goto done;
 		if (S_ISLNK(sb.st_mode)) {
-			if (symlinks_followed > 10)
-				return (EMLINK);
+			if (symlinks_followed > 10) {
+				rc = EMLINK;
+				goto done;
+			}
 			symlinks_followed++;
 
 			/*
 			 * Read the link value and copy the tail of our
 			 * current path onto the end.
 			 */
-			if (p)
-				strcpy(&path[sb.st_size], p);
-			else
-				path[sb.st_size] = 0;
-			/*
-			 * Second test is purely to silence bogus compiler
-			 * warning about accessing past the end of dn_bonus.
-			 */
-			if (sb.st_size + sizeof(znode_phys_t) <=
-			    dn.dn_bonuslen && sizeof(znode_phys_t) <=
-			    sizeof(dn.dn_bonus)) {
-				memcpy(path, &dn.dn_bonus[sizeof(znode_phys_t)],
-					sb.st_size);
-			} else {
-				rc = dnode_read(spa, &dn, 0, path, sb.st_size);
-				if (rc)
-					return (rc);
+			if (sb.st_size + strlen(p) + 1 > sizeof(path)) {
+				rc = ENAMETOOLONG;
+				goto done;
 			}
+			strcpy(&path[sb.st_size], p);
 
+			rc = zfs_dnode_readlink(spa, &dn, path, sb.st_size);
+			if (rc != 0)
+				goto done;
+
 			/*
 			 * Restart with the new path, starting either at
 			 * the root or at the parent depending whether or
 			 * not the link is relative.
 			 */
 			p = path;
-			if (*p == '/')
-				objnum = rootnum;
-			else
-				objnum = parentnum;
-			objset_get_dnode(spa, &mount->objset, objnum, &dn);
+			if (*p == '/') {
+				while (STAILQ_FIRST(&on_cache) !=
+				    STAILQ_LAST(&on_cache, obj_list, entry)) {
+					entry = STAILQ_FIRST(&on_cache);
+					STAILQ_REMOVE_HEAD(&on_cache, entry);
+					free(entry);
+				}
+			} else {
+				entry = STAILQ_FIRST(&on_cache);
+				STAILQ_REMOVE_HEAD(&on_cache, entry);
+				free(entry);
+			}
+			objnum = (STAILQ_FIRST(&on_cache))->objnum;
 		}
 	}
 
 	*dnode = dn;
-	return (0);
+done:
+	STAILQ_FOREACH(entry, &on_cache, entry)
+		free(entry);
+	return (rc);
 }

Modified: stable/11/sys/cddl/boot/zfs/zfsimpl.h
==============================================================================
--- stable/11/sys/cddl/boot/zfs/zfsimpl.h	Wed Jul 26 15:24:17 2017	(r321524)
+++ stable/11/sys/cddl/boot/zfs/zfsimpl.h	Wed Jul 26 15:29:56 2017	(r321525)
@@ -1070,6 +1070,7 @@ typedef struct sa_hdr_phys {
 #define	SA_UID_OFFSET		24
 #define	SA_GID_OFFSET		32
 #define	SA_PARENT_OFFSET	40
+#define	SA_SYMLINK_OFFSET	160
 
 /*
  * Intent log header - this on disk structure holds fields to manage



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201707261529.v6QFTv4U043105>