svn commit: r314112 - in head/sys: boot/zfs cddl/boot/zfs
Toomas Soome
tsoome at FreeBSD.org
Wed Feb 22 22:00:52 UTC 2017
Author: tsoome
Date: Wed Feb 22 22:00:50 2017
New Revision: 314112
URL: https://svnweb.freebsd.org/changeset/base/314112
Log:
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.
Reviewed by: allanjude, smh
Approved by: allanjude (mentor)
Differential Revision: https://reviews.freebsd.org/D9706
Modified:
head/sys/boot/zfs/zfsimpl.c
head/sys/cddl/boot/zfs/zfsimpl.h
Modified: head/sys/boot/zfs/zfsimpl.c
==============================================================================
--- head/sys/boot/zfs/zfsimpl.c Wed Feb 22 21:50:37 2017 (r314111)
+++ head/sys/boot/zfs/zfsimpl.c Wed Feb 22 22:00:50 2017 (r314112)
@@ -2264,6 +2264,61 @@ zfs_dnode_stat(const spa_t *spa, dnode_p
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.
*/
@@ -2271,7 +2326,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;
@@ -2279,6 +2334,8 @@ zfs_lookup(const struct zfsmount *mount,
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) {
@@ -2287,87 +2344,119 @@ zfs_lookup(const struct zfsmount *mount,
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, sizeof (rootnum), 1, &rootnum);
- if (rc)
+ rc = zap_lookup(spa, &dn, ZFS_ROOT_OBJ, sizeof (objnum), 1, &objnum);
+ 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++;
- rc = zfs_dnode_stat(spa, &dn, &sb);
- if (rc)
- return (rc);
- if (!S_ISDIR(sb.st_mode))
- return (ENOTDIR);
+ /* 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;
+
+ 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, sizeof (objnum), 1, &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
@@ -2375,14 +2464,25 @@ zfs_lookup(const struct zfsmount *mount,
* 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: head/sys/cddl/boot/zfs/zfsimpl.h
==============================================================================
--- head/sys/cddl/boot/zfs/zfsimpl.h Wed Feb 22 21:50:37 2017 (r314111)
+++ head/sys/cddl/boot/zfs/zfsimpl.h Wed Feb 22 22:00:50 2017 (r314112)
@@ -1080,6 +1080,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
More information about the svn-src-head
mailing list