diff options
Diffstat (limited to 'stand/libsa')
-rw-r--r-- | stand/libsa/bzipfs.c | 1 | ||||
-rw-r--r-- | stand/libsa/cd9660.c | 1 | ||||
-rw-r--r-- | stand/libsa/dosfs.c | 1 | ||||
-rw-r--r-- | stand/libsa/environment.c | 17 | ||||
-rw-r--r-- | stand/libsa/ext2fs.c | 1 | ||||
-rw-r--r-- | stand/libsa/globals.c | 1 | ||||
-rw-r--r-- | stand/libsa/gzipfs.c | 1 | ||||
-rw-r--r-- | stand/libsa/libsa.3 | 4 | ||||
-rw-r--r-- | stand/libsa/mount.c | 5 | ||||
-rw-r--r-- | stand/libsa/net.h | 1 | ||||
-rw-r--r-- | stand/libsa/nfs.c | 1 | ||||
-rw-r--r-- | stand/libsa/open.c | 41 | ||||
-rw-r--r-- | stand/libsa/pkgfs.c | 1 | ||||
-rw-r--r-- | stand/libsa/splitfs.c | 1 | ||||
-rw-r--r-- | stand/libsa/stand.h | 7 | ||||
-rw-r--r-- | stand/libsa/tftp.c | 154 | ||||
-rw-r--r-- | stand/libsa/ufs.c | 1 | ||||
-rw-r--r-- | stand/libsa/zfs/zfsimpl.c | 227 |
18 files changed, 325 insertions, 141 deletions
diff --git a/stand/libsa/bzipfs.c b/stand/libsa/bzipfs.c index f4002796f0ae..ff7ec16e7dc6 100644 --- a/stand/libsa/bzipfs.c +++ b/stand/libsa/bzipfs.c @@ -68,6 +68,7 @@ static int bzf_stat(struct open_file *f, struct stat *sb); #ifndef REGRESSION struct fs_ops bzipfs_fsops = { .fs_name = "bzip", + .fs_flags = 0, .fo_open = bzf_open, .fo_close = bzf_close, .fo_read = bzf_read, diff --git a/stand/libsa/cd9660.c b/stand/libsa/cd9660.c index 973a7dddcda9..d1da39aa479a 100644 --- a/stand/libsa/cd9660.c +++ b/stand/libsa/cd9660.c @@ -81,6 +81,7 @@ static ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f, struct fs_ops cd9660_fsops = { .fs_name = "cd9660", + .fs_flags = 0, .fo_open = cd9660_open, .fo_close = cd9660_close, .fo_read = cd9660_read, diff --git a/stand/libsa/dosfs.c b/stand/libsa/dosfs.c index aca198cdf6fa..38610d917007 100644 --- a/stand/libsa/dosfs.c +++ b/stand/libsa/dosfs.c @@ -61,6 +61,7 @@ static int dos_unmount(const char *dev, void *data); struct fs_ops dosfs_fsops = { .fs_name = "dosfs", + .fs_flags = 0, .fo_open = dos_open, .fo_close = dos_close, .fo_read = dos_read, diff --git a/stand/libsa/environment.c b/stand/libsa/environment.c index 95ee1718f8d4..d139249a8e84 100644 --- a/stand/libsa/environment.c +++ b/stand/libsa/environment.c @@ -66,6 +66,17 @@ env_setenv(const char *name, int flags, const void *value, if ((ev = env_getenv(name)) != NULL) { /* + * If the new value doesn't have NOKENV set, we'll drop the flag + * if it's set on the entry so that the override propagates + * correctly. We do this *before* sending it to the hook in + * case the hook declines to operate on it (e.g., because the + * value matches what was already set) -- we would still want + * the explicitly set value to propagate. + */ + if (!(flags & EV_NOKENV)) + ev->ev_flags &= ~EV_NOKENV; + + /* * If there's a set hook, let it do the work * (unless we are working for one already). */ @@ -77,7 +88,6 @@ env_setenv(const char *name, int flags, const void *value, free(ev->ev_value); ev->ev_value = NULL; ev->ev_flags &= ~EV_DYNAMIC; - } else { /* @@ -123,12 +133,13 @@ env_setenv(const char *name, int flags, const void *value, /* If we have a new value, use it */ if (flags & EV_VOLATILE) { ev->ev_value = strdup(value); - ev->ev_flags |= EV_DYNAMIC; + flags |= EV_DYNAMIC; } else { ev->ev_value = (char *)value; - ev->ev_flags |= flags & EV_DYNAMIC; } + ev->ev_flags |= flags & (EV_DYNAMIC | EV_NOKENV); + return (0); } diff --git a/stand/libsa/ext2fs.c b/stand/libsa/ext2fs.c index 47812f4543a1..f7096282f156 100644 --- a/stand/libsa/ext2fs.c +++ b/stand/libsa/ext2fs.c @@ -106,6 +106,7 @@ static int dtmap[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, struct fs_ops ext2fs_fsops = { .fs_name = "ext2fs", + .fs_flags = 0, .fo_open = ext2fs_open, .fo_close = ext2fs_close, .fo_read = ext2fs_read, diff --git a/stand/libsa/globals.c b/stand/libsa/globals.c index 2797045d4faf..6bd3a4243d73 100644 --- a/stand/libsa/globals.c +++ b/stand/libsa/globals.c @@ -17,6 +17,7 @@ u_char bcea[6] = BA; /* broadcast ethernet address */ char rootpath[FNAME_SIZE] = "/"; /* root mount path */ +int rootport; /* port for rootpath server */ char bootfile[FNAME_SIZE]; /* bootp says to boot this */ char hostname[FNAME_SIZE]; /* our hostname */ int hostnamelen; diff --git a/stand/libsa/gzipfs.c b/stand/libsa/gzipfs.c index 6c2b8cac9e34..6b22f750f3ef 100644 --- a/stand/libsa/gzipfs.c +++ b/stand/libsa/gzipfs.c @@ -50,6 +50,7 @@ static int zf_stat(struct open_file *f, struct stat *sb); struct fs_ops gzipfs_fsops = { .fs_name = "zip", + .fs_flags = 0, .fo_open = zf_open, .fo_close = zf_close, .fo_read = zf_read, diff --git a/stand/libsa/libsa.3 b/stand/libsa/libsa.3 index 3e3f70610516..0947f97a0a1f 100644 --- a/stand/libsa/libsa.3 +++ b/stand/libsa/libsa.3 @@ -781,6 +781,10 @@ The same as but for .Xr bzip2 1 Ns -compressed files. +.It Va pkgfs_fsops +File access from a tar file typically streamed via TFTP. +The order of files in the tar file must match the order they are +to be consumed as rewind is not practical. .El .Pp The array of diff --git a/stand/libsa/mount.c b/stand/libsa/mount.c index 73bf6ab8118c..c866dc9c7055 100644 --- a/stand/libsa/mount.c +++ b/stand/libsa/mount.c @@ -107,7 +107,10 @@ mount(const char *dev, const char *path, int flags __unused, void *data) fs = file_system[i]; if (fs->fo_mount == NULL) continue; - + DEBUG_PRINTF(1,("%s: fs=%s path=%s\n", + __func__, fs->fs_name, path)); + if (is_tftp()) + break; if (fs->fo_mount(dev, path, &data) != 0) continue; diff --git a/stand/libsa/net.h b/stand/libsa/net.h index d4823d88f58b..945b6b9ea45f 100644 --- a/stand/libsa/net.h +++ b/stand/libsa/net.h @@ -75,6 +75,7 @@ enum net_proto { extern u_char bcea[6]; extern char rootpath[FNAME_SIZE]; +extern int rootport; extern char bootfile[FNAME_SIZE]; extern char hostname[FNAME_SIZE]; extern int hostnamelen; diff --git a/stand/libsa/nfs.c b/stand/libsa/nfs.c index ee6af8a726c7..f3e9060c9881 100644 --- a/stand/libsa/nfs.c +++ b/stand/libsa/nfs.c @@ -131,6 +131,7 @@ struct nfs_iodesc nfs_root_node; struct fs_ops nfs_fsops = { .fs_name = "nfs", + .fs_flags = 0, .fo_open = nfs_open, .fo_close = nfs_close, .fo_read = nfs_read, diff --git a/stand/libsa/open.c b/stand/libsa/open.c index ccee4aa5c07b..91848aca7dbe 100644 --- a/stand/libsa/open.c +++ b/stand/libsa/open.c @@ -138,6 +138,8 @@ open(const char *fname, int mode) struct fs_ops *fs; struct open_file *f; int fd, i, error, besterror; + bool is_dir; + size_t n; const char *file; TSENTER(); @@ -154,31 +156,42 @@ open(const char *fname, int mode) f->f_devdata = NULL; file = NULL; + if (exclusive_file_system == NULL || + (exclusive_file_system->fs_flags & FS_OPS_NO_DEVOPEN) == 0) { + error = devopen(f, fname, &file); + if (error || + (((f->f_flags & F_NODEV) == 0) && f->f_dev == NULL)) + goto err; + + /* see if we opened a raw device; otherwise, 'file' is the file name. */ + if (file == NULL || *file == '\0') { + f->f_flags |= F_RAW; + f->f_rabuf = NULL; + TSEXIT(); + return (fd); + } + } else + file = fname; + if (exclusive_file_system != NULL) { + /* loader is forcing the filesystem to be used */ fs = exclusive_file_system; - error = (fs->fo_open)(fname, f); + error = (fs->fo_open)(file, f); if (error == 0) goto ok; goto err; } - error = devopen(f, fname, &file); - if (error || - (((f->f_flags & F_NODEV) == 0) && f->f_dev == NULL)) - goto err; - - /* see if we opened a raw device; otherwise, 'file' is the file name. */ - if (file == NULL || *file == '\0') { - f->f_flags |= F_RAW; - f->f_rabuf = NULL; - TSEXIT(); - return (fd); - } - /* pass file name to the different filesystem open routines */ besterror = ENOENT; + n = strlen(file); + is_dir = (n > 0 && file[n - 1] == '/'); for (i = 0; file_system[i] != NULL; i++) { fs = file_system[i]; + if (is_dir && is_tftp()) { + error = EOPNOTSUPP; + goto err; + } error = (fs->fo_open)(file, f); if (error == 0) goto ok; diff --git a/stand/libsa/pkgfs.c b/stand/libsa/pkgfs.c index 32d488de5cfb..6eb3badf7068 100644 --- a/stand/libsa/pkgfs.c +++ b/stand/libsa/pkgfs.c @@ -41,6 +41,7 @@ static off_t pkg_atol(const char *, unsigned); struct fs_ops pkgfs_fsops = { .fs_name = "pkg", + .fs_flags = FS_OPS_NO_DEVOPEN, .fo_open = pkg_open, .fo_close = pkg_close, .fo_read = pkg_read, diff --git a/stand/libsa/splitfs.c b/stand/libsa/splitfs.c index 69912522000e..eb4b3a1feb11 100644 --- a/stand/libsa/splitfs.c +++ b/stand/libsa/splitfs.c @@ -50,6 +50,7 @@ static int splitfs_stat(struct open_file *f, struct stat *sb); struct fs_ops splitfs_fsops = { .fs_name = "split", + .fs_flags = 0, .fo_open = splitfs_open, .fo_close = splitfs_close, .fo_read = splitfs_read, diff --git a/stand/libsa/stand.h b/stand/libsa/stand.h index 0e99d8778fa6..aaba0aa7fb39 100644 --- a/stand/libsa/stand.h +++ b/stand/libsa/stand.h @@ -94,6 +94,8 @@ __BEGIN_DECLS struct open_file; +#define FS_OPS_NO_DEVOPEN 1 + /* * This structure is used to define file system operations in a file system * independent way. @@ -104,6 +106,7 @@ struct open_file; */ struct fs_ops { const char *fs_name; + int fs_flags; int (*fo_open)(const char *path, struct open_file *f); int (*fo_close)(struct open_file *f); int (*fo_read)(struct open_file *f, void *buf, @@ -349,6 +352,7 @@ extern int pager_file(const char *fname); #define EV_DYNAMIC (1<<0) /* value was dynamically allocated, free if changed/unset */ #define EV_VOLATILE (1<<1) /* value is volatile, make a copy of it */ #define EV_NOHOOK (1<<2) /* don't call hook when setting */ +#define EV_NOKENV (1<<3) /* don't add to kenv (loader-only) */ struct env_var; typedef char *(ev_format_t)(struct env_var *ev); @@ -504,6 +508,9 @@ extern void *reallocf(void *, size_t); */ caddr_t ptov(uintptr_t); +/* dev_net.c */ +bool is_tftp(void); + /* features.c */ typedef void (feature_iter_fn)(void *, const char *, const char *, bool); diff --git a/stand/libsa/tftp.c b/stand/libsa/tftp.c index c6cc8f11a765..656c402683bb 100644 --- a/stand/libsa/tftp.c +++ b/stand/libsa/tftp.c @@ -50,6 +50,10 @@ #include <netinet/in_systm.h> #include <arpa/tftp.h> +#ifdef LOADER_VERIEXEC +#include <verify_file.h> +#endif + #include <string.h> #include "stand.h" @@ -73,6 +77,7 @@ static int tftp_preload(struct open_file *); struct fs_ops tftp_fsops = { .fs_name = "tftp", + .fs_flags = 0, .fo_open = tftp_open, .fo_close = tftp_close, .fo_read = tftp_read, @@ -84,7 +89,6 @@ struct fs_ops tftp_fsops = { }; static int tftpport = 2000; -static int is_open = 0; /* * The legacy TFTP_BLKSIZE value was SEGSIZE(512). @@ -98,10 +102,14 @@ static int is_open = 0; * Jumbo frames in the future. */ #define TFTP_MAX_BLKSIZE 9008 -#define TFTP_TRIES 2 +#define TFTP_TRIES 3 struct tftp_handle { struct iodesc *iodesc; + struct iodesc io; + int id; + ino_t ino; + int port; int currblock; /* contents of lastdata */ unsigned int islastblock:1; /* flag */ unsigned int tries:4; /* number of read attempts */ @@ -177,6 +185,9 @@ tftp_sendack(struct tftp_handle *h, u_short block) wbuf.t.th_block = htons(block); wtail += 2; + DEBUG_PRINTF(5,("%s: myport=%hu xid=%lu, block=%hu\n", + __func__, h->iodesc->myport, h->iodesc->xid, block)); + sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); } @@ -190,6 +201,7 @@ recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft, void *ptr = NULL; ssize_t len; int tftp_error; + unsigned short block; errno = 0; extra = recv_extra; @@ -203,19 +215,22 @@ recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft, } extra->rtype = ntohs(t->th_opcode); - switch (ntohs(t->th_opcode)) { + block = ntohs(t->th_block); + DEBUG_PRINTF(6,("%s: myport=%hu xid=%lu, block=%hu, opcode=%hu\n", + __func__, d->myport, d->xid, block, extra->rtype)); + switch (extra->rtype) { case DATA: { int got; - if (htons(t->th_block) < (u_short)d->xid) { + if (block < (u_short)d->xid) { /* * Apparently our ACK was missed, re-send. */ - tftp_sendack(h, htons(t->th_block)); + tftp_sendack(h, block); free(ptr); return (-1); } - if (htons(t->th_block) != (u_short)d->xid) { + if (block != (u_short)d->xid) { /* * Packet from the future, drop this. */ @@ -241,9 +256,7 @@ recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft, printf("illegal tftp error %d\n", tftp_error); errno = EIO; } else { -#ifdef TFTP_DEBUG - printf("tftp-error %d\n", tftp_error); -#endif + DEBUG_PRINTF(0, ("tftp-error %d\n", tftp_error)); errno = tftperrors[tftp_error]; } free(ptr); @@ -284,9 +297,7 @@ recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft, return (0); } default: -#ifdef TFTP_DEBUG - printf("tftp type %d not handled\n", ntohs(t->th_opcode)); -#endif + DEBUG_PRINTF(0, ("tftp type %hu not handled\n", extra->rtype)); free(ptr); return (-1); } @@ -343,7 +354,7 @@ tftp_makereq(struct tftp_handle *h) bcopy("0", wtail, 2); wtail += 2; - h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff)); + h->iodesc->myport = htons(h->port + (getsecs() & 0x3ff)); h->iodesc->destport = htons(IPPORT_TFTP); h->iodesc->xid = 1; /* expected block */ @@ -351,11 +362,15 @@ tftp_makereq(struct tftp_handle *h) h->islastblock = 0; h->validsize = 0; + DEBUG_PRINTF(5,("%s: %s: id=%d port=%d myport=%hu xid=1\n", + __func__, h->path, h->id, h->port, ntohs(h->iodesc->myport))); pkt = NULL; recv_extra.tftp_handle = h; res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *)&wbuf.t, &recvtftp, &pkt, (void **)&t, &recv_extra); if (res == -1) { + DEBUG_PRINTF(3,("%s: %s: id=%d errno=%d\n", + __func__, h->path, h->id, errno)); free(pkt); return (errno); } @@ -410,12 +425,18 @@ tftp_getnextblock(struct tftp_handle *h) h->iodesc->xid = h->currblock + 1; /* expected block */ + DEBUG_PRINTF(5,("%s: %s: id=%d port=%d myport=%hu xid=%lu\n", + __func__, h->path, h->id, h->port, + ntohs(h->iodesc->myport), h->iodesc->xid)); + pkt = NULL; recv_extra.tftp_handle = h; res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *)&wbuf.t, &recvtftp, &pkt, (void **)&t, &recv_extra); if (res == -1) { /* 0 is OK! */ + DEBUG_PRINTF(3,("%s: %s: id=%d errno=%d\n", + __func__, h->path, h->id, errno)); free(pkt); return (errno); } @@ -428,21 +449,32 @@ tftp_getnextblock(struct tftp_handle *h) if (res < h->tftp_blksize) h->islastblock = 1; /* EOF */ - if (h->islastblock == 1) { + DEBUG_PRINTF(5,("%s: %s: id=%d res=%d blksz=%d last=%d\n", + __func__, h->path, h->id, res, h->tftp_blksize, h->islastblock)); + + if (h->islastblock) { /* Send an ACK for the last block */ - wbuf.t.th_block = htons((u_short)h->currblock); - sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); + tftp_sendack(h, h->currblock); } return (0); } +/* + * If doing verification we need to handle multiple + * files at the same time. + */ +#define TOPEN_MAX 8 +static struct tftp_handle *handles[TOPEN_MAX]; + static int tftp_open(const char *path, struct open_file *f) { struct devdesc *dev; struct tftp_handle *tftpfile; struct iodesc *io; + static int lx = 0; + int i, x; int res; size_t pathsize; const char *extraslash; @@ -450,24 +482,39 @@ tftp_open(const char *path, struct open_file *f) if (netproto != NET_TFTP) return (EINVAL); - if (f->f_dev->dv_type != DEVT_NET) + if (f->f_dev == NULL || f->f_dev->dv_type != DEVT_NET) return (EINVAL); - if (is_open) + tftpfile = NULL; + for (x = lx + 1, i = 0; i < TOPEN_MAX; i++, x++) { + x %= TOPEN_MAX; + if (handles[x] == NULL) { + handles[x] = tftpfile = calloc(1, sizeof(*tftpfile)); + if (tftpfile == NULL) + return (ENOMEM); + /* id allows us to clear the slot on close */ + tftpfile->id = lx = x; + /* port ensures a different session with server */ + tftpfile->port = (tftpport + (x * tftpport)) & 0xffff; + DEBUG_PRINTF(1, ("%s(%s) id=%d port=%d\n", + __func__, path, tftpfile->id, tftpfile->port)); + break; + } + } + if (tftpfile == NULL) { + DEBUG_PRINTF(1, ("%s: EBUSY\n", __func__)); return (EBUSY); - - tftpfile = calloc(1, sizeof(*tftpfile)); - if (!tftpfile) - return (ENOMEM); - + } tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE; dev = f->f_devdata; - tftpfile->iodesc = io = socktodesc(*(int *)(dev->d_opendata)); + io = socktodesc(*(int *)(dev->d_opendata)); if (io == NULL) { free(tftpfile); return (EINVAL); } + memcpy(&tftpfile->io, io, sizeof(tftpfile->io)); + io = tftpfile->iodesc = &tftpfile->io; io->destip = rootip; tftpfile->off = 0; pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char); @@ -480,8 +527,11 @@ tftp_open(const char *path, struct open_file *f) extraslash = ""; else extraslash = "/"; - res = snprintf(tftpfile->path, pathsize, "%s%s%s", - rootpath, extraslash, path); + if (rootpath[0] == '/' && rootpath[1] == '\0' && path[0] == '/') + res = strlcpy(tftpfile->path, path, pathsize); + else + res = snprintf(tftpfile->path, pathsize, "%s%s%s", + rootpath, extraslash, path); if (res < 0 || res > pathsize) { free(tftpfile->path); free(tftpfile); @@ -491,13 +541,13 @@ tftp_open(const char *path, struct open_file *f) res = tftp_makereq(tftpfile); if (res) { + handles[tftpfile->id] = NULL; free(tftpfile->path); free(tftpfile->pkt); free(tftpfile); return (res); } f->f_fsdata = tftpfile; - is_open = 1; return (0); } @@ -547,9 +597,7 @@ tftp_read(struct open_file *f, void *addr, size_t size, rc = tftp_getnextblock(tftpfile); if (rc) { /* no answer */ -#ifdef TFTP_DEBUG - printf("tftp: read error\n"); -#endif + DEBUG_PRINTF(0, ("tftp: read error\n")); if (tftpfile->tries > TFTP_TRIES) { return (rc); } else { @@ -568,10 +616,8 @@ tftp_read(struct open_file *f, void *addr, size_t size, inbuffer = tftpfile->validsize - offinblock; if (inbuffer < 0) { -#ifdef TFTP_DEBUG - printf("tftp: invalid offset %d\n", - tftpfile->off); -#endif + DEBUG_PRINTF(0, ("tftp: invalid offset %d\n", + tftpfile->off)); return (EINVAL); } count = (size < inbuffer ? size : inbuffer); @@ -586,15 +632,15 @@ tftp_read(struct open_file *f, void *addr, size_t size, if ((tftpfile->islastblock) && (count == inbuffer)) break; /* EOF */ } else { -#ifdef TFTP_DEBUG - printf("tftp: block %d not found\n", needblock); -#endif + DEBUG_PRINTF(0, ("tftp: block %d not found\n", needblock)); return (EINVAL); } } out: + DEBUG_PRINTF(4, ("%s(%s) res=%ld\n", __func__, tftpfile->path, + (tftpfile->tftp_tsize - tftpfile->off))); if (resid != NULL) *resid = res; return (rc); @@ -610,15 +656,18 @@ tftp_close(struct open_file *f) tftp_senderr(tftpfile, 0, "No error: file closed"); if (tftpfile) { + DEBUG_PRINTF(1, ("%s(%d): %s\n", __func__, + tftpfile->id, tftpfile->path)); + handles[tftpfile->id] = NULL; free(tftpfile->path); free(tftpfile->pkt); free(tftpfile->tftp_cache); free(tftpfile); } - is_open = 0; return (0); } + static int tftp_stat(struct open_file *f, struct stat *sb) { @@ -630,6 +679,29 @@ tftp_stat(struct open_file *f, struct stat *sb) sb->st_uid = 0; sb->st_gid = 0; sb->st_size = tftpfile->tftp_tsize; + sb->st_mtime = 0; +#ifdef LOADER_VERIEXEC + /* libsecureboot needs st_dev and st_ino at minimum; + * we need to fake something that will be close enough to + * unique. + */ + sb->st_dev = (dev_t)tftpfile->iodesc->destip.s_addr; + /* we don't want to compute this more than once */ + if (tftpfile->ino == 0) { + union { + unsigned char digest[SHA_DIGEST_LENGTH]; + ino_t ino; + } u; + + hash_string(tftpfile->path, 0, u.digest, sizeof(u.digest)); + + tftpfile->ino = u.ino & 0x7fffffff; + DEBUG_PRINTF(2,("%s(%s) dev=%lu ino=%lu\n", __func__, + tftpfile->path, (unsigned long)sb->st_dev, + (unsigned long)tftpfile->ino)); + } + sb->st_ino = tftpfile->ino; +#endif return (0); } @@ -827,9 +899,7 @@ tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len) return (-1); } -#ifdef TFTP_DEBUG - printf("tftp_blksize: %u\n", h->tftp_blksize); - printf("tftp_tsize: %lu\n", h->tftp_tsize); -#endif + DEBUG_PRINTF(2, ("tftp_blksize: %u\n", h->tftp_blksize)); + DEBUG_PRINTF(2, ("tftp_tsize: %lu\n", h->tftp_tsize)); return (0); } diff --git a/stand/libsa/ufs.c b/stand/libsa/ufs.c index e1d540ed2321..86cd3be9a27a 100644 --- a/stand/libsa/ufs.c +++ b/stand/libsa/ufs.c @@ -93,6 +93,7 @@ static int ufs_unmount(const char *dev, void *data); struct fs_ops ufs_fsops = { .fs_name = "ufs", + .fs_flags = 0, .fo_open = ufs_open, .fo_close = ufs_close, .fo_read = ufs_read, diff --git a/stand/libsa/zfs/zfsimpl.c b/stand/libsa/zfs/zfsimpl.c index 971d71d098d3..f15d9b016068 100644 --- a/stand/libsa/zfs/zfsimpl.c +++ b/stand/libsa/zfs/zfsimpl.c @@ -107,11 +107,6 @@ typedef struct indirect_vsd { } indirect_vsd_t; /* - * List of all vdevs, chained through v_alllink. - */ -static vdev_list_t zfs_vdevs; - -/* * List of supported read-incompatible ZFS features. Do not add here features * marked as ZFEATURE_FLAG_READONLY_COMPAT, they are irrelevant for read-only! */ @@ -167,7 +162,6 @@ vdev_indirect_mapping_entry_phys_t * static void zfs_init(void) { - STAILQ_INIT(&zfs_vdevs); STAILQ_INIT(&zfs_pools); dnode_cache_buf = malloc(SPA_MAXBLOCKSIZE); @@ -839,16 +833,27 @@ vdev_replacing_read(vdev_t *vdev, const blkptr_t *bp, void *buf, return (kid->v_read(kid, bp, buf, offset, bytes)); } +/* + * List of vdevs that were fully initialized from their own label, but later a + * newer label was found that obsoleted the stale label, freeing its + * configuration tree. We keep those vdevs around, since a new configuration + * may include them. + */ +static vdev_list_t orphans = STAILQ_HEAD_INITIALIZER(orphans); + static vdev_t * -vdev_find(uint64_t guid) +vdev_find(vdev_list_t *list, uint64_t guid) { - vdev_t *vdev; + vdev_t *vdev, *safe; - STAILQ_FOREACH(vdev, &zfs_vdevs, v_alllink) + STAILQ_FOREACH_SAFE(vdev, list, v_childlink, safe) { if (vdev->v_guid == guid) return (vdev); + if ((vdev = vdev_find(&vdev->v_children, guid)) != NULL) + return (vdev); + } - return (0); + return (NULL); } static vdev_t * @@ -857,6 +862,11 @@ vdev_create(uint64_t guid, vdev_read_t *_read) vdev_t *vdev; vdev_indirect_config_t *vic; + if ((vdev = vdev_find(&orphans, guid))) { + STAILQ_REMOVE(&orphans, vdev, vdev, v_childlink); + return (vdev); + } + vdev = calloc(1, sizeof(vdev_t)); if (vdev != NULL) { STAILQ_INIT(&vdev->v_children); @@ -871,7 +881,6 @@ vdev_create(uint64_t guid, vdev_read_t *_read) if (_read != NULL) { vic = &vdev->vdev_indirect_config; vic->vic_prev_indirect_vdev = UINT64_MAX; - STAILQ_INSERT_TAIL(&zfs_vdevs, vdev, v_alllink); } } @@ -1035,22 +1044,19 @@ vdev_init(uint64_t guid, const nvlist_t *nvlist, vdev_t **vdevp) * STAILQ_INSERT_AFTER. */ static vdev_t * -vdev_find_previous(vdev_t *top_vdev, vdev_t *vdev) +vdev_find_previous(vdev_t *top_vdev, uint64_t id) { vdev_t *v, *previous; - if (STAILQ_EMPTY(&top_vdev->v_children)) - return (NULL); - previous = NULL; STAILQ_FOREACH(v, &top_vdev->v_children, v_childlink) { - if (v->v_id > vdev->v_id) + if (v->v_id > id) return (previous); - if (v->v_id == vdev->v_id) + if (v->v_id == id) return (v); - if (v->v_id < vdev->v_id) + if (v->v_id < id) previous = v; } return (previous); @@ -1072,7 +1078,7 @@ vdev_child_count(vdev_t *vdev) /* * Insert vdev into top_vdev children list. List is ordered by v_id. */ -static void +static vdev_t * vdev_insert(vdev_t *top_vdev, vdev_t *vdev) { vdev_t *previous; @@ -1085,7 +1091,7 @@ vdev_insert(vdev_t *top_vdev, vdev_t *vdev) * so we can use either STAILQ_INSERT_HEAD or STAILQ_INSERT_AFTER * as STAILQ does not have insert before. */ - previous = vdev_find_previous(top_vdev, vdev); + previous = vdev_find_previous(top_vdev, vdev->v_id); if (previous == NULL) { STAILQ_INSERT_HEAD(&top_vdev->v_children, vdev, v_childlink); @@ -1094,7 +1100,8 @@ vdev_insert(vdev_t *top_vdev, vdev_t *vdev) * This vdev was configured from label config, * do not insert duplicate. */ - return; + free(vdev); + return (previous); } else { STAILQ_INSERT_AFTER(&top_vdev->v_children, previous, vdev, v_childlink); @@ -1103,26 +1110,28 @@ vdev_insert(vdev_t *top_vdev, vdev_t *vdev) count = vdev_child_count(top_vdev); if (top_vdev->v_nchildren < count) top_vdev->v_nchildren = count; + return (vdev); } static int -vdev_from_nvlist(spa_t *spa, uint64_t top_guid, uint64_t txg, - const nvlist_t *nvlist) +vdev_from_nvlist(spa_t *spa, uint64_t top_guid, uint64_t label_guid, + uint64_t txg, const nvlist_t *nvlist) { vdev_t *top_vdev, *vdev; nvlist_t **kids = NULL; int rc, nkids; /* Get top vdev. */ - top_vdev = vdev_find(top_guid); + top_vdev = vdev_find(&spa->spa_root_vdev->v_children, top_guid); if (top_vdev == NULL) { rc = vdev_init(top_guid, nvlist, &top_vdev); if (rc != 0) return (rc); top_vdev->v_spa = spa; top_vdev->v_top = top_vdev; + top_vdev->v_label = label_guid; top_vdev->v_txg = txg; - vdev_insert(spa->spa_root_vdev, top_vdev); + (void )vdev_insert(spa->spa_root_vdev, top_vdev); } /* Add children if there are any. */ @@ -1143,7 +1152,7 @@ vdev_from_nvlist(spa_t *spa, uint64_t top_guid, uint64_t txg, vdev->v_spa = spa; vdev->v_top = top_vdev; - vdev_insert(top_vdev, vdev); + vdev = vdev_insert(top_vdev, vdev); } } else { /* @@ -1162,30 +1171,6 @@ done: return (rc); } -static int -vdev_init_from_label(spa_t *spa, const nvlist_t *nvlist) -{ - uint64_t pool_guid, top_guid, txg; - nvlist_t *vdevs; - int rc; - - if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64, - NULL, &pool_guid, NULL) || - nvlist_find(nvlist, ZPOOL_CONFIG_TOP_GUID, DATA_TYPE_UINT64, - NULL, &top_guid, NULL) || - nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG, DATA_TYPE_UINT64, - NULL, &txg, NULL) != 0 || - nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST, - NULL, &vdevs, NULL)) { - printf("ZFS: can't find vdev details\n"); - return (ENOENT); - } - - rc = vdev_from_nvlist(spa, top_guid, txg, vdevs); - nvlist_destroy(vdevs); - return (rc); -} - static void vdev_set_state(vdev_t *vdev) { @@ -1232,14 +1217,14 @@ vdev_set_state(vdev_t *vdev) } static int -vdev_update_from_nvlist(uint64_t top_guid, const nvlist_t *nvlist) +vdev_update_from_nvlist(vdev_t *root, uint64_t top_guid, const nvlist_t *nvlist) { vdev_t *vdev; nvlist_t **kids = NULL; int rc, nkids; /* Update top vdev. */ - vdev = vdev_find(top_guid); + vdev = vdev_find(&root->v_children, top_guid); if (vdev != NULL) vdev_set_initial_state(vdev, nvlist); @@ -1255,7 +1240,7 @@ vdev_update_from_nvlist(uint64_t top_guid, const nvlist_t *nvlist) if (rc != 0) break; - vdev = vdev_find(guid); + vdev = vdev_find(&root->v_children, guid); if (vdev != NULL) vdev_set_initial_state(vdev, kids[i]); } @@ -1271,10 +1256,6 @@ vdev_update_from_nvlist(uint64_t top_guid, const nvlist_t *nvlist) return (rc); } -/* - * Shall not be called on root vdev, that is not linked into zfs_vdevs. - * See comment in vdev_create(). - */ static void vdev_free(struct vdev *vdev) { @@ -1282,8 +1263,10 @@ vdev_free(struct vdev *vdev) STAILQ_FOREACH_SAFE(kid, &vdev->v_children, v_childlink, safe) vdev_free(kid); - STAILQ_REMOVE(&zfs_vdevs, vdev, vdev, v_alllink); - free(vdev); + if (vdev->v_phys_read != NULL) + STAILQ_INSERT_HEAD(&orphans, vdev, v_childlink); + else + free(vdev); } static int @@ -1329,15 +1312,16 @@ vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist) NULL, &guid, NULL); if (rc != 0) break; - vdev = vdev_find(guid); + vdev = vdev_find(&spa->spa_root_vdev->v_children, guid); /* * Top level vdev is missing, create it. * XXXGL: how can this happen? */ if (vdev == NULL) - rc = vdev_from_nvlist(spa, guid, 0, kids[i]); + rc = vdev_from_nvlist(spa, guid, 0, 0, kids[i]); else - rc = vdev_update_from_nvlist(guid, kids[i]); + rc = vdev_update_from_nvlist(spa->spa_root_vdev, guid, + kids[i]); if (rc != 0) break; } @@ -1355,6 +1339,53 @@ vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist) return (rc); } +static bool +nvlist_find_child_guid(const nvlist_t *nvlist, uint64_t guid) +{ + nvlist_t **kids = NULL; + int nkids, i; + bool rv = false; + + if (nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY, + &nkids, &kids, NULL) != 0) + nkids = 0; + + for (i = 0; i < nkids; i++) { + uint64_t kid_guid; + + if (nvlist_find(kids[i], ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, + NULL, &kid_guid, NULL) != 0) + break; + if (kid_guid == guid) + rv = true; + else + rv = nvlist_find_child_guid(kids[i], guid); + if (rv) + break; + } + + for (i = 0; i < nkids; i++) + nvlist_destroy(kids[i]); + free(kids); + + return (rv); +} + +static bool +nvlist_find_vdev_guid(const nvlist_t *nvlist, uint64_t guid) +{ + nvlist_t *vdevs; + bool rv; + + if (nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST, NULL, + &vdevs, NULL) != 0) + return (false); + rv = nvlist_find_child_guid(vdevs, guid); + nvlist_destroy(vdevs); + + return (rv); +} + static spa_t * spa_find_by_guid(uint64_t guid) { @@ -2023,8 +2054,8 @@ vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv, { vdev_t vtmp; spa_t *spa; - vdev_t *vdev; - nvlist_t *nvl; + vdev_t *vdev, *top; + nvlist_t *nvl, *vdevs; uint64_t val; uint64_t guid, pool_guid, top_guid, txg; const char *pool_name; @@ -2083,6 +2114,7 @@ vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv, if (nvlist_find(nvl, ZPOOL_CONFIG_POOL_TXG, DATA_TYPE_UINT64, NULL, &txg, NULL) != 0 || + txg == 0 || nvlist_find(nvl, ZPOOL_CONFIG_TOP_GUID, DATA_TYPE_UINT64, NULL, &top_guid, NULL) != 0 || nvlist_find(nvl, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64, @@ -2092,7 +2124,7 @@ vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv, nvlist_find(nvl, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, NULL, &guid, NULL) != 0) { /* - * Cache and spare devices end up here - just ignore + * Cache, spare and replaced devices end up here - just ignore * them. */ nvlist_destroy(nvl); @@ -2119,22 +2151,47 @@ vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv, nvlist_destroy(nvl); return (ENOMEM); } - } else { - struct vdev *kid; - - STAILQ_FOREACH(kid, &spa->spa_root_vdev->v_children, - v_childlink) - if (kid->v_guid == top_guid && kid->v_txg < txg) { - printf("ZFS: pool %s vdev %s ignoring stale " - "label from txg 0x%jx, using 0x%jx@0x%jx\n", - spa->spa_name, kid->v_name, - kid->v_txg, guid, txg); + } + + /* + * Check if configuration is already known. If configuration is known + * and txg numbers don't match, we got 2x2 scenarios here. First, is + * the label being read right now _newer_ than the one read before. + * Second, is the vdev that provided the stale label _present_ in the + * newer configuration. If neither is true, we completely ignore the + * label. + */ + STAILQ_FOREACH(top, &spa->spa_root_vdev->v_children, v_childlink) + if (top->v_guid == top_guid) { + bool newer, present; + + if (top->v_txg == txg) + break; + newer = (top->v_txg < txg); + present = newer ? + nvlist_find_vdev_guid(nvl, top->v_label) : + (vdev_find(&top->v_children, guid) != NULL); + printf("ZFS: pool %s vdev %s %s stale label from " + "0x%jx@0x%jx, %s 0x%jx@0x%jx\n", + spa->spa_name, top->v_name, + present ? "using" : "ignoring", + newer ? top->v_label : guid, + newer ? top->v_txg : txg, + present ? "referred by" : "using", + newer ? guid : top->v_label, + newer ? txg : top->v_txg); + if (newer) { STAILQ_REMOVE(&spa->spa_root_vdev->v_children, - kid, vdev, v_childlink); - vdev_free(kid); + top, vdev, v_childlink); + vdev_free(top); + break; + } else if (present) { break; + } else { + nvlist_destroy(nvl); + return (EIO); } - } + } /* * Get the vdev tree and create our in-core copy of it. @@ -2142,14 +2199,22 @@ vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv, * be some kind of alias (overlapping slices, dangerously dedicated * disks etc). */ - vdev = vdev_find(guid); + vdev = vdev_find(&spa->spa_root_vdev->v_children, guid); /* Has this vdev already been inited? */ if (vdev && vdev->v_phys_read) { nvlist_destroy(nvl); return (EIO); } - rc = vdev_init_from_label(spa, nvl); + if (nvlist_find(nvl, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST, NULL, + &vdevs, NULL)) { + printf("ZFS: can't find vdev details\n"); + nvlist_destroy(nvl); + return (ENOENT); + } + + rc = vdev_from_nvlist(spa, top_guid, guid, txg, vdevs); + nvlist_destroy(vdevs); nvlist_destroy(nvl); if (rc != 0) return (rc); @@ -2158,7 +2223,7 @@ vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv, * We should already have created an incomplete vdev for this * vdev. Find it and initialise it with our read proc. */ - vdev = vdev_find(guid); + vdev = vdev_find(&spa->spa_root_vdev->v_children, guid); if (vdev != NULL) { vdev->v_phys_read = _read; vdev->v_phys_write = _write; |