diff --git a/sysutils/bhyve+/Makefile b/sysutils/bhyve+/Makefile index 9945c581697f..4de4b073dbf7 100644 --- a/sysutils/bhyve+/Makefile +++ b/sysutils/bhyve+/Makefile @@ -1,91 +1,96 @@ PORTNAME= bhyve+ PORTVERSION= 0.1.0 CATEGORIES= sysutils MAINTAINER= pali.gabor@gmail.com COMMENT= BHyVe with unofficial extensions LICENSE= BSD2CLAUSE ONLY_FOR_ARCHS= amd64 USES= kmod uidfix .include SRCS= ${SRC_BASE}/sys/modules/vmm \ ${SRC_BASE}/sys/amd64/vmm \ ${SRC_BASE}/lib/libvmmapi \ ${SRC_BASE}/usr.sbin/bhyve \ ${SRC_BASE}/usr.sbin/bhyvectl \ ${SRC_BASE}/usr.sbin/bhyveload \ ${SRC_BASE}/share/mk .for _src in ${SRCS} . if !exists(${_src}) && empty(IGNORE) IGNORE+= requires bhyve and vmm source files in ${_src} . endif .endfor USE_GITHUB= yes GH_ACCOUNT= conclusiveeng GH_PROJECT= lib9p GH_TAGNAME= 7ef466772108856ee3e093735033232a6f1a3f04 USE_LDCONFIG= yes .if 1200000 <= ${OSVERSION} && ${OSVERSION} < 1300000 EXTRA_PATCHES= ${PATCHDIR}/freebsd-12 +.if ${OSVERSION} < 1203000 +EXTRA_PATCHES+= ${PATCHDIR}/freebsd-12/2 +.else +EXTRA_PATCHES+= ${PATCHDIR}/freebsd-12/3 +.endif .elif 1300000 <= ${OSVERSION} && ${OSVERSION} < 1400000 EXTRA_PATCHES= ${PATCHDIR}/freebsd-13 .elif 1400000 <= ${OSVERSION} && ${OSVERSION} < 1500000 EXTRA_PATCHES= ${PATCHDIR}/freebsd-14 .else . if empty(IGNORE) IGNORE+= cannot build: unsupported system . endif .endif WRKSRC= ${WRKDIR} _LINKS= sys/cam/ctl \ sys/dev/e1000 \ sys/dev/mii \ sys/dev/usb/controller \ sys/dev/virtio \ sys/amd64/include \ sys/x86/include \ stand/userboot post-extract: @${MKDIR} ${WRKSRC}/contrib @cd ${WRKSRC} && ${LN} -s ../${GH_PROJECT}-${GH_TAGNAME} contrib/lib9p .for _link in ${_LINKS} @${MKDIR} $$(dirname ${WRKSRC}/${_link}) @cd ${WRKSRC} && ${LN} -s ${SRC_BASE}/${_link} ${_link} .endfor .for _src in ${SRCS} @cd ${_src} && ${COPYTREE_SHARE} . ${WRKDIR}/${_src:S/${SRC_BASE}//1} .endfor @cd ${FILESDIR} && ${COPYTREE_SHARE} kernconf ${WRKSRC} do-build: @cd ${WRKSRC}/contrib/lib9p && \ ${MAKE} SRCTOP=${WRKSRC} SYSDIR=${SRC_BASE}/sys LIB9PDIR= .for _src in ${SRCS} . if exists(${_src}/Makefile) @cd ${WRKSRC}/${_src:S/${SRC_BASE}//1} && \ ${MAKE} SRCTOP=${WRKSRC} SYSDIR=${SRC_BASE}/sys DEBUG_FLAGS=-g \ MK_CTF=yes KERNBUILDDIR=${WRKSRC}/kernconf . endif .endfor do-install: ${INSTALL_LIB} ${WRKSRC}/contrib/lib9p/lib9p.so.1.1 ${STAGEDIR}${PREFIX}/lib ${INSTALL_LIB} ${WRKSRC}/lib/libvmmapi/libvmmapi.so.5.1 ${STAGEDIR}${PREFIX}/lib .for _bin in bhyve bhyveload bhyvectl ${INSTALL_PROGRAM} ${WRKSRC}/usr.sbin/${_bin}/${_bin} ${STAGEDIR}${PREFIX}/sbin ${INSTALL_MAN} ${WRKSRC}/usr.sbin/${_bin}/${_bin}.8.gz ${STAGEDIR}${PREFIX}/man/man8 .endfor ${INSTALL_KLD} ${WRKSRC}/sys/modules/vmm/vmm.ko ${STAGEDIR}${KMODDIR} .include diff --git a/sysutils/bhyve+/files/freebsd-12/2/patch-virtio-9p b/sysutils/bhyve+/files/freebsd-12/2/patch-virtio-9p new file mode 100644 index 000000000000..9ce027d3171a --- /dev/null +++ b/sysutils/bhyve+/files/freebsd-12/2/patch-virtio-9p @@ -0,0 +1,31 @@ +--- usr.sbin/bhyve/bhyve.8.orig 2021-08-19 22:38:50 UTC ++++ usr.sbin/bhyve/bhyve.8 +@@ -242,6 +242,8 @@ Virtio network interface. + Virtio block storage interface. + .It Li virtio-scsi + Virtio SCSI interface. ++.It Li virtio-9p ++Virtio 9p (VirtFS) interface. + .It Li virtio-rnd + Virtio RNG interface. + .It Li virtio-console +@@ -327,6 +329,19 @@ are: + .It Li iid= Ns Ar IID + Initiator ID to use when sending requests to specified CTL port. + The default value is 0. ++.El ++.Pp ++9P devices: ++.Bl -tag -width 10n ++.It Pa sharename=/path/to/share[,9p-device-options] ++.El ++.Pp ++The ++.Ar 9p-device-options ++are: ++.Bl -tag -width 10n ++.It Li ro ++Expose the share in read-only mode. + .El + .Pp + TTY devices: diff --git a/sysutils/bhyve+/files/freebsd-12/3/patch-virtio-9p b/sysutils/bhyve+/files/freebsd-12/3/patch-virtio-9p new file mode 100644 index 000000000000..28457afb3486 --- /dev/null +++ b/sysutils/bhyve+/files/freebsd-12/3/patch-virtio-9p @@ -0,0 +1,31 @@ +--- usr.sbin/bhyve/bhyve.8.orig 2021-12-17 02:14:53.986085000 +0100 ++++ usr.sbin/bhyve/bhyve.8 2021-12-17 02:21:03.716235000 +0100 +@@ -299,6 +299,8 @@ + Virtio block storage interface. + .It Cm virtio-scsi + Virtio SCSI interface. ++.It Cm virtio-9p ++Virtio 9p (VirtFS) interface. + .It Cm virtio-rnd + Virtio RNG interface. + .It Cm virtio-console +@@ -404,6 +406,19 @@ + .It Cm iid= Ns Ar IID + Initiator ID to use when sending requests to specified CTL port. + The default value is 0. ++.El ++.Pp ++9P devices: ++.Bl -tag -width 10n ++.It Pa sharename=/path/to/share[,9p-device-options] ++.El ++.Pp ++The ++.Ar 9p-device-options ++are: ++.Bl -tag -width 10n ++.It Li ro ++Expose the share in read-only mode. + .El + .Pp + TTY device backends: diff --git a/sysutils/bhyve+/files/freebsd-12/patch-virtio-9p b/sysutils/bhyve+/files/freebsd-12/patch-virtio-9p index 6799fd986342..1e14bcd58d61 100644 --- a/sysutils/bhyve+/files/freebsd-12/patch-virtio-9p +++ b/sysutils/bhyve+/files/freebsd-12/patch-virtio-9p @@ -1,416 +1,385 @@ ---- usr.sbin/bhyve/bhyve.8.orig 2021-08-19 22:38:50 UTC -+++ usr.sbin/bhyve/bhyve.8 -@@ -242,6 +242,8 @@ Virtio network interface. - Virtio block storage interface. - .It Li virtio-scsi - Virtio SCSI interface. -+.It Li virtio-9p -+Virtio 9p (VirtFS) interface. - .It Li virtio-rnd - Virtio RNG interface. - .It Li virtio-console -@@ -327,6 +329,19 @@ are: - .It Li iid= Ns Ar IID - Initiator ID to use when sending requests to specified CTL port. - The default value is 0. -+.El -+.Pp -+9P devices: -+.Bl -tag -width 10n -+.It Pa sharename=/path/to/share[,9p-device-options] -+.El -+.Pp -+The -+.Ar 9p-device-options -+are: -+.Bl -tag -width 10n -+.It Li ro -+Expose the share in read-only mode. - .El - .Pp - TTY devices: --- usr.sbin/bhyve/Makefile.orig 2021-08-19 23:00:57 UTC +++ usr.sbin/bhyve/Makefile @@ -3,6 +3,7 @@ # .include +CFLAGS+=-I${SRCTOP}/contrib/lib9p CFLAGS+=-I${SRCTOP}/sys .PATH: ${SRCTOP}/sys/cam/ctl @@ -43,6 +44,7 @@ SRCS= \ pci_lpc.c \ pci_nvme.c \ pci_passthru.c \ + pci_virtio_9p.c \ pci_virtio_block.c \ pci_virtio_console.c \ pci_virtio_net.c \ @@ -71,7 +73,8 @@ SRCS= \ .PATH: ${BHYVE_SYSDIR}/sys/amd64/vmm SRCS+= vmm_instruction_emul.c -LIBADD= vmmapi md pthread z util sbuf cam +LIBADD= md pthread z util sbuf cam casper cap_pwd cap_grp +LDADD= ../../contrib/lib9p/lib9p.so.1.1 ../../lib/libvmmapi/libvmmapi.so.5.1 .if ${MK_INET_SUPPORT} != "no" CFLAGS+=-DINET --- usr.sbin/bhyve/pci_virtio_9p.c.orig 2021-08-19 22:44:10 UTC +++ usr.sbin/bhyve/pci_virtio_9p.c @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 2015 iXsystems Inc. + * Copyright (c) 2017-2018 Jakub Klama + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * VirtIO filesystem passthrough using 9p protocol. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "bhyverun.h" +#include "pci_emul.h" +#include "virtio.h" + +#define VT9P_MAX_IOV 128 +#define VT9P_RINGSZ 256 +#define VT9P_MAXTAGSZ 256 +#define VT9P_CONFIGSPACESZ (VT9P_MAXTAGSZ + sizeof(uint16_t)) + +static int pci_vt9p_debug; +#define DPRINTF(params) if (pci_vt9p_debug) printf params +#define WPRINTF(params) printf params + +/* + * Per-device softc + */ +struct pci_vt9p_softc { + struct virtio_softc vsc_vs; + struct vqueue_info vsc_vq; + pthread_mutex_t vsc_mtx; + uint64_t vsc_cfg; + uint64_t vsc_features; + char * vsc_rootpath; + struct pci_vt9p_config * vsc_config; + struct l9p_backend * vsc_fs_backend; + struct l9p_server * vsc_server; + struct l9p_connection * vsc_conn; +}; + +struct pci_vt9p_request { + struct pci_vt9p_softc * vsr_sc; + struct iovec * vsr_iov; + size_t vsr_niov; + size_t vsr_respidx; + size_t vsr_iolen; + uint16_t vsr_idx; +}; + +struct pci_vt9p_config { + uint16_t tag_len; + char tag[0]; +} __attribute__((packed)); + +static int pci_vt9p_send(struct l9p_request *, const struct iovec *, + const size_t, const size_t, void *); +static void pci_vt9p_drop(struct l9p_request *, const struct iovec *, size_t, + void *); +static void pci_vt9p_reset(void *); +static void pci_vt9p_notify(void *, struct vqueue_info *); +static int pci_vt9p_cfgread(void *, int, int, uint32_t *); +static void pci_vt9p_neg_features(void *, uint64_t); + +static struct virtio_consts vt9p_vi_consts = { + "vt9p", /* our name */ + 1, /* we support 1 virtqueue */ + VT9P_CONFIGSPACESZ, /* config reg size */ + pci_vt9p_reset, /* reset */ + pci_vt9p_notify, /* device-wide qnotify */ + pci_vt9p_cfgread, /* read virtio config */ + NULL, /* write virtio config */ + pci_vt9p_neg_features, /* apply negotiated features */ + (1 << 0), /* our capabilities */ +}; + + +static void +pci_vt9p_reset(void *vsc) +{ + struct pci_vt9p_softc *sc; + + sc = vsc; + + DPRINTF(("vt9p: device reset requested !\n")); + vi_reset_dev(&sc->vsc_vs); +} + +static void +pci_vt9p_neg_features(void *vsc, uint64_t negotiated_features) +{ + struct pci_vt9p_softc *sc = vsc; + + sc->vsc_features = negotiated_features; +} + +static int +pci_vt9p_cfgread(void *vsc, int offset, int size, uint32_t *retval) +{ + struct pci_vt9p_softc *sc = vsc; + void *ptr; + + ptr = (uint8_t *)sc->vsc_config + offset; + memcpy(retval, ptr, size); + return (0); +} + +static int +pci_vt9p_get_buffer(struct l9p_request *req, struct iovec *iov, size_t *niov, + void *arg) +{ + struct pci_vt9p_request *preq = req->lr_aux; + size_t n = preq->vsr_niov - preq->vsr_respidx; + + memcpy(iov, preq->vsr_iov + preq->vsr_respidx, + n * sizeof(struct iovec)); + *niov = n; + return (0); +} + +static int +pci_vt9p_send(struct l9p_request *req, const struct iovec *iov, + const size_t niov, const size_t iolen, void *arg) +{ + struct pci_vt9p_request *preq = req->lr_aux; + struct pci_vt9p_softc *sc = preq->vsr_sc; + + preq->vsr_iolen = iolen; + + pthread_mutex_lock(&sc->vsc_mtx); + vq_relchain(&sc->vsc_vq, preq->vsr_idx, preq->vsr_iolen); + vq_endchains(&sc->vsc_vq, 1); + pthread_mutex_unlock(&sc->vsc_mtx); + free(preq); + return (0); +} + +static void +pci_vt9p_drop(struct l9p_request *req, const struct iovec *iov, size_t niov, + void *arg) +{ + struct pci_vt9p_request *preq = req->lr_aux; + struct pci_vt9p_softc *sc = preq->vsr_sc; + + pthread_mutex_lock(&sc->vsc_mtx); + vq_relchain(&sc->vsc_vq, preq->vsr_idx, 0); + vq_endchains(&sc->vsc_vq, 1); + pthread_mutex_unlock(&sc->vsc_mtx); + free(preq); +} + +static void +pci_vt9p_notify(void *vsc, struct vqueue_info *vq) +{ + struct iovec iov[VT9P_MAX_IOV]; + struct pci_vt9p_softc *sc; + struct pci_vt9p_request *preq; + uint16_t idx, n, i; + uint16_t flags[VT9P_MAX_IOV]; + + sc = vsc; + + while (vq_has_descs(vq)) { + n = vq_getchain(vq, &idx, iov, VT9P_MAX_IOV, flags); + preq = calloc(1, sizeof(struct pci_vt9p_request)); + preq->vsr_sc = sc; + preq->vsr_idx = idx; + preq->vsr_iov = iov; + preq->vsr_niov = n; + preq->vsr_respidx = 0; + + /* Count readable descriptors */ + for (i = 0; i < n; i++) { + if (flags[i] & VRING_DESC_F_WRITE) + break; + + preq->vsr_respidx++; + } + + for (int i = 0; i < n; i++) { + DPRINTF(("vt9p: vt9p_notify(): desc%d base=%p, " + "len=%zu, flags=0x%04x\r\n", i, iov[i].iov_base, + iov[i].iov_len, flags[i])); + } + + l9p_connection_recv(sc->vsc_conn, iov, preq->vsr_respidx, preq); + } +} + + +static int +pci_vt9p_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + struct pci_vt9p_softc *sc; + char *opt; + char *sharename = NULL; + char *rootpath = NULL; + int rootfd; + bool ro = false; + cap_rights_t rootcap; + + if (opts == NULL) { + printf("virtio-9p: share name and path required\n"); + return (1); + } + + while ((opt = strsep(&opts, ",")) != NULL) { + if (strchr(opt, '=') != NULL) { + if (sharename != NULL) { + printf("virtio-9p: more than one share name given\n"); + return (1); + } + + sharename = strsep(&opt, "="); + rootpath = opt; + continue; + } + + if (strcmp(opt, "ro") == 0) { + DPRINTF(("read-only mount requested\r\n")); + ro = true; + continue; + } + + printf("virtio-9p: invalid option '%s'\n", opt); + return (1); + } + + if (strlen(sharename) > VT9P_MAXTAGSZ) { + printf("virtio-9p: share name too long\n"); + return (1); + } + + rootfd = open(rootpath, O_DIRECTORY); + if (rootfd < 0) + return (-1); + + sc = calloc(1, sizeof(struct pci_vt9p_softc)); + sc->vsc_config = calloc(1, sizeof(struct pci_vt9p_config) + + VT9P_MAXTAGSZ); + + pthread_mutex_init(&sc->vsc_mtx, NULL); + + cap_rights_init(&rootcap, + CAP_LOOKUP, CAP_ACL_CHECK, CAP_ACL_DELETE, CAP_ACL_GET, + CAP_ACL_SET, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_FSTAT, + CAP_CREATE, CAP_FCHMODAT, CAP_FCHOWNAT, CAP_FTRUNCATE, + CAP_LINKAT_SOURCE, CAP_LINKAT_TARGET, CAP_MKDIRAT, CAP_MKNODAT, + CAP_PREAD, CAP_PWRITE, CAP_RENAMEAT_SOURCE, CAP_RENAMEAT_TARGET, + CAP_SEEK, CAP_SYMLINKAT, CAP_UNLINKAT, CAP_EXTATTR_DELETE, + CAP_EXTATTR_GET, CAP_EXTATTR_LIST, CAP_EXTATTR_SET, + CAP_FUTIMES, CAP_FSTATFS, CAP_FSYNC, CAP_FPATHCONF); + + if (cap_rights_limit(rootfd, &rootcap) != 0) + return (1); + + sc->vsc_config->tag_len = (uint16_t)strlen(sharename); + memcpy(sc->vsc_config->tag, sharename, sc->vsc_config->tag_len); + + if (l9p_backend_fs_init(&sc->vsc_fs_backend, rootfd, ro) != 0) { + errno = ENXIO; + return (1); + } + + if (l9p_server_init(&sc->vsc_server, sc->vsc_fs_backend) != 0) { + errno = ENXIO; + return (1); + } + + if (l9p_connection_init(sc->vsc_server, &sc->vsc_conn) != 0) { + errno = EIO; + return (1); + } + + sc->vsc_conn->lc_msize = L9P_MAX_IOV * PAGE_SIZE; + sc->vsc_conn->lc_lt.lt_get_response_buffer = pci_vt9p_get_buffer; + sc->vsc_conn->lc_lt.lt_send_response = pci_vt9p_send; + sc->vsc_conn->lc_lt.lt_drop_response = pci_vt9p_drop; + + vi_softc_linkup(&sc->vsc_vs, &vt9p_vi_consts, sc, pi, &sc->vsc_vq); + sc->vsc_vs.vs_mtx = &sc->vsc_mtx; + sc->vsc_vq.vq_qsize = VT9P_RINGSZ; + + /* initialize config space */ + pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_9P); + pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); + pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE); + pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_9P); + pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR); + + if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix())) + return (1); + vi_set_io_bar(&sc->vsc_vs, 0); + + return (0); +} + +struct pci_devemu pci_de_v9p = { + .pe_emu = "virtio-9p", + .pe_init = pci_vt9p_init, + .pe_barwrite = vi_pci_write, + .pe_barread = vi_pci_read +}; +PCI_EMUL_SET(pci_de_v9p); --- usr.sbin/bhyve/virtio.h.orig 2021-08-19 22:41:21 UTC +++ usr.sbin/bhyve/virtio.h @@ -216,6 +216,7 @@ struct vring_used { #define VIRTIO_DEV_CONSOLE 0x1003 #define VIRTIO_DEV_RANDOM 0x1005 #define VIRTIO_DEV_SCSI 0x1008 +#define VIRTIO_DEV_9P 0x1009 /* * PCI config space constants. diff --git a/sysutils/bhyve+/files/freebsd-14/patch-lib9p b/sysutils/bhyve+/files/freebsd-14/patch-lib9p index 97f2fd5b00a7..41bf90a59d7d 100644 --- a/sysutils/bhyve+/files/freebsd-14/patch-lib9p +++ b/sysutils/bhyve+/files/freebsd-14/patch-lib9p @@ -1,12 +1,12 @@ ---- share/mk/src.libnames.mk.orig 2021-09-04 10:14:17.407288000 +0200 -+++ share/mk/src.libnames.mk 2021-09-04 10:15:27.832153000 +0200 -@@ -245,6 +245,9 @@ +--- share/mk/src.libnames.mk.orig 2021-12-17 02:31:11.492798000 +0100 ++++ share/mk/src.libnames.mk 2021-12-17 02:36:30.736989000 +0100 +@@ -261,6 +261,9 @@ # 2nd+ order consumers. Auto-generating this would be better. _DP_80211= sbuf bsdxml _DP_9p= sbuf +.if ${MK_CASPER} != "no" -+_DP_9p+= casper cap_pwd cap_grp ++_DP_9p+= casper cap_pwd cap_grp +.endif - _DP_archive= z bz2 lzma bsdxml zstd - _DP_zstd= pthread - .if ${MK_BLACKLIST} != "no" + # XXX: Not bootstrapped so uses host version on non-FreeBSD, so don't use a + # FreeBSD-specific dependency list + .if ${.MAKE.OS} == "FreeBSD" || !defined(BOOTSTRAPPING) diff --git a/sysutils/bhyve+/files/kernconf/opt_inet.h b/sysutils/bhyve+/files/kernconf/opt_inet.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sysutils/bhyve+/files/kernconf/opt_inet6.h b/sysutils/bhyve+/files/kernconf/opt_inet6.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sysutils/bhyve+/files/kernconf/opt_platform.h b/sysutils/bhyve+/files/kernconf/opt_platform.h new file mode 100644 index 000000000000..e69de29bb2d1