diff options
author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2017-06-26 18:32:36 +0000 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2017-06-26 18:32:36 +0000 |
commit | 8aef19f7563fa7e20f072f46202468eb3bda3868 (patch) | |
tree | cd9a1b1f0d83a83bdf98e50ab1418774cce610ed | |
parent | f4df7cccbc6a219c58b697781910766c28800dd9 (diff) | |
parent | f67cf39ba88ad7c4bee81445cc7a7468e0289f01 (diff) | |
download | illumos-joyent-8aef19f7563fa7e20f072f46202468eb3bda3868.tar.gz |
[illumos-gate merge]
commit f67cf39ba88ad7c4bee81445cc7a7468e0289f01
commit 7edb9f69d2426b044fa60c7a168c9eaeb12f1884
6431 document FreeBSD regex extensions
commit dfc115332c94a2f62058ac7f2bce7631fbd20b3d
7431 ZFS Channel Programs
Conflicts:
usr/src/lib/libzpool/common/sys/zfs_context.h
usr/src/lib/libzfs/common/libzfs_util.c
usr/src/lib/libzfs/common/libzfs_impl.h
182 files changed, 28060 insertions, 1851 deletions
diff --git a/exception_lists/cstyle b/exception_lists/cstyle index 3b2eedf626..e9e84dd8dc 100644 --- a/exception_lists/cstyle +++ b/exception_lists/cstyle @@ -732,15 +732,16 @@ usr/src/lib/libsmbfs/smb/derparse.h usr/src/lib/libsmbfs/smb/spnego.c usr/src/lib/libsmbfs/smb/spnegoparse.c usr/src/lib/libsmbfs/smb/spnegoparse.h -usr/src/test/os-tests/tests/sockfs/conn.c -usr/src/test/os-tests/tests/sockfs/dgram.c -usr/src/test/os-tests/tests/sockfs/drop_priv.c -usr/src/test/os-tests/tests/sockfs/sockpair.c +usr/src/test/os-tests/tests/sockfs/conn.c +usr/src/test/os-tests/tests/sockfs/dgram.c +usr/src/test/os-tests/tests/sockfs/drop_priv.c +usr/src/test/os-tests/tests/sockfs/sockpair.c usr/src/tools/btxld/btx.h usr/src/tools/btxld/btxld.c usr/src/tools/btxld/elfh.c usr/src/tools/btxld/elfh.h usr/src/tools/btxld/imgact_aout.h +usr/src/uts/common/fs/zfs/lua/* usr/src/uts/common/gssapi/gssapi.h usr/src/uts/common/gssapi/mechs/krb5/crypto/block_size.c usr/src/uts/common/gssapi/mechs/krb5/crypto/checksum_length.c @@ -12268,6 +12268,7 @@ f usr/share/man/man1m/ypset.1m 0444 root bin f usr/share/man/man1m/zdb.1m 0444 root bin f usr/share/man/man1m/zdump.1m 0444 root bin f usr/share/man/man1m/zfs.1m 0444 root bin +f usr/share/man/man1m/zfs-program.1m 0444 root bin f usr/share/man/man1m/zic.1m 0444 root bin f usr/share/man/man1m/zoneadm.1m 0444 root bin f usr/share/man/man1m/zoneadmd.1m 0444 root bin diff --git a/usr/src/cmd/fs.d/nfs/statd/sm_proc.c b/usr/src/cmd/fs.d/nfs/statd/sm_proc.c index 1a07de0877..00d8d59f89 100644 --- a/usr/src/cmd/fs.d/nfs/statd/sm_proc.c +++ b/usr/src/cmd/fs.d/nfs/statd/sm_proc.c @@ -1174,8 +1174,7 @@ in_host_array(char *host) * nothing and leaves host_name the way it was previous to the call. */ static void -add_to_host_array(char *host) -{ +add_to_host_array(char *host) { void *new_block = NULL; @@ -1183,7 +1182,7 @@ add_to_host_array(char *host) if (addrix >= host_name_count) { host_name_count += HOST_NAME_INCR; new_block = realloc((void *)host_name, - host_name_count*sizeof (char *)); + host_name_count*sizeof (char *)); if (new_block != NULL) host_name = new_block; else { @@ -1339,9 +1338,6 @@ merge_ips(void) break; } - case AF_LINK: - continue; - default: syslog(LOG_WARNING, "Unknown address family %d for " "interface %s", sa->sa_family, cifap->ifa_name); diff --git a/usr/src/cmd/mdb/intel/amd64/zfs/Makefile b/usr/src/cmd/mdb/intel/amd64/zfs/Makefile index bae4d7f502..870f91ad99 100644 --- a/usr/src/cmd/mdb/intel/amd64/zfs/Makefile +++ b/usr/src/cmd/mdb/intel/amd64/zfs/Makefile @@ -23,6 +23,8 @@ # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2016 by Delphix. All rights reserved. +# MODULE = zfs.so MDBTGT = kvm @@ -36,6 +38,7 @@ include ../../../Makefile.module include ../../../common/modules/zfs/Makefile.zfs CPPFLAGS += -I../../../../../uts/common/fs/zfs +CPPFLAGS += -I../../../../../uts/common/fs/zfs/lua C99MODE= -xc99=%all C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/mdb/intel/ia32/libzpool/Makefile b/usr/src/cmd/mdb/intel/ia32/libzpool/Makefile index bd86114ad7..68416f725b 100644 --- a/usr/src/cmd/mdb/intel/ia32/libzpool/Makefile +++ b/usr/src/cmd/mdb/intel/ia32/libzpool/Makefile @@ -23,6 +23,8 @@ # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2016 by Delphix. All rights reserved. +# MODULE = libzpool.so MDBTGT = proc @@ -38,7 +40,8 @@ MODSRCS_DIR = ../../../common/modules/zfs GENUNIX_DIR = ../../../common/modules/genunix CPPFLAGS += -I../../../../../lib/libzpool/common \ - -I../../../../../uts/common/fs/zfs + -I../../../../../uts/common/fs/zfs \ + -I../../../../../uts/common/fs/zfs/lua C99MODE= -xc99=%all C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/mdb/intel/ia32/zfs/Makefile b/usr/src/cmd/mdb/intel/ia32/zfs/Makefile index 17ab63f959..d1f1efa749 100644 --- a/usr/src/cmd/mdb/intel/ia32/zfs/Makefile +++ b/usr/src/cmd/mdb/intel/ia32/zfs/Makefile @@ -23,6 +23,8 @@ # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2016 by Delphix. All rights reserved. +# MODULE = zfs.so MDBTGT = kvm @@ -35,6 +37,7 @@ include ../../../Makefile.module include ../../../common/modules/zfs/Makefile.zfs CPPFLAGS += -I../../../../../uts/common/fs/zfs +CPPFLAGS += -I../../../../../uts/common/fs/zfs/lua C99MODE= -xc99=%all C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/mdb/sparc/v7/libzpool/Makefile b/usr/src/cmd/mdb/sparc/v7/libzpool/Makefile index 8d90aaca0f..b00ff3c0bc 100644 --- a/usr/src/cmd/mdb/sparc/v7/libzpool/Makefile +++ b/usr/src/cmd/mdb/sparc/v7/libzpool/Makefile @@ -23,6 +23,8 @@ # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2016 by Delphix. All rights reserved. +# MODULE = libzpool.so MDBTGT = proc @@ -38,7 +40,8 @@ MODSRCS_DIR = ../../../common/modules/zfs GENUNIX_DIR = ../../../common/modules/genunix CPPFLAGS += -I../../../../../lib/libzpool/common \ - -I../../../../../uts/common/fs/zfs + -I../../../../../uts/common/fs/zfs \ + -I../../../../../uts/common/fs/zfs/lua C99MODE= -xc99=%all C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/mdb/sparc/v9/libzpool/Makefile b/usr/src/cmd/mdb/sparc/v9/libzpool/Makefile index ab253f6363..5c3b38b760 100644 --- a/usr/src/cmd/mdb/sparc/v9/libzpool/Makefile +++ b/usr/src/cmd/mdb/sparc/v9/libzpool/Makefile @@ -23,6 +23,8 @@ # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2016 by Delphix. All rights reserved. +# MODULE = libzpool.so MDBTGT = proc @@ -39,7 +41,8 @@ MODSRCS_DIR = ../../../common/modules/zfs GENUNIX_DIR = ../../../common/modules/genunix CPPFLAGS += -I../../../../../lib/libzpool/common \ - -I../../../../../uts/common/fs/zfs + -I../../../../../uts/common/fs/zfs \ + -I../../../../../uts/common/fs/zfs/lua C99MODE= -xc99=%all C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/mdb/sparc/v9/zfs/Makefile b/usr/src/cmd/mdb/sparc/v9/zfs/Makefile index b5191cc3cd..751c3c3571 100644 --- a/usr/src/cmd/mdb/sparc/v9/zfs/Makefile +++ b/usr/src/cmd/mdb/sparc/v9/zfs/Makefile @@ -23,6 +23,8 @@ # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2016 by Delphix. All rights reserved. +# MODULE = zfs.so MDBTGT = kvm @@ -36,6 +38,7 @@ include ../../../Makefile.module include ../../../common/modules/zfs/Makefile.zfs CPPFLAGS += -I../../../../../uts/common/fs/zfs +CPPFLAGS += -I../../../../../uts/common/fs/zfs/lua C99MODE= -xc99=%all C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c index 303b652b91..e747e2b233 100644 --- a/usr/src/cmd/truss/codes.c +++ b/usr/src/cmd/truss/codes.c @@ -21,7 +21,7 @@ /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, 2016 by Delphix. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2015, Joyent, Inc. All rights reserved. * Copyright (c) 2014, OmniTI Computer Consulting, Inc. All rights reserved. @@ -1280,6 +1280,8 @@ const struct ioc { "zfs_cmd_t" }, { (uint_t)ZFS_IOC_DESTROY_BOOKMARKS, "ZFS_IOC_DESTROY_BOOKMARKS", "zfs_cmd_t" }, + { (uint_t)ZFS_IOC_CHANNEL_PROGRAM, "ZFS_IOC_CHANNEL_PROGRAM", + "zfs_cmd_t" }, /* kssl ioctls */ { (uint_t)KSSL_ADD_ENTRY, "KSSL_ADD_ENTRY", diff --git a/usr/src/cmd/zdb/Makefile.com b/usr/src/cmd/zdb/Makefile.com index ae18f2872d..63b8366c80 100644 --- a/usr/src/cmd/zdb/Makefile.com +++ b/usr/src/cmd/zdb/Makefile.com @@ -32,7 +32,7 @@ OBJS= $(PROG).o zdb_il.o include ../../Makefile.cmd include ../../Makefile.ctf -INCS += -I../../../lib/libzpool/common +INCS += -I../../../lib/libzpool/common INCS += -I../../../uts/common/fs/zfs INCS += -I../../../common/zfs diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index 2a7a9a6ec2..a5e51ad607 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2015 by Delphix. All rights reserved. + * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. @@ -50,6 +50,7 @@ #include <grp.h> #include <pwd.h> #include <signal.h> +#include <sys/debug.h> #include <sys/list.h> #include <sys/mkdev.h> #include <sys/mntent.h> @@ -106,6 +107,7 @@ static int zfs_do_holds(int argc, char **argv); static int zfs_do_release(int argc, char **argv); static int zfs_do_diff(int argc, char **argv); static int zfs_do_bookmark(int argc, char **argv); +static int zfs_do_channel_program(int argc, char **argv); /* * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. @@ -153,6 +155,7 @@ typedef enum { HELP_RELEASE, HELP_DIFF, HELP_BOOKMARK, + HELP_CHANNEL_PROGRAM, } zfs_help_t; typedef struct zfs_command { @@ -180,6 +183,7 @@ static zfs_command_t command_table[] = { { "promote", zfs_do_promote, HELP_PROMOTE }, { "rename", zfs_do_rename, HELP_RENAME }, { "bookmark", zfs_do_bookmark, HELP_BOOKMARK }, + { "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM }, { NULL }, { "list", zfs_do_list, HELP_LIST }, { NULL }, @@ -324,6 +328,10 @@ get_usage(zfs_help_t idx) "[snapshot|filesystem]\n")); case HELP_BOOKMARK: return (gettext("\tbookmark <snapshot> <bookmark>\n")); + case HELP_CHANNEL_PROGRAM: + return (gettext("\tprogram [-t <instruction limit>] " + "[-m <memory limit (b)>] <pool> <program file> " + "[lua args...]\n")); } abort(); @@ -352,6 +360,18 @@ safe_malloc(size_t size) return (data); } +void * +safe_realloc(void *data, size_t size) +{ + void *newp; + if ((newp = realloc(data, size)) == NULL) { + free(data); + nomem(); + } + + return (newp); +} + static char * safe_strdup(char *str) { @@ -7059,6 +7079,190 @@ usage: return (-1); } +static int +zfs_do_channel_program(int argc, char **argv) +{ + int ret, fd; + char c; + char *progbuf, *filename, *poolname; + size_t progsize, progread; + nvlist_t *outnvl; + uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT; + uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT; + zpool_handle_t *zhp; + + /* check options */ + while (-1 != + (c = getopt(argc, argv, "t:(instr-limit)m:(memory-limit)"))) { + switch (c) { + case 't': + case 'm': { + uint64_t arg; + char *endp; + + errno = 0; + arg = strtoull(optarg, &endp, 0); + if (errno != 0 || *endp != '\0') { + (void) fprintf(stderr, gettext( + "invalid argument " + "'%s': expected integer\n"), optarg); + goto usage; + } + + if (c == 't') { + if (arg > ZCP_MAX_INSTRLIMIT || arg == 0) { + (void) fprintf(stderr, gettext( + "Invalid instruction limit: " + "%s\n"), optarg); + return (1); + } else { + instrlimit = arg; + } + } else { + ASSERT3U(c, ==, 'm'); + if (arg > ZCP_MAX_MEMLIMIT || arg == 0) { + (void) fprintf(stderr, gettext( + "Invalid memory limit: " + "%s\n"), optarg); + return (1); + } else { + memlimit = arg; + } + } + break; + } + case '?': + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + goto usage; + } + } + + argc -= optind; + argv += optind; + + if (argc < 2) { + (void) fprintf(stderr, + gettext("invalid number of arguments\n")); + goto usage; + } + + poolname = argv[0]; + filename = argv[1]; + if (strcmp(filename, "-") == 0) { + fd = 0; + filename = "standard input"; + } else if ((fd = open(filename, O_RDONLY)) < 0) { + (void) fprintf(stderr, gettext("cannot open '%s': %s\n"), + filename, strerror(errno)); + return (1); + } + + if ((zhp = zpool_open(g_zfs, poolname)) == NULL) { + (void) fprintf(stderr, gettext("cannot open pool '%s'"), + poolname); + return (1); + } + zpool_close(zhp); + + /* + * Read in the channel program, expanding the program buffer as + * necessary. + */ + progread = 0; + progsize = 1024; + progbuf = safe_malloc(progsize); + do { + ret = read(fd, progbuf + progread, progsize - progread); + progread += ret; + if (progread == progsize && ret > 0) { + progsize *= 2; + progbuf = safe_realloc(progbuf, progsize); + } + } while (ret > 0); + + if (fd != 0) + (void) close(fd); + if (ret < 0) { + free(progbuf); + (void) fprintf(stderr, + gettext("cannot read '%s': %s\n"), + filename, strerror(errno)); + return (1); + } + progbuf[progread] = '\0'; + + /* + * Any remaining arguments are passed as arguments to the lua script as + * a string array: + * { + * "argv" -> [ "arg 1", ... "arg n" ], + * } + */ + nvlist_t *argnvl = fnvlist_alloc(); + fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2); + + ret = lzc_channel_program(poolname, progbuf, instrlimit, memlimit, + argnvl, &outnvl); + + if (ret != 0) { + /* + * On error, report the error message handed back by lua if one + * exists. Otherwise, generate an appropriate error message, + * falling back on strerror() for an unexpected return code. + */ + char *errstring = NULL; + if (nvlist_exists(outnvl, ZCP_RET_ERROR)) { + (void) nvlist_lookup_string(outnvl, + ZCP_RET_ERROR, &errstring); + if (errstring == NULL) + errstring = strerror(ret); + } else { + switch (ret) { + case EINVAL: + errstring = + "Invalid instruction or memory limit."; + break; + case ENOMEM: + errstring = "Return value too large."; + break; + case ENOSPC: + errstring = "Memory limit exhausted."; + break; + case ETIME: + errstring = "Timed out."; + break; + case EPERM: + errstring = "Permission denied. Channel " + "programs must be run as root."; + break; + default: + errstring = strerror(ret); + } + } + (void) fprintf(stderr, + gettext("Channel program execution failed:\n%s\n"), + errstring); + } else { + (void) printf("Channel program fully executed "); + if (nvlist_empty(outnvl)) { + (void) printf("with no return value.\n"); + } else { + (void) printf("with return value:\n"); + dump_nvlist(outnvl, 4); + } + } + + free(progbuf); + fnvlist_free(outnvl); + fnvlist_free(argnvl); + return (ret != 0); + +usage: + usage(B_FALSE); + return (-1); +} + int main(int argc, char **argv) { diff --git a/usr/src/cmd/zhack/Makefile.com b/usr/src/cmd/zhack/Makefile.com index 4f42a0e276..f077ed252a 100644 --- a/usr/src/cmd/zhack/Makefile.com +++ b/usr/src/cmd/zhack/Makefile.com @@ -35,8 +35,9 @@ OBJS= $(PROG).o include ../../Makefile.cmd include ../../Makefile.ctf -INCS += -I../../../lib/libzpool/common +INCS += -I../../../lib/libzpool/common INCS += -I../../../uts/common/fs/zfs +INCS += -I../../../uts/common/fs/zfs/lua INCS += -I../../../common/zfs LDLIBS += -lzpool -lumem -lnvpair -lzfs diff --git a/usr/src/cmd/zinject/Makefile.com b/usr/src/cmd/zinject/Makefile.com index 6f21815fa8..82b4e6c2c1 100644 --- a/usr/src/cmd/zinject/Makefile.com +++ b/usr/src/cmd/zinject/Makefile.com @@ -33,6 +33,7 @@ include ../../Makefile.cmd INCS += -I../../../lib/libzpool/common INCS += -I../../../uts/common/fs/zfs +INCS += -I../../../uts/common/fs/zfs/lua LDLIBS += -lzpool -lzfs -lnvpair diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c index 7e65accb85..7f1d2fa7a1 100644 --- a/usr/src/cmd/zpool/zpool_main.c +++ b/usr/src/cmd/zpool/zpool_main.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2015 by Delphix. All rights reserved. + * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2012 by Frederik Wessels. All rights reserved. * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved. * Copyright (c) 2013 Joyent, Inc. All rights reserved. @@ -5130,6 +5130,11 @@ get_history_one(zpool_handle_t *zhp, void *data) dump_nvlist(fnvlist_lookup_nvlist(rec, ZPOOL_HIST_OUTPUT_NVL), 8); } + if (nvlist_exists(rec, ZPOOL_HIST_ERRNO)) { + (void) printf(" errno: %lld\n", + fnvlist_lookup_int64(rec, + ZPOOL_HIST_ERRNO)); + } } else { if (!cb->internal) continue; diff --git a/usr/src/cmd/ztest/Makefile.com b/usr/src/cmd/ztest/Makefile.com index 21a6659fc9..6491ee2047 100644 --- a/usr/src/cmd/ztest/Makefile.com +++ b/usr/src/cmd/ztest/Makefile.com @@ -20,7 +20,7 @@ # # # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright (c) 2012, 2016 by Delphix. All rights reserved. # PROG= ztest @@ -32,6 +32,7 @@ include ../../Makefile.ctf INCS += -I../../../lib/libzpool/common INCS += -I../../../uts/common/fs/zfs +INCS += -I../../../uts/common/fs/zfs/lua INCS += -I../../../common/zfs LDLIBS += -lumem -lzpool -lcmdutils -lm -lnvpair diff --git a/usr/src/common/zfs/zfs_prop.c b/usr/src/common/zfs/zfs_prop.c index bfd1d48cab..b9d490c45d 100644 --- a/usr/src/common/zfs/zfs_prop.c +++ b/usr/src/common/zfs/zfs_prop.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2016, Joyent, Inc. @@ -381,7 +381,8 @@ zfs_prop_init(void) zprop_register_number(ZFS_PROP_WRITTEN, "written", 0, PROP_READONLY, ZFS_TYPE_DATASET, "<size>", "WRITTEN"); zprop_register_number(ZFS_PROP_LOGICALUSED, "logicalused", 0, - PROP_READONLY, ZFS_TYPE_DATASET, "<size>", "LUSED"); + PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", + "LUSED"); zprop_register_number(ZFS_PROP_LOGICALREFERENCED, "logicalreferenced", 0, PROP_READONLY, ZFS_TYPE_DATASET, "<size>", "LREFER"); zprop_register_number(ZFS_PROP_FILESYSTEM_COUNT, "filesystem_count", @@ -602,6 +603,15 @@ zfs_prop_readonly(zfs_prop_t prop) } /* + * Returns TRUE if the property is visible (not hidden). + */ +boolean_t +zfs_prop_visible(zfs_prop_t prop) +{ + return (zfs_prop_table[prop].pd_visible); +} + +/* * Returns TRUE if the property is only allowed to be set once. */ boolean_t diff --git a/usr/src/lib/libipadm/common/ipadm_addr.c b/usr/src/lib/libipadm/common/ipadm_addr.c index 31f6f9cfc5..a62bcb165b 100644 --- a/usr/src/lib/libipadm/common/ipadm_addr.c +++ b/usr/src/lib/libipadm/common/ipadm_addr.c @@ -375,9 +375,6 @@ retry: for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { struct sockaddr_storage data; - if (ifap->ifa_addr->sa_family == AF_LINK) - continue; - (void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname)); lnum = 0; if ((sep = strrchr(cifname, ':')) != NULL) { diff --git a/usr/src/lib/libipadm/common/ipadm_if.c b/usr/src/lib/libipadm/common/ipadm_if.c index c58eb6248e..41f22e4eeb 100644 --- a/usr/src/lib/libipadm/common/ipadm_if.c +++ b/usr/src/lib/libipadm/common/ipadm_if.c @@ -276,8 +276,6 @@ retry: * to find the interface state. */ for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { - if (ifap->ifa_addr->sa_family == AF_LINK) - continue; if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0) break; } diff --git a/usr/src/lib/libsocket/inet/getifaddrs.c b/usr/src/lib/libsocket/inet/getifaddrs.c index 42afca41bc..df6d4199d1 100644 --- a/usr/src/lib/libsocket/inet/getifaddrs.c +++ b/usr/src/lib/libsocket/inet/getifaddrs.c @@ -21,7 +21,6 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2017 Sebastian Wiedenroth. All rights reserved. */ #include <netdb.h> @@ -29,21 +28,11 @@ #include <netinet/in.h> #include <sys/socket.h> #include <string.h> -#include <strings.h> #include <stdio.h> #include <sys/sockio.h> #include <sys/types.h> #include <stdlib.h> #include <net/if.h> -#include <door.h> -#include <fcntl.h> -#include <sys/mman.h> -#include <sys/dld_ioc.h> -#include <sys/dld.h> -#include <sys/dls_mgmt.h> -#include <sys/mac.h> -#include <sys/dlpi.h> -#include <net/if_types.h> #include <ifaddrs.h> #include <libsocket_priv.h> @@ -89,140 +78,10 @@ freeifaddrs(struct ifaddrs *ifa) free(curr->ifa_addr); free(curr->ifa_netmask); free(curr->ifa_dstaddr); - free(curr->ifa_data); free(curr); } } -static uint_t -dlpi_iftype(uint_t dlpitype) -{ - switch (dlpitype) { - case DL_ETHER: - return (IFT_ETHER); - - case DL_ATM: - return (IFT_ATM); - - case DL_CSMACD: - return (IFT_ISO88023); - - case DL_TPB: - return (IFT_ISO88024); - - case DL_TPR: - return (IFT_ISO88025); - - case DL_FDDI: - return (IFT_FDDI); - - case DL_IB: - return (IFT_IB); - - case DL_OTHER: - return (IFT_OTHER); - } - - return (IFT_OTHER); -} - -/* - * Make a door call to dlmgmtd. - * If successful the result is stored in rbuf and 0 returned. - * On errors, return -1 and set `errno'. - */ -static int -dl_door_call(int door_fd, void *arg, size_t asize, void *rbuf, size_t *rsizep) -{ - int err; - door_arg_t darg; - darg.data_ptr = arg; - darg.data_size = asize; - darg.desc_ptr = NULL; - darg.desc_num = 0; - darg.rbuf = rbuf; - darg.rsize = *rsizep; - - if (door_call(door_fd, &darg) == -1) { - return (-1); - } - - if (darg.rbuf != rbuf) { - /* - * The size of the input rbuf was not big enough so that - * the door allocated the rbuf itself. In this case, return - * the required size to the caller. - */ - err = errno; - (void) munmap(darg.rbuf, darg.rsize); - *rsizep = darg.rsize; - errno = err; - return (-1); - } else if (darg.rsize != *rsizep) { - return (-1); - } - return (0); -} - - -/* - * Get the name from dlmgmtd by linkid. - * If successful the result is stored in name_retval and 0 returned. - * On errors, return -1 and set `errno'. - */ -static int -dl_get_name(int door_fd, datalink_id_t linkid, - dlmgmt_getname_retval_t *name_retval) -{ - size_t name_sz = sizeof (*name_retval); - dlmgmt_door_getname_t getname; - bzero(&getname, sizeof (dlmgmt_door_getname_t)); - getname.ld_cmd = DLMGMT_CMD_GETNAME; - getname.ld_linkid = linkid; - - if (dl_door_call(door_fd, &getname, sizeof (getname), name_retval, - &name_sz) < 0) { - return (-1); - } - if (name_retval->lr_err != 0) { - errno = name_retval->lr_err; - return (-1); - } - return (0); -} - -/* - * Get the next link from dlmgmtd. - * Start iterating by passing DATALINK_INVALID_LINKID as linkid. - * The end is marked by next_retval.lr_linkid set to DATALINK_INVALID_LINKID. - * If successful the result is stored in next_retval and 0 returned. - * On errors, return -1 and set `errno'. - */ -static int -dl_get_next(int door_fd, datalink_id_t linkid, datalink_class_t class, - datalink_media_t dmedia, uint32_t flags, - dlmgmt_getnext_retval_t *next_retval) -{ - size_t next_sz = sizeof (*next_retval); - dlmgmt_door_getnext_t getnext; - bzero(&getnext, sizeof (dlmgmt_door_getnext_t)); - getnext.ld_cmd = DLMGMT_CMD_GETNEXT; - getnext.ld_class = class; - getnext.ld_dmedia = dmedia; - getnext.ld_flags = flags; - getnext.ld_linkid = linkid; - - if (dl_door_call(door_fd, &getnext, sizeof (getnext), next_retval, - &next_sz) < 0) { - return (-1); - } - if (next_retval->lr_err != 0) { - errno = next_retval->lr_err; - return (-1); - } - return (0); -} - /* * Returns all addresses configured on the system. If flags contain * LIFC_ENABLED, only the addresses that are UP are returned. @@ -238,33 +97,19 @@ getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags) int ret; int s, n, numifs; struct ifaddrs *curr, *prev; - struct sockaddr_dl *ifa_addr = NULL; - if_data_t *ifa_data = NULL; sa_family_t lifr_af; - datalink_id_t linkid; - dld_ioc_attr_t dia; - dld_macaddrinfo_t *dmip; - dld_ioc_macaddrget_t *iomp = NULL; - dlmgmt_getnext_retval_t next_retval; - dlmgmt_getname_retval_t name_retval; - int bufsize; - int nmacaddr = 1024; - int sock4 = -1; - int sock6 = -1; - int door_fd = -1; - int dld_fd = -1; + int sock4; + int sock6; int err; - if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || - (sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 || - (door_fd = open(DLMGMT_DOOR, O_RDONLY)) < 0 || - (dld_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) - goto fail; - - bufsize = sizeof (dld_ioc_macaddrget_t) + nmacaddr * - sizeof (dld_macaddrinfo_t); - if ((iomp = calloc(1, bufsize)) == NULL) - goto fail; + if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return (-1); + if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + err = errno; + close(sock4); + errno = err; + return (-1); + } retry: /* Get all interfaces from SIOCGLIFCONF */ @@ -352,114 +197,19 @@ retry: } } - - /* add AF_LINK entries */ - if (af == AF_UNSPEC || af == AF_LINK) { - - linkid = DATALINK_INVALID_LINKID; - for (;;) { - if (dl_get_next(door_fd, linkid, DATALINK_CLASS_ALL, - DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE, - &next_retval) != 0) { - break; - } - - linkid = next_retval.lr_linkid; - if (linkid == DATALINK_INVALID_LINKID) - break; - - /* get mac addr */ - iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t); - iomp->dig_linkid = linkid; - - if (ioctl(dld_fd, DLDIOC_MACADDRGET, iomp) < 0) - continue; - - dmip = (dld_macaddrinfo_t *)(iomp + 1); - - /* get name */ - if (dl_get_name(door_fd, linkid, &name_retval) != 0) - continue; - - /* get MTU */ - dia.dia_linkid = linkid; - if (ioctl(dld_fd, DLDIOC_ATTR, &dia) < 0) - continue; - - curr = calloc(1, sizeof (struct ifaddrs)); - if (curr == NULL) - goto fail; - - curr->ifa_flags = prev->ifa_flags; - prev->ifa_next = curr; - prev = curr; - - if ((curr->ifa_name = strdup(name_retval.lr_link)) == - NULL) - goto fail; - - curr->ifa_addr = - calloc(1, sizeof (struct sockaddr_storage)); - if (curr->ifa_addr == NULL) - goto fail; - - curr->ifa_data = calloc(1, sizeof (if_data_t)); - if (curr->ifa_data == NULL) - goto fail; - - curr->ifa_addr->sa_family = AF_LINK; - ifa_addr = (struct sockaddr_dl *)curr->ifa_addr; - ifa_data = curr->ifa_data; - - (void) memcpy(ifa_addr->sdl_data, dmip->dmi_addr, - dmip->dmi_addrlen); - ifa_addr->sdl_alen = dmip->dmi_addrlen; - - ifa_data->ifi_mtu = dia.dia_max_sdu; - ifa_data->ifi_type = dlpi_iftype(next_retval.lr_media); - - /* - * get interface index - * This is only possible if the link has been plumbed. - */ - if (strlcpy(lifrl.lifr_name, name_retval.lr_link, - sizeof (lifrl.lifr_name)) >= - sizeof (lifrl.lifr_name)) - continue; - - if (ioctl(sock4, SIOCGLIFINDEX, (caddr_t)&lifrl) >= 0) { - ifa_addr->sdl_index = lifrl.lifr_index; - } else if (ioctl(sock6, SIOCGLIFINDEX, - (caddr_t)&lifrl) >= 0) { - /* retry for IPv6 */ - ifa_addr->sdl_index = lifrl.lifr_index; - } - } - } free(buf); - free(iomp); - (void) close(sock4); - (void) close(sock6); - (void) close(door_fd); - (void) close(dld_fd); + close(sock4); + close(sock6); return (0); fail: err = errno; free(buf); - free(iomp); freeifaddrs(*ifap); *ifap = NULL; if (err == ENXIO) goto retry; - - if (sock4 != -1) - (void) close(sock4); - if (sock6 != -1) - (void) close(sock6); - if (door_fd != -1) - (void) close(door_fd); - if (dld_fd != -1) - (void) close(dld_fd); + close(sock4); + close(sock6); errno = err; return (-1); } diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c index 8383b156d9..bd7f649e1c 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -2333,6 +2333,74 @@ zfs_get_clones_nvl(zfs_handle_t *zhp) } /* + * Accepts a property and value and checks that the value + * matches the one found by the channel program. If they are + * not equal, print both of them. + */ +void +zcp_check(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t intval, + const char *strval) +{ + if (!zhp->zfs_hdl->libzfs_prop_debug) + return; + int error; + char *poolname = zhp->zpool_hdl->zpool_name; + const char *program = + "args = ...\n" + "ds = args['dataset']\n" + "prop = args['property']\n" + "value, setpoint = zfs.get_prop(ds, prop)\n" + "return {value=value, setpoint=setpoint}\n"; + nvlist_t *outnvl; + nvlist_t *retnvl; + nvlist_t *argnvl = fnvlist_alloc(); + + fnvlist_add_string(argnvl, "dataset", zhp->zfs_name); + fnvlist_add_string(argnvl, "property", zfs_prop_to_name(prop)); + + error = lzc_channel_program(poolname, program, + 10 * 1000 * 1000, 10 * 1024 * 1024, argnvl, &outnvl); + + if (error == 0) { + retnvl = fnvlist_lookup_nvlist(outnvl, "return"); + if (zfs_prop_get_type(prop) == PROP_TYPE_NUMBER) { + int64_t ans; + error = nvlist_lookup_int64(retnvl, "value", &ans); + if (error != 0) { + (void) fprintf(stderr, "zcp check error: %u\n", + error); + return; + } + if (ans != intval) { + (void) fprintf(stderr, + "%s: zfs found %lld, but zcp found %lld\n", + zfs_prop_to_name(prop), + (longlong_t)intval, (longlong_t)ans); + } + } else { + char *str_ans; + error = nvlist_lookup_string(retnvl, "value", &str_ans); + if (error != 0) { + (void) fprintf(stderr, "zcp check error: %u\n", + error); + return; + } + if (strcmp(strval, str_ans) != 0) { + (void) fprintf(stderr, + "%s: zfs found %s, but zcp found %s\n", + zfs_prop_to_name(prop), + strval, str_ans); + } + } + } else { + (void) fprintf(stderr, + "zcp check failed, channel program error: %u\n", error); + } + nvlist_free(argnvl); + nvlist_free(outnvl); +} + +/* * Retrieve a property from the given object. If 'literal' is specified, then * numbers are left as exact values. Otherwise, numbers are converted to a * human-readable form. @@ -2389,6 +2457,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, &t) == 0) (void) snprintf(propbuf, proplen, "%llu", val); } + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_MOUNTPOINT: @@ -2457,7 +2526,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, /* 'legacy' or 'none' */ (void) strlcpy(propbuf, str, proplen); } - + zcp_check(zhp, prop, NULL, propbuf); break; case ZFS_PROP_ORIGIN: @@ -2465,6 +2534,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, if (str == NULL) return (-1); (void) strlcpy(propbuf, str, proplen); + zcp_check(zhp, prop, NULL, str); break; case ZFS_PROP_CLONES: @@ -2479,7 +2549,6 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, if (get_numeric_property(zhp, prop, src, &source, &val) != 0) return (-1); - /* * If quota or reservation is 0, we translate this into 'none' * (unless literal is set), and indicate that it's the default @@ -2498,6 +2567,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, else zfs_nicenum(val, propbuf, proplen); } + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_FILESYSTEM_LIMIT: @@ -2522,6 +2592,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, } else { zfs_nicenum(val, propbuf, proplen); } + + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_REFRATIO: @@ -2531,6 +2603,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, (void) snprintf(propbuf, proplen, "%llu.%02llux", (u_longlong_t)(val / 100), (u_longlong_t)(val % 100)); + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_TYPE: @@ -2551,6 +2624,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, abort(); } (void) snprintf(propbuf, proplen, "%s", str); + zcp_check(zhp, prop, NULL, propbuf); break; case ZFS_PROP_MOUNTED: @@ -2576,6 +2650,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, * consumers. */ (void) strlcpy(propbuf, zhp->zfs_name, proplen); + zcp_check(zhp, prop, NULL, propbuf); break; case ZFS_PROP_MLSLABEL: @@ -2625,26 +2700,33 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, if (get_numeric_property(zhp, prop, src, &source, &val) != 0) return (-1); (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val); + zcp_check(zhp, prop, val, NULL); break; default: switch (zfs_prop_get_type(prop)) { case PROP_TYPE_NUMBER: if (get_numeric_property(zhp, prop, src, - &source, &val) != 0) + &source, &val) != 0) { return (-1); - if (literal) + } + + if (literal) { (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val); - else + } else { zfs_nicenum(val, propbuf, proplen); + } + zcp_check(zhp, prop, val, NULL); break; case PROP_TYPE_STRING: str = getprop_string(zhp, prop, &source); if (str == NULL) return (-1); + (void) strlcpy(propbuf, str, proplen); + zcp_check(zhp, prop, NULL, str); break; case PROP_TYPE_INDEX: @@ -2653,7 +2735,9 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, return (-1); if (zfs_prop_index_to_string(prop, val, &strval) != 0) return (-1); + (void) strlcpy(propbuf, strval, proplen); + zcp_check(zhp, prop, NULL, strval); break; default: diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h index e45aed3d29..9e5641ec46 100644 --- a/usr/src/lib/libzfs/common/libzfs_impl.h +++ b/usr/src/lib/libzfs/common/libzfs_impl.h @@ -81,6 +81,7 @@ struct libzfs_handle { libzfs_fru_t *libzfs_fru_list; char libzfs_chassis_id[256]; boolean_t libzfs_cachedprops; + boolean_t libzfs_prop_debug; }; struct zfs_handle { diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index c79da78024..365ece14e0 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -687,6 +687,9 @@ libzfs_init(void) libzfs_mnttab_init(hdl); hdl->libzfs_cachedprops = B_FALSE; + if (getenv("ZFS_PROP_DEBUG") != NULL) { + hdl->libzfs_prop_debug = B_TRUE; + } return (hdl); } diff --git a/usr/src/lib/libzfs_core/common/libzfs_core.c b/usr/src/lib/libzfs_core/common/libzfs_core.c index 87f7ea4f46..5d92d3b1fa 100644 --- a/usr/src/lib/libzfs_core/common/libzfs_core.c +++ b/usr/src/lib/libzfs_core/common/libzfs_core.c @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 RackTop Systems. @@ -152,7 +152,15 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name, } while (ioctl(g_fd, ioc, &zc) != 0) { - if (errno == ENOMEM && resultp != NULL) { + /* + * If ioctl exited with ENOMEM, we retry the ioctl after + * increasing the size of the destination nvlist. + * + * Channel programs that exit with ENOMEM probably ran over the + * lua memory sandbox; they should not be retried. + */ + if (errno == ENOMEM && resultp != NULL && + ioc != ZFS_IOC_CHANNEL_PROGRAM) { free((void *)(uintptr_t)zc.zc_nvlist_dst); zc.zc_nvlist_dst_size *= 2; zc.zc_nvlist_dst = (uint64_t)(uintptr_t) @@ -843,3 +851,54 @@ lzc_destroy_bookmarks(nvlist_t *bmarks, nvlist_t **errlist) return (error); } + +/* + * Executes a channel program. + * + * If this function returns 0 the channel program was successfully loaded and + * ran without failing. Note that individual commands the channel program ran + * may have failed and the channel program is responsible for reporting such + * errors through outnvl if they are important. + * + * This method may also return: + * + * EINVAL The program contains syntax errors, or an invalid memory or time + * limit was given. No part of the channel program was executed. + * If caused by syntax errors, 'outnvl' contains information about the + * errors. + * + * ECHRNG The program was executed, but encountered a runtime error, such as + * calling a function with incorrect arguments, invoking the error() + * function directly, failing an assert() command, etc. Some portion + * of the channel program may have executed and committed changes. + * Information about the failure can be found in 'outnvl'. + * + * ENOMEM The program fully executed, but the output buffer was not large + * enough to store the returned value. No output is returned through + * 'outnvl'. + * + * ENOSPC The program was terminated because it exceeded its memory usage + * limit. Some portion of the channel program may have executed and + * committed changes to disk. No output is returned through 'outnvl'. + * + * ETIME The program was terminated because it exceeded its Lua instruction + * limit. Some portion of the channel program may have executed and + * committed changes to disk. No output is returned through 'outnvl'. + */ +int +lzc_channel_program(const char *pool, const char *program, uint64_t instrlimit, + uint64_t memlimit, nvlist_t *argnvl, nvlist_t **outnvl) +{ + int error; + nvlist_t *args; + + args = fnvlist_alloc(); + fnvlist_add_string(args, ZCP_ARG_PROGRAM, program); + fnvlist_add_nvlist(args, ZCP_ARG_ARGLIST, argnvl); + fnvlist_add_uint64(args, ZCP_ARG_INSTRLIMIT, instrlimit); + fnvlist_add_uint64(args, ZCP_ARG_MEMLIMIT, memlimit); + error = lzc_ioctl(ZFS_IOC_CHANNEL_PROGRAM, pool, args, outnvl); + fnvlist_free(args); + + return (error); +} diff --git a/usr/src/lib/libzfs_core/common/libzfs_core.h b/usr/src/lib/libzfs_core/common/libzfs_core.h index 3d9e14d3da..caa6dcb2a6 100644 --- a/usr/src/lib/libzfs_core/common/libzfs_core.h +++ b/usr/src/lib/libzfs_core/common/libzfs_core.h @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 RackTop Systems. */ @@ -85,6 +85,9 @@ boolean_t lzc_exists(const char *); int lzc_rollback(const char *, char *, int); +int lzc_channel_program(const char *, const char *, uint64_t, uint64_t, + nvlist_t *, nvlist_t **); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libzfs_core/common/mapfile-vers b/usr/src/lib/libzfs_core/common/mapfile-vers index fd7650c977..aa1bf945d6 100644 --- a/usr/src/lib/libzfs_core/common/mapfile-vers +++ b/usr/src/lib/libzfs_core/common/mapfile-vers @@ -19,7 +19,7 @@ # CDDL HEADER END # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2012, 2014 by Delphix. All rights reserved. +# Copyright (c) 2012, 2016 by Delphix. All rights reserved. # Copyright 2017 RackTop Systems. # # MAPFILE HEADER START @@ -43,6 +43,7 @@ SYMBOL_VERSION ILLUMOS_0.1 { libzfs_core_fini; libzfs_core_init; lzc_bookmark; + lzc_channel_program; lzc_clone; lzc_promote; lzc_create; diff --git a/usr/src/lib/libzpool/Makefile.com b/usr/src/lib/libzpool/Makefile.com index 17f24d1a11..13aa1858c6 100644 --- a/usr/src/lib/libzpool/Makefile.com +++ b/usr/src/lib/libzpool/Makefile.com @@ -20,7 +20,7 @@ # # # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2013, 2015 by Delphix. All rights reserved. +# Copyright (c) 2013, 2016 by Delphix. All rights reserved. # Copyright 2017 Joyent, Inc. # @@ -32,16 +32,17 @@ include ../../../uts/common/Makefile.files KERNEL_OBJS = kernel.o taskq.o util.o DTRACE_OBJS = zfs.o -OBJECTS=$(ZFS_COMMON_OBJS) $(ZFS_SHARED_OBJS) $(KERNEL_OBJS) +OBJECTS=$(LUA_OBJS) $(ZFS_COMMON_OBJS) $(ZFS_SHARED_OBJS) $(KERNEL_OBJS) # include library definitions include ../../Makefile.lib +LUA_SRCS= $(LUA_OBJS:%.o=../../../uts/common/fs/zfs/lua/%.c) ZFS_COMMON_SRCS= $(ZFS_COMMON_OBJS:%.o=../../../uts/common/fs/zfs/%.c) ZFS_SHARED_SRCS= $(ZFS_SHARED_OBJS:%.o=../../../common/zfs/%.c) KERNEL_SRCS= $(KERNEL_OBJS:%.o=../common/%.c) -SRCS=$(ZFS_COMMON_SRCS) $(ZFS_SHARED_SRCS) $(KERNEL_SRCS) +SRCS=$(LUA_SRCS) $(ZFS_COMMON_SRCS) $(ZFS_SHARED_SRCS) $(KERNEL_SRCS) SRCDIR= ../common # There should be a mapfile here @@ -51,6 +52,7 @@ LIBS += $(LINTLIB) $(DYNLIB) INCS += -I../common INCS += -I../../../uts/common/fs/zfs +INCS += -I../../../uts/common/fs/zfs/lua INCS += -I../../../common/zfs INCS += -I../../../common @@ -90,6 +92,10 @@ pics/%.o: ../../../uts/common/fs/zfs/%.c ../common/zfs.h $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) +pics/%.o: ../../../uts/common/fs/zfs/lua/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + pics/%.o: ../../../common/zfs/%.c ../common/zfs.h $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) diff --git a/usr/src/lib/libzpool/common/kernel.c b/usr/src/lib/libzpool/common/kernel.c index 2290164413..590398d118 100644 --- a/usr/src/lib/libzpool/common/kernel.c +++ b/usr/src/lib/libzpool/common/kernel.c @@ -703,6 +703,7 @@ vpanic(const char *fmt, va_list adx) char buf[512]; (void) vsnprintf(buf, 512, fmt, adx); assfail(buf, NULL, 0); + abort(); /* necessary to make vpanic meet noreturn requirements */ } void diff --git a/usr/src/lib/libzpool/common/sys/zfs_context.h b/usr/src/lib/libzpool/common/sys/zfs_context.h index 21853a2e70..bc43d455e0 100644 --- a/usr/src/lib/libzpool/common/sys/zfs_context.h +++ b/usr/src/lib/libzpool/common/sys/zfs_context.h @@ -62,6 +62,7 @@ extern "C" { #include <time.h> #include <procfs.h> #include <pthread.h> +#include <setjmp.h> #include <sys/debug.h> #include <libsysevent.h> #include <sys/note.h> @@ -107,8 +108,8 @@ extern void dprintf_setup(int *argc, char **argv); extern void cmn_err(int, const char *, ...); extern void vcmn_err(int, const char *, __va_list); -extern void panic(const char *, ...); -extern void vpanic(const char *, __va_list); +extern void panic(const char *, ...) __NORETURN; +extern void vpanic(const char *, __va_list) __NORETURN; #define fm_panic panic @@ -319,7 +320,7 @@ extern void kstat_runq_back_to_waitq(kstat_io_t *); #define KM_SLEEP UMEM_NOFAIL #define KM_PUSHPAGE KM_SLEEP #define KM_NOSLEEP UMEM_DEFAULT -#define KM_NORMALPRI 0 +#define KM_NORMALPRI 0 /* not needed with UMEM_DEFAULT */ #define KMC_NODEBUG UMC_NODEBUG #define KMC_NOTOUCH 0 /* not needed for userland caches */ #define kmem_alloc(_s, _f) umem_alloc(_s, _f) diff --git a/usr/src/man/man1m/Makefile b/usr/src/man/man1m/Makefile index bb7ecef1de..874d21e8b2 100644 --- a/usr/src/man/man1m/Makefile +++ b/usr/src/man/man1m/Makefile @@ -12,6 +12,8 @@ # # Copyright 2011, Richard Lowe # Copyright 2016 Joyent, Inc. +# Copyright 2015 Nexenta Systems, Inc. All rights reserved. +# Copyright (c) 2013, 2016 by Delphix. All rights reserved. # Copyright 2016 Toomas Soome <tsoome@me.com> # Copyright 2017 Nexenta Systems, Inc. # @@ -548,6 +550,7 @@ _MANFILES= 6to4relay.1m \ zdb.1m \ zdump.1m \ zfs.1m \ + zfs-program.1m \ zic.1m \ zoneadm.1m \ zoneadmd.1m \ diff --git a/usr/src/man/man1m/zfs-program.1m b/usr/src/man/man1m/zfs-program.1m new file mode 100644 index 0000000000..0834607f94 --- /dev/null +++ b/usr/src/man/man1m/zfs-program.1m @@ -0,0 +1,499 @@ +.\" This file and its contents are supplied under the terms of the +.\" Common Development and Distribution License ("CDDL"), version 1.0. +.\" You may only use this file in accordance with the terms of version +.\" 1.0 of the CDDL. +.\" +.\" A full copy of the text of the CDDL should have accompanied this +.\" source. A copy of the CDDL is also available via the Internet at +.\" http://www.illumos.org/license/CDDL. +.\" +.\" +.\" Copyright (c) 2016 by Delphix. All Rights Reserved. +.\" +.Dd January 21, 2016 +.Dt ZFS-PROGRAM 1M +.Os +.Sh NAME +.Nm zfs program +.Nd executes ZFS channel programs +.Sh SYNOPSIS +.Cm zfs program +.Op Fl t Ar instruction-limit +.Op Fl m Ar memory-limit +.Ar pool +.Ar script +.\".Op Ar optional arguments to channel program +.Sh DESCRIPTION +The ZFS channel program interface allows ZFS administrative operations to be +run programmatically as a Lua script. +The entire script is executed atomically, with no other administrative +operations taking effect concurrently. +A library of ZFS calls is made available to channel program scripts. +Channel programs may only be run with root privileges. +.Pp +A modified version of the Lua 5.2 interpreter is used to run channel program +scripts. +The Lua 5.2 manual can be found at: +.Bd -centered -offset indent +.Lk http://www.lua.org/manual/5.2/ +.Ed +.Pp +The channel program given by +.Ar script +will be run on +.Ar pool , +and any attempts to access or modify other pools will cause an error. +.Sh OPTIONS +.Bl -tag -width "-t" +.It Fl t Ar instruction-limit +Execution time limit, in number of Lua instructions to execute. +If a channel program executes more than the specified number of instructions, +it will be stopped and an error will be returned. +The default limit is 10 million instructions, and it can be set to a maximum of +100 million instructions. +.It Fl m Ar memory-limit +Memory limit, in bytes. +If a channel program attempts to allocate more memory than the given limit, it +will be stopped and an error returned. +The default memory limit is 10 MB, and can be set to a maximum of 100 MB. +.El +.Pp +All remaining argument strings will be passed directly to the Lua script as +described in the +.Sx LUA INTERFACE +section below. +.Sh LUA INTERFACE +A channel program can be invoked either from the command line, or via a library +call to +.Fn lzc_channel_program . +.Ss Arguments +Arguments passed to the channel program are converted to a Lua table. +If invoked from the command line, extra arguments to the Lua script will be +accessible as an array stored in the argument table with the key 'argv': +.Bd -literal -offset indent +args = ... +argv = args["argv"] +-- argv == {1="arg1", 2="arg2", ...} +.Ed +.Pp +If invoked from the libZFS interface, an arbitrary argument list can be +passed to the channel program, which is accessible via the same +"..." syntax in Lua: +.Bd -literal -offset indent +args = ... +-- args == {"foo"="bar", "baz"={...}, ...} +.Ed +.Pp +Note that because Lua arrays are 1-indexed, arrays passed to Lua from the +libZFS interface will have their indices incremented by 1. +That is, the element +in +.Va arr[0] +in a C array passed to a channel program will be stored in +.Va arr[1] +when accessed from Lua. +.Ss Return Values +Lua return statements take the form: +.Bd -literal -offset indent +return ret0, ret1, ret2, ... +.Ed +.Pp +Return statements returning multiple values are permitted internally in a +channel program script, but attempting to return more than one value from the +top level of the channel program is not permitted and will throw an error. +However, tables containing multiple values can still be returned. +If invoked from the command line, a return statement: +.Bd -literal -offset indent +a = {foo="bar", baz=2} +return a +.Ed +.Pp +Will be output formatted as: +.Bd -literal -offset indent +Channel program fully executed with return value: + return: + baz: 2 + foo: 'bar' +.Ed +.Ss Fatal Errors +If the channel program encounters a fatal error while running, a non-zero exit +status will be returned. +If more information about the error is available, a singleton list will be +returned detailing the error: +.Bd -literal -offset indent +error: "error string, including Lua stack trace" +.Ed +.Pp +If a fatal error is returned, the channel program may have not executed at all, +may have partially executed, or may have fully executed but failed to pass a +return value back to userland. +.Pp +If the channel program exhausts an instruction or memory limit, a fatal error +will be generated and the program will be stopped, leaving the program partially +executed. +No attempt is made to reverse or undo any operations already performed. +Note that because both the instruction count and amount of memory used by a +channel program are deterministic when run against the same inputs and +filesystem state, as long as a channel program has run successfully once, you +can guarantee that it will finish successfully against a similar size system. +.Pp +If a channel program attempts to return too large a value, the program will +fully execute but exit with a nonzero status code and no return value. +.Pp +.Em Note: +ZFS API functions do not generate Fatal Errors when correctly invoked, they +return an error code and the channel program continues executing. +See the +.Sx ZFS API +section below for function-specific details on error return codes. +.Ss Lua to C Value Conversion +When invoking a channel program via the libZFS interface, it is necessary to +translate arguments and return values from Lua values to their C equivalents, +and vice-versa. +.Pp +There is a correspondence between nvlist values in C and Lua tables. +A Lua table which is returned from the channel program will be recursively +converted to an nvlist, with table values converted to their natural +equivalents: +.Bd -literal -offset indent +string -> string +number -> int64 +boolean -> boolean_value +nil -> boolean (no value) +table -> nvlist +.Ed +.Pp +Likewise, table keys are replaced by string equivalents as follows: +.Bd -literal -offset indent +string -> no change +number -> signed decimal string ("%lld") +boolean -> "true" | "false" +.Ed +.Pp +Any collision of table key strings (for example, the string "true" and a +true boolean value) will cause a fatal error. +.Pp +Lua numbers are represented internally as signed 64-bit integers. +.Sh LUA STANDARD LIBRARY +The following Lua built-in base library functions are available: +.Bd -literal -offset indent +assert rawlen +collectgarbage rawget +error rawset +getmetatable select +ipairs setmetatable +next tonumber +pairs tostring +rawequal type +.Ed +.Pp +All functions in the +.Em coroutine , +.Em string , +and +.Em table +built-in submodules are also available. +A complete list and documentation of these modules is available in the Lua +manual. +.Pp +The following functions base library functions have been disabled and are +not available for use in channel programs: +.Bd -literal -offset indent +dofile +loadfile +load +pcall +print +xpcall +.Ed +.Sh ZFS API +.Ss Function Arguments +Each API function takes a fixed set of required positional arguments and +optional keyword arguments. +For example, the destroy function takes a single positional string argument +(the name of the dataset to destroy) and an optional "defer" keyword boolean +argument. +When using parentheses to specify the arguments to a Lua function, only +positional arguments can be used: +.Bd -literal -offset indent +zfs.sync.destroy("rpool@snap") +.Ed +.Pp +To use keyword arguments, functions must be called with a single argument that +is a Lua table containing entries mapping integers to positional arguments and +strings to keyword arguments: +.Bd -literal -offset indent +zfs.sync.destroy({1="rpool@snap", defer=true}) +.Ed +.Pp +The Lua language allows curly braces to be used in place of parenthesis as +syntactic sugar for this calling convention: +.Bd -literal -offset indent +zfs.sync.snapshot{"rpool@snap", defer=true} +.Ed +.Ss Function Return Values +If an API function succeeds, it returns 0. +If it fails, it returns an error code and the channel program continues +executing. +API functions do not generate Fatal Errors except in the case of an +unrecoverable internal file system error. +.Pp +In addition to returning an error code, some functions also return extra +details describing what caused the error. +This extra description is given as a second return value, and will always be a +Lua table, or Nil if no error details were returned. +Different keys will exist in the error details table depending on the function +and error case. +Any such function may be called expecting a single return value: +.Bd -literal -offset indent +errno = zfs.sync.promote(dataset) +.Ed +.Pp +Or, the error details can be retrieved: +.Bd -literal -offset indent +errno, details = zfs.sync.promote(dataset) +if (errno == EEXIST) then + assert(details ~= Nil) + list_of_conflicting_snapshots = details +end +.Ed +.Pp +The following global aliases for API function error return codes are defined +for use in channel programs: +.Bd -literal -offset indent +EPERM ECHILD ENODEV ENOSPC +ENOENT EAGAIN ENOTDIR ESPIPE +ESRCH ENOMEM EISDIR EROFS +EINTR EACCES EINVAL EMLINK +EIO EFAULT ENFILE EPIPE +ENXIO ENOTBLK EMFILE EDOM +E2BIG EBUSY ENOTTY ERANGE +ENOEXEC EEXIST ETXTBSY EDQUOT +EBADF EXDEV EFBIG +.Ed +.Ss API Functions +For detailed descriptions of the exact behavior of any zfs administrative +operations, see the main +.Xr zfs 1 +manual page. +.Bl -tag -width "xx" +.It Em zfs.debug(msg) +Record a debug message in the zfs_dbgmsg log. +A log of these messages can be printed via mdb's "::zfs_dbgmsg" command, or +can be monitored live by running: +.Bd -literal -offset indent + dtrace -n 'zfs-dbgmsg{trace(stringof(arg0))}' +.Ed +.Pp +msg (string) +.Bd -ragged -compact -offset "xxxx" +Debug message to be printed. +.Ed +.It Em zfs.get_prop(dataset, property) +Returns two values. +First, a string, number or table containing the property value for the given +dataset. +Second, a string containing the source of the property (i.e. the name of the +dataset in which it was set or nil if it is readonly). +Throws a Lua error if the dataset is invalid or the property doesn't exist. +Note that Lua only supports int64 number types whereas ZFS number properties +are uint64. +This means very large values (like guid) may wrap around and appear negative. +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Filesystem or snapshot path to retrieve properties from. +.Ed +.Pp +property (string) +.Bd -ragged -compact -offset "xxxx" +Name of property to retrieve. +All filesystem, snapshot and volume properties are supported except +for 'mounted' and 'iscsioptions.' +Also supports the 'written@snap' and 'written#bookmark' properties and +the '<user|group><quota|used>@id' properties, though the id must be in numeric +form. +.Ed +.El +.Bl -tag -width "xx" +.It Sy zfs.sync submodule +The sync submodule contains functions that modify the on-disk state. +They are executed in "syncing context". +.Pp +The available sync submodule functions are as follows: +.Bl -tag -width "xx" +.It Em zfs.sync.destroy(dataset, [defer=true|false]) +Destroy the given dataset. +Returns 0 on successful destroy, or a nonzero error code if the dataset could +not be destroyed (for example, if the dataset has any active children or +clones). +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Filesystem or snapshot to be destroyed. +.Ed +.Pp +[optional] defer (boolean) +.Bd -ragged -compact -offset "xxxx" +Valid only for destroying snapshots. +If set to true, and the snapshot has holds or clones, allows the snapshot to be +marked for deferred deletion rather than failing. +.Ed +.It Em zfs.sync.promote(dataset) +Promote the given clone to a filesystem. +Returns 0 on successful promotion, or a nonzero error code otherwise. +If EEXIST is returned, the second return value will be an array of the clone's +snapshots whose names collide with snapshots of the parent filesystem. +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Clone to be promoted. +.Ed +.El +.It Sy zfs.check submodule +For each function in the zfs.sync submodule, there is a corresponding zfs.check +function which performs a "dry run" of the same operation. +Each takes the same arguments as its zfs.sync counterpart and returns 0 if the +operation would succeed, or a non-zero error code if it would fail, along with +any other error details. +That is, each has the same behavior as the corresponding sync function except +for actually executing the requested change. +For example, +.Em zfs.check.destroy("fs") +returns 0 if +.Em zfs.sync.destroy("fs") +would successfully destroy the dataset. +.Pp +The available zfs.check functions are: +.Bl -tag -width "xx" +.It Em zfs.check.destroy(dataset, [defer=true|false]) +.It Em zfs.check.promote(dataset) +.El +.It Sy zfs.list submodule +The zfs.list submodule provides functions for iterating over datasets and +properties. +Rather than returning tables, these functions act as Lua iterators, and are +generally used as follows: +.Bd -literal -offset indent +for child in zfs.list.children("rpool") do + ... +end +.Ed +.Pp +The available zfs.list functions are: +.Bl -tag -width "xx" +.It Em zfs.list.clones(snapshot) +Iterate through all clones of the given snapshot. +.Pp +snapshot (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid snapshot path in the current pool. +.Ed +.It Em zfs.list.snapshots(dataset) +Iterate through all snapshots of the given dataset. +Each snapshot is returned as a string containing the full dataset name, e.g. +"pool/fs@snap". +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid filesystem or volume. +.Ed +.It Em zfs.list.children(dataset) +Iterate through all direct children of the given dataset. +Each child is returned as a string containing the full dataset name, e.g. +"pool/fs/child". +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid filesystem or volume. +.Ed +.It Em zfs.list.properties(dataset) +Iterate through all user properties for the given dataset. +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid filesystem, snapshot, or volume. +.Ed +.It Em zfs.list.system_properties(dataset) +Returns an array of strings, the names of the valid system (non-user defined) +properties for the given dataset. +Throws a Lua error if the dataset is invalid. +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid filesystem, snapshot or volume. +.Ed +.El +.El +.Sh EXAMPLES +.Ss Example 1 +The following channel program recursively destroys a filesystem and all its +snapshots and children in a naive manner. +Note that this does not involve any error handling or reporting. +.Bd -literal -offset indent +function destroy_recursive(root) + for child in zfs.list.children(root) do + destroy_recursive(child) + end + for snap in zfs.list.snapshots(root) do + zfs.sync.destroy(snap) + end + zfs.sync.destroy(root) +end +destroy_recursive("pool/somefs") +.Ed +.Ss Example 2 +A more verbose and robust version of the same channel program, which +properly detects and reports errors, and also takes the dataset to destroy +as a command line argument, would be as follows: +.Bd -literal -offset indent +succeeded = {} +failed = {} + +function destroy_recursive(root) + for child in zfs.list.children(root) do + destroy_recursive(child) + end + for snap in zfs.list.snapshots(root) do + err = zfs.sync.destroy(snap) + if (err ~= 0) then + failed[snap] = err + else + succeeded[snap] = err + end + end + err = zfs.sync.destroy(root) + if (err ~= 0) then + failed[root] = err + else + succeeded[root] = err + end +end + +args = ... +argv = args["argv"] + +destroy_recursive(argv[1]) + +results = {} +results["succeeded"] = succeeded +results["failed"] = failed +return results +.Ed +.Ss Example 3 +The following function performs a forced promote operation by attempting to +promote the given clone and destroying any conflicting snapshots. +.Bd -literal -offset indent +function force_promote(ds) + errno, details = zfs.check.promote(ds) + if (errno == EEXIST) then + assert(details ~= Nil) + for i, snap in ipairs(details) do + zfs.sync.destroy(ds .. "@" .. snap) + end + elseif (errno ~= 0) then + return errno + end + return zfs.sync.promote(ds) +end +.Ed diff --git a/usr/src/man/man1m/zfs.1m b/usr/src/man/man1m/zfs.1m index 6b872c5ac7..2ac925f71d 100644 --- a/usr/src/man/man1m/zfs.1m +++ b/usr/src/man/man1m/zfs.1m @@ -265,6 +265,12 @@ .Cm diff .Op Fl FHt .Ar snapshot Ar snapshot Ns | Ns Ar filesystem +.Nm +.Cm program +.Op Fl t Ar timeout +.Op Fl m Ar memory_limit +.Ar pool script +.Op Ar arg1 No ... .Sh DESCRIPTION The .Nm @@ -3393,6 +3399,47 @@ arrows. .It Fl t Display the path's inode change time as the first column of output. .El +.It Xo +.Nm +.Cm program +.Op Fl t Ar timeout +.Op Fl m Ar memory_limit +.Ar pool script +.Op Ar arg1 No ... +.Xc +Executes +.Ar script +as a ZFS channel program on +.Ar pool . +The ZFS channel +program interface allows ZFS administrative operations to be run +programmatically via a Lua script. +The entire script is executed atomically, with no other administrative +operations taking effect concurrently. +A library of ZFS calls is made available to channel program scripts. +Channel programs may only be run with root privileges. +.sp +For full documentation of the ZFS channel program interface, see the manual +page for +.Xr zfs-program 1M . +.Bl -tag -width "" +.It Fl t Ar timeout +Execution time limit, in milliseconds. +If a channel program executes for longer than the provided timeout, it will +be stopped and an error will be returned. +The default timeout is 1000 ms, and can be set to a maximum of 10000 ms. +.It Fl m Ar memory-limit +Memory limit, in bytes. +If a channel program attempts to allocate more memory than the given limit, +it will be stopped and an error returned. +The default memory limit is 10 MB, and can be set to a maximum of 100 MB. +.sp +All remaining argument strings are passed directly to the channel program as +arguments. +See +.Xr zfs-program 1M +for more information. +.El .El .Sh EXIT STATUS The diff --git a/usr/src/man/man3c/regcomp.3c b/usr/src/man/man3c/regcomp.3c index 1b893e3531..bd2db58ae9 100644 --- a/usr/src/man/man3c/regcomp.3c +++ b/usr/src/man/man3c/regcomp.3c @@ -1,594 +1,764 @@ -'\" te -.\" Copyright (c) 1992, X/Open Company Limited. All Rights Reserved. Portions Copyright (c) 2003, Sun Microsystems, Inc. All Rights Reserved. -.\" Sun Microsystems, Inc. gratefully acknowledges The Open Group for permission to reproduce portions of its copyrighted documentation. Original documentation from The Open Group can be obtained online at +.\" Copyright (c) 1992, 1993, 1994 Henry Spencer. +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Henry Spencer. +.\" +.\" 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. +.\" 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. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. +.\" +.\" +.\" Sun Microsystems, Inc. gratefully acknowledges The Open Group for permission +.\" to reproduce portions of its copyrighted documentation. +.\" +.\" Original documentation from The Open Group can be obtained online at .\" http://www.opengroup.org/bookstore/. -.\" The Institute of Electrical and Electronics Engineers and The Open Group, have given us permission to reprint portions of their documentation. In the following statement, the phrase "this text" refers to portions of the system documentation. Portions of this text are reprinted and reproduced in electronic form in the Sun OS Reference Manual, from IEEE Std 1003.1, 2004 Edition, Standard for Information Technology -- Portable Operating System Interface (POSIX), The Open Group Base Specifications Issue 6, Copyright (C) 2001-2004 by the Institute of Electrical and Electronics Engineers, Inc and The Open Group. In the event of any discrepancy between these versions and the original IEEE and The Open Group Standard, the original IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.opengroup.org/unix/online.html. -.\" This notice shall appear on any product containing this material. -.\" The contents of this file are subject to the terms of the Common Development and Distribution License (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] -.TH REGCOMP 3C "Nov 1, 2003" -.SH NAME -regcomp, regexec, regerror, regfree \- regular expression matching -.SH SYNOPSIS -.LP -.nf -#include <sys/types.h> -#include <regex.h> - -\fBint\fR \fBregcomp\fR(\fBregex_t *restrict\fR \fIpreg\fR, \fBconst char *restrict\fR \fIpattern\fR, - \fBint\fR \fIcflags\fR); -.fi - -.LP -.nf -\fBint\fR \fBregexec\fR(\fBconst regex_t *restrict\fR \fIpreg\fR, - \fBconst char *restrict\fR \fIstring\fR, \fBsize_t\fR \fInmatch\fR, - \fBregmatch_t\fR \fIpmatch\fR[restrict], \fBint\fR \fIeflags\fR); -.fi - -.LP -.nf -\fBsize_t\fR \fBregerror\fR(\fBint\fR \fIerrcode\fR, \fBconst regex_t *restrict\fR \fIpreg\fR, - \fBchar *restrict\fR \fIerrbuf\fR, \fBsize_t\fR \fIerrbuf_size\fR); -.fi - -.LP -.nf -\fBvoid\fR \fBregfree\fR(\fBregex_t *\fR\fIpreg\fR); -.fi - -.SH DESCRIPTION -.sp -.LP -These functions interpret \fIbasic\fR and \fIextended\fR regular expressions -(described on the \fBregex\fR(5) manual page). -.sp -.LP -The structure type \fBregex_t\fR contains at least the following member: -.sp -.ne 2 -.na -\fB\fBsize_t\fR \fBre_nsub\fR\fR -.ad -.RS 18n -Number of parenthesised subexpressions. -.RE - -.sp -.LP -The structure type \fBregmatch_t\fR contains at least the following members: -.sp -.ne 2 -.na -\fB\fBregoff_t\fR \fBrm_so\fR\fR -.ad -.RS 18n -Byte offset from start of \fIstring\fR to start of substring. -.RE - -.sp -.ne 2 -.na -\fB\fBregoff_t\fR \fBrm_eo\fR\fR -.ad -.RS 18n -Byte offset from start of \fIstring\fR of the first character after the end of -substring. -.RE - -.SS "\fBregcomp()\fR" -.sp -.LP -The \fBregcomp()\fR function will compile the regular expression contained in -the string pointed to by the \fIpattern\fR argument and place the results in -the structure pointed to by \fIpreg\fR. The \fIcflags\fR argument is the -bitwise inclusive \fBOR\fR of zero or more of the following flags, which are -defined in the header \fB<regex.h>\fR: -.sp -.ne 2 -.na -\fB\fBREG_EXTENDED\fR\fR -.ad -.RS 16n -Use Extended Regular Expressions. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_ICASE\fR\fR -.ad -.RS 16n -Ignore case in match. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_NOSUB\fR\fR -.ad -.RS 16n -Report only success/fail in \fBregexec()\fR. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_NEWLINE\fR\fR -.ad -.RS 16n -Change the handling of \fBNEWLINE\fR characters, as described in the text. -.RE - -.sp -.LP -The default regular expression type for \fIpattern\fR is a Basic Regular -Expression. The application can specify Extended Regular Expressions using the -\fBREG_EXTENDED\fR \fIcflags\fR flag. -.sp -.LP -If the \fBREG_NOSUB\fR flag was not set in \fIcflags\fR, then \fBregcomp()\fR -will set \fIre_nsub\fR to the number of parenthesised subexpressions (delimited -by \e(\e) in basic regular expressions or () in extended regular expressions) -found in \fI pattern\fR. -.SS "\fBregexec()\fR" -.sp -.LP -The \fBregexec()\fR function compares the null-terminated string specified by -\fIstring\fR with the compiled regular expression \fIpreg\fR initialized by a -previous call to \fBregcomp()\fR. The \fIeflags\fR argument is the bitwise -inclusive \fBOR\fR of zero or more of the following flags, which are defined in -the header <\fBregex.h\fR>: -.sp -.ne 2 -.na -\fB\fBREG_NOTBOL\fR\fR -.ad -.RS 14n -The first character of the string pointed to by \fIstring\fR is not the -beginning of the line. Therefore, the circumflex character (\fI^\fR), when -taken as a special character, will not match the beginning of \fIstring\fR. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_NOTEOL\fR\fR -.ad -.RS 14n -The last character of the string pointed to by \fIstring\fR is not the end of -the line. Therefore, the dollar sign (\fI$\fR), when taken as a special -character, will not match the end of \fIstring\fR. -.RE - -.sp -.LP -If \fInmatch\fR is zero or \fBREG_NOSUB\fR was set in the \fIcflags\fR argument -to \fBregcomp()\fR, then \fBregexec()\fR will ignore the \fIpmatch\fR argument. -Otherwise, the \fIpmatch\fR argument must point to an array with at least -\fInmatch\fR elements, and \fBregexec()\fR will fill in the elements of that -array with offsets of the substrings of \fIstring\fR that correspond to the -parenthesised subexpressions of \fIpattern\fR: -\fIpmatch\fR\fB[\fR\fIi\fR\fB]\fR.\fIrm_so\fR will be the byte offset of the -beginning and \fIpmatch\fR\fB[\fR\fIi\fR\fB]\fR.\fIrm_eo\fR will be one greater -than the byte offset of the end of substring \fIi\fR. (Subexpression \fIi\fR -begins at the \fIi\fRth matched open parenthesis, counting from 1.) Offsets in -\fIpmatch\fR\fB[0]\fR identify the substring that corresponds to the entire -regular expression. Unused elements of \fIpmatch\fR up to -\fIpmatch\fR\fB[\fR\fInmatch\fR\fB\(mi1]\fR will be filled with \fB\(mi1\fR\&. -If there are more than \fInmatch\fR subexpressions in \fIpattern\fR -(\fIpattern\fR itself counts as a subexpression), then \fBregexec()\fR will -still do the match, but will record only the first \fInmatch\fR substrings. -.sp -.LP -When matching a basic or extended regular expression, any given parenthesised -subexpression of \fIpattern\fR might participate in the match of several -different substrings of \fIstring\fR, or it might not match any substring even -though the pattern as a whole did match. The following rules are used to -determine which substrings to report in \fIpmatch\fR when matching regular -expressions: -.sp -.ne 2 -.na -\fB1.\fR -.ad -.RS 6n -If subexpression \fIi\fR in a regular expression is not contained within -another subexpression, and it participated in the match several times, then the -byte offsets in \fIpmatch\fR\fB[\fR\fIi\fR\fB]\fR will delimit the last such -match. -.RE - -.sp -.ne 2 -.na -\fB2.\fR -.ad -.RS 6n -If subexpression \fIi\fR is not contained within another subexpression, and it -did not participate in an otherwise successful match, the byte offsets in -\fIpmatch\fR\fB[\fR\fIi\fR\fB]\fR will be \fB\(mi1\fR\&. A subexpression does -not participate in the match when: -.sp -\fB*\fR or \fB\e{\e} \fR appears immediately after the subexpression in a basic -regular expression, or \fB*\fR, \fB?\fR, or \fB{\|}\fR appears immediately -after the subexpression in an extended regular expression, and the -subexpression did not match (matched zero times) -.sp +.\" +.\" The Institute of Electrical and Electronics Engineers and The Open Group, +.\" have given us permission to reprint portions of their documentation. In the +.\" following statement, the phrase "this text" refers to portions of the system +.\" documentation. +.\" +.\" Portions of this text are reprinted and reproduced in electronic form in the +.\" Sun OS Reference Manual, from IEEE Std 1003.1, 2004 Edition, Standard for +.\" Information Technology -- Portable Operating System Interface (POSIX), +.\" The Open Group Base Specifications Issue 6, Copyright (C) 2001-2004 by the +.\" Institute of Electrical and Electronics Engineers, Inc and The Open Group. +.\" +.\" In the event of any discrepancy between these versions and the original +.\" IEEE and The Open Group Standard, the original IEEE and The Open Group +.\" Standard is the referee document. +.\" +.\" The original Standard can be obtained online at +.\" http://www.opengroup.org/unix/online.html. +.\" +.\" This notice shall appear on any product containing this material. +.\" +.\" The contents of this file are subject to the terms of the +.\" Common Development and Distribution License (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] +.\" +.\" +.\" Copyright (c) 1992, X/Open Company Limited. All Rights Reserved. +.\" Portions Copyright (c) 2003, Sun Microsystems, Inc. All Rights Reserved. +.\" Copyright 2017 Nexenta Systems, Inc. +.\" +.Dd June 14, 2017 +.Dt REGCOMP 3C +.Os +.Sh NAME +.Nm regcomp , +.Nm regexec , +.Nm regerror , +.Nm regfree +.Nd regular-expression library +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In regex.h +.Ft int +.Fo regcomp +.Fa "regex_t *restrict preg" "const char *restrict pattern" "int cflags" +.Fc +.Ft int +.Fo regexec +.Fa "const regex_t *restrict preg" "const char *restrict string" +.Fa "size_t nmatch" "regmatch_t pmatch[restrict]" "int eflags" +.Fc +.Ft size_t +.Fo regerror +.Fa "int errcode" "const regex_t *restrict preg" +.Fa "char *restrict errbuf" "size_t errbuf_size" +.Fc +.Ft void +.Fn regfree "regex_t *preg" +.Sh DESCRIPTION +These routines implement +.St -p1003.2 +regular expressions; see +.Xr regex 5 . +The +.Fn regcomp +function compiles an RE written as a string into an internal form, +.Fn regexec +matches that internal form against a string and reports results, +.Fn regerror +transforms error codes from either into human-readable messages, +and +.Fn regfree +frees any dynamically-allocated storage used by the internal form +of an RE. +.Pp +The header +.In regex.h +declares two structure types, +.Ft regex_t +and +.Ft regmatch_t , +the former for compiled internal forms and the latter for match reporting. +It also declares the four functions, a type +.Ft regoff_t , +and a number of constants with names starting with +.Qq Dv REG_ . +.Ss Fn regcomp +The +.Fn regcomp +function compiles the regular expression contained in the +.Fa pattern +string, subject to the flags in +.Fa cflags , +and places the results in the +.Ft regex_t +structure pointed to by +.Fa preg . +The +.Fa cflags +argument is the bitwise OR of zero or more of the following flags: +.Bl -tag -width REG_EXTENDED +.It Dv REG_EXTENDED +Compile extended regular expressions +.Pq EREs , +rather than the basic regular expressions +.Pq BREs +that are the default. +.It Dv REG_BASIC +This is a synonym for 0, provided as a counterpart to +.Dv REG_EXTENDED +to improve readability. +.It Dv REG_NOSPEC +Compile with recognition of all special characters turned off. +All characters are thus considered ordinary, so the RE is a literal string. +This is an extension, compatible with but not specified by +.St -p1003.2 , +and should be used with caution in software intended to be portable to other +systems. +.Dv REG_EXTENDED +and +.Dv REG_NOSPEC +may not be used in the same call to +.Fn regcomp . +.It Dv REG_ICASE +Compile for matching that ignores upper/lower case distinctions. +See +.Xr regex 5 . +.It Dv REG_NOSUB +Compile for matching that need only report success or failure, +not what was matched. +.It Dv REG_NEWLINE +Compile for newline-sensitive matching. +By default, newline is a completely ordinary character with no special +meaning in either REs or strings. +With this flag, +.Qq [^ +bracket expressions and +.Qq \&. +never match newline, +a +.Qq \&^ +anchor matches the null string after any newline in the string in addition to +its normal function, and the +.Qq \&$ +anchor matches the null string before any newline in the string in addition to +its normal function. +.It Dv REG_PEND +The regular expression ends, not at the first NUL, but just before the character +pointed to by the +.Va re_endp +member of the structure pointed to by +.Fa preg . +The +.Va re_endp +member is of type +.Vt "const char *" . +This flag permits inclusion of NULs in the RE; they are considered ordinary +characters. +This is an extension, compatible with but not specified by +.St -p1003.2 , +and should be used with caution in software intended to be portable to other +systems. +.El +.Pp +When successful, +.Fn regcomp +returns 0 and fills in the structure pointed to by +.Fa preg . +One member of that structure +.Po other than +.Va re_endp +.Pc +is publicized: +.Va re_nsub , +of type +.Ft size_t , +contains the number of parenthesized subexpressions within the RE +.Po except that the value of this member is undefined if the +.Dv REG_NOSUB +flag was used +.Pc . +.Ss Fn regexec +The +.Fn regexec +function matches the compiled RE pointed to by +.Fa preg +against the +.Fa string , +subject to the flags in +.Fa eflags , +and reports results using +.Fa nmatch , +.Fa pmatch , +and the returned value. +The RE must have been compiled by a previous invocation of +.Fn regcomp . +The compiled form is not altered during execution of +.Fn regexec , +so a single compiled RE can be used simultaneously by multiple threads. +.Pp +By default, the NUL-terminated string pointed to by +.Fa string +is considered to be the text of an entire line, minus any terminating +newline. +The +.Fa eflags +argument is the bitwise OR of zero or more of the following flags: +.Bl -tag -width REG_STARTEND +.It Dv REG_NOTBOL +The first character of the string is treated as the continuation +of a line. +This means that the anchors +.Qq \&^ , +.Qq [[:<:]] , +and +.Qq \e< +do not match before it; but see +.Dv REG_STARTEND +below. +This does not affect the behavior of newlines under +.Dv REG_NEWLINE . +.It Dv REG_NOTEOL +The NUL terminating the string does not end a line, so the +.Qq \&$ +anchor does not match before it. +This does not affect the behavior of newlines under +.Dv REG_NEWLINE . +.It Dv REG_STARTEND +The string is considered to start at +.Fa string No + +.Fa pmatch Ns [0]. Ns Fa rm_so +and to end before the byte located at +.Fa string No + +.Fa pmatch Ns [0]. Ns Fa rm_eo , +regardless of the value of +.Fa nmatch . +See below for the definition of +.Fa pmatch +and +.Fa nmatch . +This is an extension, compatible with but not specified by +.St -p1003.2 , +and should be used with caution in software intended to be portable to other +systems. +.Pp +Without +.Dv REG_NOTBOL , +the position +.Fa rm_so +is considered the beginning of a line, such that +.Qq \&^ +matches before it, and the beginning of a word if there is a word character at +this position, such that +.Qq [[:<:]] +and +.Qq \e< +match before it. +.Pp +With +.Dv REG_NOTBOL , +the character at position +.Fa rm_so +is treated as the continuation of a line, and if +.Fa rm_so +is greater than 0, the preceding character is taken into consideration. +If the preceding character is a newline and the regular expression was compiled +with +.Dv REG_NEWLINE , +.Qq ^ +matches before the string; if the preceding character is not a word character +but the string starts with a word character, +.Qq [[:<:]] +and +.Qq \e< +match before the string. +.El +.Pp +See +.Xr regex 5 +for a discussion of what is matched in situations where an RE or a portion +thereof could match any of several substrings of +.Fa string . +.Pp +If +.Dv REG_NOSUB +was specified in the compilation of the RE, or if +.Fa nmatch +is 0, +.Fn regexec +ignores the +.Fa pmatch +argument +.Po but see below for the case where +.Dv REG_STARTEND +is specified +.Pc . +Otherwise, +.Fa pmatch +points to an array of +.Fa nmatch +structures of type +.Ft regmatch_t . +Such a structure has at least the members +.Va rm_so +and +.Va rm_eo , +both of type +.Ft regoff_t +.Po a signed arithmetic type at least as large as an +.Ft off_t +and a +.Ft ssize_t +.Pc , +containing respectively the offset of the first character of a substring +and the offset of the first character after the end of the substring. +Offsets are measured from the beginning of the +.Fa string +argument given to +.Fn regexec . +An empty substring is denoted by equal offsets, both indicating the character +following the empty substring. +.Pp +The 0th member of the +.Fa pmatch +array is filled in to indicate what substring of +.Fa string +was matched by the entire RE. +Remaining members report what substring was matched by parenthesized +subexpressions within the RE; member +.Va i +reports subexpression +.Va i , +with subexpressions counted +.Pq starting at 1 +by the order of their opening parentheses in the RE, left to right. +Unused entries in the array +.Po corresponding either to subexpressions that did not participate in the match +at all, or to subexpressions that do not exist in the RE +.Po that is, +.Va i +> +.Fa preg Ns -> Ns Va re_nsub +.Pc +.Pc +have both +.Va rm_so +and +.Va rm_eo +set to -1. +If a subexpression participated in the match several times, +the reported substring is the last one it matched. +.Po Note, as an example in particular, that when the RE +.Qq (b*)+ +matches +.Qq bbb , +the parenthesized subexpression matches each of the three +.So Li b Sc Ns s +and then an infinite number of empty strings following the last +.Qq b , +so the reported substring is one of the empties. +.Pc +.Pp +If +.Dv REG_STARTEND +is specified, +.Fa pmatch +must point to at least one +.Ft regmatch_t +.Po even if +.Fa nmatch +is 0 or +.Dv REG_NOSUB +was specified +.Pc , +to hold the input offsets for +.Dv REG_STARTEND . +Use for output is still entirely controlled by +.Fa nmatch ; +if +.Fa nmatch +is 0 or +.Dv REG_NOSUB +was specified, +the value of +.Fa pmatch Ns [0] +will not be changed by a successful +.Fn regexec . +.Ss Fn regerror +The +.Fn regerror +function maps a non-zero +.Fa errcode +from either +.Fn regcomp or -.sp -\fB|\fR is used in an extended regular expression to select this subexpression -or another, and the other subexpression matched. -.RE - -.sp -.ne 2 -.na -\fB3.\fR -.ad -.RS 6n -If subexpression \fIi\fR is contained within another subexpression \fIj\fR, and -\fIi\fR is not contained within any other subexpression that is contained -within \fIj\fR, and a match of subexpression \fIj\fR is reported in -\fIpmatch\fR\fB[\fR\fIj\fR\fB]\fR, then the match or non-match of subexpression -\fIi\fR reported in \fIpmatch\fR\fB[\fR\fIi\fR\fB]\fR will be as described in -1. and 2. above, but within the substring reported in -\fIpmatch\fR\fB[\fR\fIj\fR\fB]\fR rather than the whole string. -.RE - -.sp -.ne 2 -.na -\fB4.\fR -.ad -.RS 6n -If subexpression \fIi\fR is contained in subexpression \fIj\fR, and the byte -offsets in \fIpmatch\fR\fB[\fR\fIj\fR\fB]\fR are \fB\(mi1\fR, then the pointers -in \fIpmatch\fR\fB[\fR\fIi\fR\fB]\fR also will be \fB\(mi1\fR\&. -.RE - -.sp -.ne 2 -.na -\fB5.\fR -.ad -.RS 6n -If subexpression \fIi\fR matched a zero-length string, then both byte offsets -in \fIpmatch\fR\fB[\fR\fIi\fR\fB]\fR will be the byte offset of the character -or \fINULL\fR terminator immediately following the zero-length string. -.RE - -.sp -.LP -If, when \fBregexec()\fR is called, the locale is different from when the -regular expression was compiled, the result is undefined. -.sp -.LP -If \fBREG_NEWLINE\fR is not set in \fIcflags\fR, then a \fBNEWLINE\fR character -in \fIpattern\fR or \fIstring\fR will be treated as an ordinary character. If -\fBREG_NEWLINE\fR is set, then newline will be treated as an ordinary character -except as follows: -.sp -.ne 2 -.na -\fB1.\fR -.ad -.RS 6n -A \fBNEWLINE\fR character in \fIstring\fR will not be matched by a period -outside a bracket expression or by any form of a non-matching list. -.RE - -.sp -.ne 2 -.na -\fB2.\fR -.ad -.RS 6n -A circumflex (^) in \fIpattern\fR, when used to specify expression anchoring -will match the zero-length string immediately after a newline in \fIstring\fR, -regardless of the setting of \fBREG_NOTBOL\fR. -.RE - -.sp -.ne 2 -.na -\fB3.\fR -.ad -.RS 6n -A dollar-sign ($) in \fIpattern\fR, when used to specify expression anchoring, -will match the zero-length string immediately before a newline in \fIstring\fR, -regardless of the setting of \fBREG_NOTEOL.\fR -.RE - -.SS "\fBregfree()\fR" -.sp -.LP -The \fBregfree()\fR function frees any memory allocated by \fBregcomp()\fR -associated with \fIpreg\fR. -.sp -.LP +.Fn regexec +to a human-readable, printable message. +If +.Fa preg +is non-NULL, the error code should have arisen from use of the +.Ft regex_t +pointed to by +.Fa preg , +and if the error code came from +.Fn regcomp , +it should have been the result from the most recent +.Fn regcomp +using that +.Ft regex_t . +The +.Po +.Fn regerror +may be able to supply a more detailed message using information +from the +.Ft regex_t . +.Pc +The +.Fn regerror +function places the NUL-terminated message into the buffer pointed to by +.Fa errbuf , +limiting the length +.Pq including the NUL +to at most +.Fa errbuf_size +bytes. +If the whole message will not fit, as much of it as will fit before the +terminating NUL is supplied. +In any case, the returned value is the size of buffer needed to hold the whole +message +.Pq including terminating NUL . +If +.Fa errbuf_size +is 0, +.Fa errbuf +is ignored but the return value is still correct. +.Pp +If the +.Fa errcode +given to +.Fn regerror +is first ORed with +.Dv REG_ITOA , +the +.Qq message +that results is the printable name of the error code, e.g. +.Qq Dv REG_NOMATCH , +rather than an explanation thereof. +If +.Fa errcode +is +.Dv REG_ATOI , +then +.Fa preg +shall be non-NULL and the +.Va re_endp +member of the structure it points to must point to the printable name of an +error code; in this case, the result in +.Fa errbuf +is the decimal digits of the numeric value of the error code +.Pq 0 if the name is not recognized . +.Dv REG_ITOA +and +.Dv REG_ATOI +are intended primarily as debugging facilities; they are extensions, +compatible with but not specified by +.St -p1003.2 , +and should be used with caution in software intended to be portable to other +systems. +.Ss Fn regfree +The +.Fn regfree +function frees any dynamically-allocated storage associated with the compiled RE +pointed to by +.Fa preg . +The remaining +.Ft regex_t +is no longer a valid compiled RE and the effect of supplying it to +.Fn regexec +or +.Fn regerror +is undefined. +.Sh IMPLEMENTATION NOTES +There are a number of decisions that +.St -p1003.2 +leaves up to the implementor, +either by explicitly saying +.Qq undefined +or by virtue of them being forbidden by the RE grammar. +This implementation treats them as follows. +.Pp +There is no particular limit on the length of REs, except insofar as memory is +limited. +Memory usage is approximately linear in RE size, and largely insensitive +to RE complexity, except for bounded repetitions. +.Pp +A backslashed character other than one specifically given a magic meaning by +.St -p1003.2 +.Pq such magic meanings occur only in BREs +is taken as an ordinary character. +.Pp +Any unmatched +.Qq \&[ +is a +.Dv REG_EBRACK +error. +.Pp +Equivalence classes cannot begin or end bracket-expression ranges. +The endpoint of one range cannot begin another. +.Pp +.Dv RE_DUP_MAX , +the limit on repetition counts in bounded repetitions, is 255. +.Pp +A repetition operator +.Po +.Qq \&? , +.Qq \&* , +.Qq \&+ , +or bounds +.Pc +cannot follow another repetition operator. +A repetition operator cannot begin an expression or subexpression +or follow +.Qq \&^ +or +.Qq \&| . +.Pp +.Qq \&| +cannot appear first or last in a (sub)expression or after another +.Qq \&| , +i.e., an operand of +.Qq \&| +cannot be an empty subexpression. +An empty parenthesized subexpression, +.Qq () , +is legal and matches an empty (sub)string. +An empty string is not a legal RE. +.Pp +A +.Qq \&{ +followed by a digit is considered the beginning of bounds for a bounded +repetition, which must then follow the syntax for bounds. +A +.Qq \&{ +.Em not +followed by a digit is considered an ordinary character. +.Pp +.Qq \&^ +and +.Qq \&$ +beginning and ending subexpressions in BREs are anchors, not ordinary +characters. +.Sh RETURN VALUES +On successful completion, the +.Fn regcomp +function returns 0. +Otherwise, it returns an integer value indicating an error as described in +.In regex.h , +and the content of preg is undefined. +.Pp +On successful completion, the +.Fn regexec +function returns 0. +Otherwise it returns +.Dv REG_NOMATCH +to indicate no match, or +.Dv REG_ENOSYS +to indicate that the function is not supported. +.Pp +Upon successful completion, the +.Fn regerror +function returns the number of bytes needed to hold the entire generated string. +Otherwise, it returns 0 to indicate that the function is not implemented. +.Pp +The +.Fn regfree +function returns no value. +.Pp The following constants are defined as error return values: -.sp -.ne 2 -.na -\fB\fBREG_NOMATCH\fR\fR -.ad -.RS 16n -The \fBregexec()\fR function failed to match. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_BADPAT\fR\fR -.ad -.RS 16n +.Pp +.Bl -tag -width "REG_ECOLLATE" -compact +.It Dv REG_NOMATCH +The +.Fn regexec +function failed to match. +.It Dv REG_BADPAT Invalid regular expression. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_ECOLLATE\fR\fR -.ad -.RS 16n +.It Dv REG_ECOLLATE Invalid collating element referenced. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_ECTYPE\fR\fR -.ad -.RS 16n +.It Dv REG_ECTYPE Invalid character class type referenced. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_EESCAPE\fR\fR -.ad -.RS 16n -Trailing \e in pattern. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_ESUBREG\fR\fR -.ad -.RS 16n -Number in \e\fIdigit\fR invalid or in error. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_EBRACK\fR\fR -.ad -.RS 16n -\fB[\|]\fR imbalance. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_ENOSYS\fR\fR -.ad -.RS 16n +.It Dv REG_EESCAPE +Trailing +.Qq \&\e +in pattern. +.It Dv REG_ESUBREG +Number in +.Qq \&\e Ns Em digit +invalid or in error. +.It Dv REG_EBRACK +.Qq [] +imbalance. +.It Dv REG_ENOSYS The function is not supported. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_EPAREN\fR\fR -.ad -.RS 16n -\fB\e(\|\e)\fR or \fB()\fR imbalance. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_EBRACE\fR\fR -.ad -.RS 16n -\e{ \e} imbalance. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_BADBR\fR\fR -.ad -.RS 16n -Content of \e{ \e} invalid: not a number, number too large, more than two +.It Dv REG_EPAREN +.Qq \e(\e) +or +.Qq () +imbalance. +.It Dv REG_EBRACE +.Qq \e{\e} +imbalance. +.It Dv REG_BADBR +Content of +.Qq \e{\e} +invalid: not a number, number too large, more than two numbers, first larger than second. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_ERANGE\fR\fR -.ad -.RS 16n +.It Dv REG_ERANGE Invalid endpoint in range expression. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_ESPACE\fR\fR -.ad -.RS 16n +.It Dv REG_ESPACE Out of memory. -.RE - -.sp -.ne 2 -.na -\fB\fBREG_BADRPT\fR\fR -.ad -.RS 16n -?, * or + not preceded by valid regular expression. -.RE - -.SS "\fBregerror()\fR" -.sp -.LP -The \fBregerror()\fR function provides a mapping from error codes returned by -\fBregcomp()\fR and \fBregexec()\fR to unspecified printable strings. It -generates a string corresponding to the value of the \fIerrcode\fR argument, -which must be the last non-zero value returned by \fBregcomp()\fR or -\fBregexec()\fR with the given value of \fIpreg\fR. If \fIerrcode\fR is not -such a value, an error message indicating that the error code is invalid is -returned. -.sp -.LP -If \fIpreg\fR is a \fINULL\fR pointer, but \fIerrcode\fR is a value returned by -a previous call to \fBregexec()\fR or \fBregcomp()\fR, the \fBregerror()\fR -still generates an error string corresponding to the value of \fIerrcode\fR. -.sp -.LP -If the \fIerrbuf_size\fR argument is not zero, \fBregerror()\fR will place the -generated string into the buffer of size \fIerrbuf_size\fR bytes pointed to by -\fIerrbuf\fR. If the string (including the terminating \fBNULL)\fR cannot fit -in the buffer, \fBregerror()\fR will truncate the string and null-terminate the -result. -.sp -.LP -If \fIerrbuf_size\fR is zero, \fBregerror()\fR ignores the \fIerrbuf\fR -argument, and returns the size of the buffer needed to hold the generated -string. -.sp -.LP -If the \fIpreg\fR argument to \fBregexec()\fR or \fBregfree()\fR is not a -compiled regular expression returned by \fBregcomp()\fR, the result is -undefined. A \fIpreg\fR is no longer treated as a compiled regular expression -after it is given to \fBregfree()\fR. -.sp -.LP -See \fBregex\fR(5) for BRE (Basic Regular Expression) Anchoring. -.SH RETURN VALUES -.sp -.LP -On successful completion, the \fBregcomp()\fR function returns \fB0\fR. -Otherwise, it returns an integer value indicating an error as described in -<\fBregex.h\fR>, and the content of \fIpreg\fR is undefined. -.sp -.LP -On successful completion, the \fBregexec()\fR function returns \fB0\fR. -Otherwise it returns \fBREG_NOMATCH\fR to indicate no match, or -\fBREG_ENOSYS\fR to indicate that the function is not supported. -.sp -.LP -Upon successful completion, the \fBregerror()\fR function returns the number of -bytes needed to hold the entire generated string. Otherwise, it returns \fB0\fR -to indicate that the function is not implemented. -.sp -.LP -The \fBregfree()\fR function returns no value. -.SH ERRORS -.sp -.LP -No errors are defined. -.SH USAGE -.sp -.LP +.It Dv REG_BADRPT +.Qq \&? , +.Qq * +or +.Qq + +not preceded by valid regular expression. +.El +.Sh USAGE An application could use: -.sp -.LP -\fBregerror(code,preg,(char *)NULL,(size_t)0)\fR -.sp -.LP -to find out how big a buffer is needed for the generated string, \fBmalloc\fR a -buffer to hold the string, and then call \fBregerror()\fR again to get the -string (see \fBmalloc\fR(3C)). Alternately, it could allocate a fixed, static -buffer that is big enough to hold most strings, and then use \fBmalloc()\fR to +.Bd -literal -offset Ds +regerror(code, preg, (char *)NULL, (size_t)0) +.Ed +.Pp +to find out how big a buffer is needed for the generated string, +.Fn malloc +a buffer to hold the string, and then call +.Fn regerror +again to get the string +.Po see +.Xr malloc 3C +.Pc . +Alternately, it could allocate a fixed, static buffer that is big enough to hold +most strings, and then use +.Fn malloc allocate a larger buffer if it finds that this is too small. -.SH EXAMPLES -.LP -\fBExample 1 \fRExample to match string against the extended regular expression -in pattern. -.sp -.in +2 -.nf +.Sh EXAMPLES +Matching string against the extended regular expression in pattern. +.Bd -literal -offset Ds #include <regex.h> + /* * Match string against the extended regular expression in * pattern, treating errors as no match. * * return 1 for match, 0 for no match */ - int match(const char *string, char *pattern) { - int status; - regex_t re; - if (regcomp(&re, pattern, REG_EXTENDED\||\|REG_NOSUB) != 0) { - return(0); /* report error */ - } - status = regexec(&re, string, (size_t) 0, NULL, 0); - regfree(&re); - if (status != 0) { - return(0); /* report error */ - } - return(1); + int status; + regex_t re; + + if (regcomp(&re, pattern, REG_EXTENDED\||\|REG_NOSUB) != 0) { + return(0); /* report error */ + } + status = regexec(&re, string, (size_t) 0, NULL, 0); + regfree(&re); + if (status != 0) { + return(0); /* report error */ + } + return(1); } -.fi -.in -2 - -.sp -.LP -The following demonstrates how the \fBREG_NOTBOL\fR flag could be used with -\fBregexec()\fR to find all substrings in a line that match a pattern supplied -by a user. (For simplicity of the example, very little error checking is done.) - -.sp -.in +2 -.nf -(void) regcomp (&re, pattern, 0); -/* this call to regexec(\|) finds the first match on the line */ -error = regexec (&re, &buffer[0], 1, &pm, 0); -while (error == 0) { /* while matches found */ - /* substring found between pm.rm_so and pm.rm_eo */ - /* This call to regexec(\|) finds the next match */ - error = regexec (&re, buffer + pm.rm_eo, 1, &pm, REG_NOTBOL); +.Ed +.Pp +The following demonstrates how the +.Dv REG_NOTBOL +flag could be used with +.Fn regexec +to find all substrings in a line that match a pattern supplied by a user. +.Pq For simplicity of the example, very little error checking is done. +.Bd -literal -offset Ds +(void) regcomp(&re, pattern, 0); +/* this call to regexec() finds the first match on the line */ +error = regexec(&re, &buffer[0], 1, &pm, 0); +while (error == 0) { /* while matches found */ + /* substring found between pm.rm_so and pm.rm_eo */ + /* This call to regexec() finds the next match */ + error = regexec(&re, buffer + pm.rm_eo, 1, &pm, REG_NOTBOL); } -.fi -.in -2 - -.SH ATTRIBUTES -.sp -.LP -See \fBattributes\fR(5) for descriptions of the following attributes: -.sp - -.sp -.TS -box; -c | c -l | l . -ATTRIBUTE TYPE ATTRIBUTE VALUE -_ -CSI Enabled -_ -Interface Stability Standard -_ -MT-Level MT-Safe with exceptions -.TE - -.SH SEE ALSO -.sp -.LP -\fBfnmatch\fR(3C), \fBglob\fR(3C), \fBmalloc\fR(3C), \fBsetlocale\fR(3C), -\fBattributes\fR(5), \fBstandards\fR(5), \fBregex\fR(5) -.SH NOTES -.sp -.LP -The \fBregcomp()\fR function can be used safely in a multithreaded application -as long as \fBsetlocale\fR(3C) is not being called to change the locale. +.Ed +.Sh ERRORS +No errors are defined. +.Sh CODE SET INDEPENDENCE +.Sy Enabled +.Sh INTERFACE STABILITY +.Sy Standard +.Sh MT-LEVEL +.Sy MT-Safe with exceptions +.Pp +The +.Fn regcomp +function can be used safely in a multithreaded application as long as +.Xr setlocale 3C +is not being called to change the locale. +.Sh SEE ALSO +.Xr attributes 5 , +.Xr regex 5 , +.Xr standards 5 +.Pp +.St -p1003.2 , +sections 2.8 +.Pq Regular Expression Notation +and +B.5 +.Pq C Binding for Regular Expression Matching . diff --git a/usr/src/man/man3socket/getifaddrs.3socket b/usr/src/man/man3socket/getifaddrs.3socket index 4b9c40b7f5..69324271af 100644 --- a/usr/src/man/man3socket/getifaddrs.3socket +++ b/usr/src/man/man3socket/getifaddrs.3socket @@ -12,7 +12,7 @@ .\" .\" Copyright (c) 2013, Joyent, Inc. All rights reserved. .\" -.TH GETIFADDRS 3SOCKET "May 18, 2017" +.TH GETIFADDRS 3SOCKET "Apr 18, 2013" .SH NAME getifaddrs, freeifaddrs \- get interface addresses .SH SYNOPSIS @@ -97,9 +97,7 @@ for more information. .sp .LP -The \fIifa_data\fR member is specific to the address family. It is currently -only available for AF_LINK entries where it contains a pointer to the -\fBstruct if_data\fR (as defined in \fBif.h\fR(3HEAD)). +The \fIifa_data\fR member is presently unused. .sp .LP @@ -137,9 +135,11 @@ MT-Level MT-Safe .SH NOTES .LP -This function lists interfaces of type AF_INET, AF_INET6, and AF_LINK. -For AF_INET and AF_INET6 only interfaces with the \fBIFF_UP\fR -flag set are listed; see \fBif_tcp\fR(7P) and \fBifconfig\fR(1M) for more -information. For AF_LINK entries the interface index is only available when the -link is plumbed. +On an illumos system, this function lists only interfaces with the \fBIFF_UP\fR +flag set; see \fBif_tcp\fR(7P) and \fBifconfig\fR(1M) for more information. +.SH BUGS +.LP +At present, this function only lists addresses from the \fBAF_INET\fR and +\fBAF_INET6\fR families. Other families, such as \fBAF_LINK\fR, are not +included. diff --git a/usr/src/man/man5/regex.5 b/usr/src/man/man5/regex.5 index 68ae1656a7..8e267ac4c0 100644 --- a/usr/src/man/man5/regex.5 +++ b/usr/src/man/man5/regex.5 @@ -1,769 +1,1041 @@ -'\" te +.\" +.\" Sun Microsystems, Inc. gratefully acknowledges The Open Group for permission +.\" to reproduce portions of its copyrighted documentation. +.\" +.\" Original documentation from The Open Group can be obtained online at +.\" http://www.opengroup.org/bookstore/. +.\" +.\" The Institute of Electrical and Electronics Engineers and The Open Group, +.\" have given us permission to reprint portions of their documentation. In the +.\" following statement, the phrase "this text" refers to portions of the system +.\" documentation. +.\" +.\" Portions of this text are reprinted and reproduced in electronic form in the +.\" Sun OS Reference Manual, from IEEE Std 1003.1, 2004 Edition, Standard for +.\" Information Technology -- Portable Operating System Interface (POSIX), +.\" The Open Group Base Specifications Issue 6, Copyright (C) 2001-2004 by the +.\" Institute of Electrical and Electronics Engineers, Inc and The Open Group. +.\" +.\" In the event of any discrepancy between these versions and the original +.\" IEEE and The Open Group Standard, the original IEEE and The Open Group +.\" Standard is the referee document. +.\" +.\" The original Standard can be obtained online at +.\" http://www.opengroup.org/unix/online.html. +.\" +.\" This notice shall appear on any product containing this material. +.\" +.\" The contents of this file are subject to the terms of the +.\" Common Development and Distribution License (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] +.\" +.\" .\" Copyright (c) 1992, X/Open Company Limited All Rights Reserved .\" Portions Copyright (c) 1999, Sun Microsystems, Inc. All Rights Reserved -.\" Sun Microsystems, Inc. gratefully acknowledges The Open Group for permission to reproduce portions of its copyrighted documentation. Original documentation from The Open Group can be obtained online at -.\" http://www.opengroup.org/bookstore/. -.\" The Institute of Electrical and Electronics Engineers and The Open Group, have given us permission to reprint portions of their documentation. In the following statement, the phrase "this text" refers to portions of the system documentation. Portions of this text are reprinted and reproduced in electronic form in the Sun OS Reference Manual, from IEEE Std 1003.1, 2004 Edition, Standard for Information Technology -- Portable Operating System Interface (POSIX), The Open Group Base Specifications Issue 6, Copyright (C) 2001-2004 by the Institute of Electrical and Electronics Engineers, Inc and The Open Group. In the event of any discrepancy between these versions and the original IEEE and The Open Group Standard, the original IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.opengroup.org/unix/online.html. -.\" This notice shall appear on any product containing this material. -.\" The contents of this file are subject to the terms of the Common Development and Distribution License (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] -.TH REGEX 5 "Apr 21, 2005" -.SH NAME -regex \- internationalized basic and extended regular expression matching -.SH DESCRIPTION -.LP -Regular Expressions (REs) provide a mechanism to select specific strings from a -set of character strings. The Internationalized Regular Expressions described -below differ from the Simple Regular Expressions described on the -\fBregexp\fR(5) manual page in the following ways: -.RS +4 -.TP -.ie t \(bu -.el o +.\" Copyright 2017 Nexenta Systems, Inc. +.\" +.Dd June 2, 2017 +.Dt REGEX 5 +.Os +.Sh NAME +.Nm regex +.Nd internationalized basic and extended regular expression matching +.Sh DESCRIPTION +Regular Expressions +.Pq REs +provide a mechanism to select specific strings from a set of character strings. +The Internationalized Regular Expressions described below differ from the Simple +Regular Expressions described on the +.Xr regexp 5 +manual page in the following ways: +.Bl -bullet +.It both Basic and Extended Regular Expressions are supported -.RE -.RS +4 -.TP -.ie t \(bu -.el o -the Internationalization features\(emcharacter class, equivalence class, and -multi-character collation\(emare supported. -.RE -.sp -.LP -The Basic Regular Expression (BRE) notation and construction rules described in -the \fBBASIC\fR \fBREGULAR\fR \fBEXPRESSIONS\fR section apply to most utilities -supporting regular expressions. Some utilities, instead, support the Extended -Regular Expressions (ERE) described in the \fBEXTENDED\fR \fBREGULAR\fR -\fBEXPRESSIONS\fR section; any exceptions for both cases are noted in the -descriptions of the specific utilities using regular expressions. Both BREs and -EREs are supported by the Regular Expression Matching interfaces -\fBregcomp\fR(3C) and \fBregexec\fR(3C). -.SH BASIC REGULAR EXPRESSIONS -.SS "BREs Matching a Single Character" -.LP +.It +the Internationalization features -- character class, equivalence class, and +multi-character collation -- are supported. +.El +.Pp +The Basic Regular Expression +.Pq BRE +notation and construction rules described in the +.Sx BASIC REGULAR EXPRESSIONS +section apply to most utilities supporting regular expressions. +Some utilities, instead, support the Extended Regular Expressions +.Pq ERE +described in the +.Sx EXTENDED REGULAR EXPRESSIONS +section; any exceptions for both cases are noted in the descriptions of the +specific utilities using regular expressions. +Both BREs and EREs are supported by the Regular Expression Matching interfaces +.Xr regcomp 3C +and +.Xr regexec 3C . +.Sh BASIC REGULAR EXPRESSIONS +.Ss BREs Matching a Single Character A BRE ordinary character, a special character preceded by a backslash, or a -period matches a single character. A bracket expression matches a single -character or a single collating element. See \fBRE Bracket Expression\fR, +period matches a single character. +A bracket expression matches a single character or a single collating element. +See +.Sx RE Bracket Expression , below. -.SS "BRE Ordinary Characters" -.LP +.Ss BRE Ordinary Characters An ordinary character is a BRE that matches itself: any character in the -supported character set, except for the BRE special characters listed in \fBBRE -Special Characters\fR, below. -.sp -.LP -The interpretation of an ordinary character preceded by a backslash (\e) is -undefined, except for: -.RS +4 -.TP -1. -the characters ), (, {, and } -.RE -.RS +4 -.TP -2. -the digits 1 to 9 inclusive (see \fBBREs Matching Multiple Characters\fR, -below) -.RE -.RS +4 -.TP -3. +supported character set, except for the BRE special characters listed in +.Sx BRE Special Characters , +below. +.Pp +The interpretation of an ordinary character preceded by a backslash +.Pq Qq \e +is undefined, except for: +.Bl -enum +.It +the characters +.Qq \&) , +.Qq \&( , +.Qq { , +and +.Qq } +.It +the digits 1 to 9 inclusive +.Po see +.Sx BREs Matching Multiple Characters , +below +.Pc +.It a character inside a bracket expression. -.RE -.SS "BRE Special Characters" -.LP -A BRE \fIspecial\fR \fIcharacter\fR has special properties in certain contexts. +.El +.Ss BRE Special Characters +A BRE special character has special properties in certain contexts. Outside those contexts, or when preceded by a backslash, such a character will -be a BRE that matches the special character itself. The BRE special characters -and the contexts in which they have their special meaning are: -.sp -.ne 2 -.na -\fB\&. [ \e\fR -.ad -.RS 12n +be a BRE that matches the special character itself. +The BRE special characters and the contexts in which they have their special +meaning are: +.Bl -tag -width Ds +.It Sy \&. \&[ \&\e The period, left-bracket, and backslash are special except when used in a -bracket expression (see \fBRE Bracket Expression\fR, below). An expression -containing a [ that is not preceded by a backslash and is not part of a bracket -expression produces undefined results. -.RE - -.sp -.ne 2 -.na -\fB*\fR -.ad -.RS 12n +bracket expression +.Po see +.Sx RE Bracket Expression , +below +.Pc . +An expression containing a +.Qq \&[ +that is not preceded by a backslash and is not part of a bracket expression +produces undefined results. +.It Sy * The asterisk is special except when used: -.RS +4 -.TP -.ie t \(bu -.el o +.Bl -bullet +.It in a bracket expression -.RE -.RS +4 -.TP -.ie t \(bu -.el o -as the first character of an entire BRE (after an initial ^, if any) -.RE -.RS +4 -.TP -.ie t \(bu -.el o -as the first character of a subexpression (after an initial ^, if any); see -\fBBREs Matching Multiple Characters\fR, below. -.RE -.RE - -.sp -.ne 2 -.na -\fB\fI^\fR\fR -.ad -.RS 12n +.It +as the first character of an entire BRE +.Po after an initial +.Qq ^ , +if any +.Pc +.It +as the first character of a subexpression +.Po after an initial +.Qq ^ , +if any; see +.Sx BREs Matching Multiple Characters , +below +.Pc . +.El +.It Sy ^ The circumflex is special when used: -.RS +4 -.TP -.ie t \(bu -.el o -as an anchor (see \fBBRE Expression Anchoring\fR, below). -.RE -.RS +4 -.TP -.ie t \(bu -.el o -as the first character of a bracket expression (see \fBRE Bracket -Expression\fR, below). -.RE -.RE - -.sp -.ne 2 -.na -\fB$\fR -.ad -.RS 12n +.Bl -bullet +.It +as an anchor +.Po see +.Sx BRE Expression Anchoring , +below +.Pc . +.It +as the first character of a bracket expression +.Po see +.Sx RE Bracket Expression , +below +.Pc . +.El +.It Sy $ The dollar sign is special when used as an anchor. -.RE - -.SS "Periods in BREs" -.LP -A period (\fB\&.\fR), when used outside a bracket expression, is a BRE that -matches any character in the supported character set except NUL. -.SS "RE Bracket Expression" -.LP -A bracket expression (an expression enclosed in square brackets, [\|]) is an RE -that matches a single collating element contained in the non-empty set of -collating elements represented by the bracket expression. -.sp -.LP +.El +.Ss Periods in BREs +A period +.Pq Qq \&. , +when used outside a bracket expression, is a BRE that matches any character in +the supported character set except NUL. +.Ss RE Bracket Expression +A bracket expression +.Po an expression enclosed in square brackets, +.Qq [] +.Pc +is an RE that matches a single collating element contained in the non-empty set +of collating elements represented by the bracket expression. +.Pp The following rules and definitions apply to bracket expressions: -.RS +4 -.TP -1. -A \fIbracket expression\fR is either a matching list expression or a -non-matching list expression. It consists of one or more expressions: collating -elements, collating symbols, equivalence classes, character classes, or range -expressions (see rule 7 below). Portable applications must not use range -expressions, even though all implementations support them. The right-bracket -(]) loses its special meaning and represents itself in a bracket expression if -it occurs first in the list (after an initial circumflex (^), if any). +.Bl -enum +.It +A +.Em bracket expression +is either a matching list expression or a non-matching list expression. +It consists of one or more expressions: collating elements, collating symbols, +equivalence classes, character classes, or range expressions +.Pq see rule 7 below . +Portable applications must not use range expressions, even though all +implementations support them. +The right-bracket +.Pq Qq \&] +loses its special meaning and represents itself in a bracket expression if it +occurs first in the list +.Po after an initial circumflex +.Pq Qq ^ , +if any +.Pc . Otherwise, it terminates the bracket expression, unless it appears in a -collating symbol (such as [.].]) or is the ending right-bracket for a collating -symbol, equivalence class, or character class. The special characters: -.sp -.in +2 -.nf - . * [ \e -.fi -.in -2 -.sp - -(period, asterisk, left-bracket and backslash, respectively) lose their special -meaning within a bracket expression. -.sp -The character sequences: -.sp -.in +2 -.nf - [. [= [: -.fi -.in -2 -.sp - -(left-bracket followed by a period, equals-sign, or colon) are special inside a -bracket expression and are used to delimit collating symbols, equivalence class -expressions, and character class expressions. These symbols must be followed by -a valid expression and the matching terminating sequence .], =] or :], as -described in the following items. -.RE -.RS +4 -.TP -2. -A \fImatching list\fR expression specifies a list that matches any one of -the expressions represented in the list. The first character in the list must -not be the circumflex. For example, \fB[abc]\fR is an RE that matches any of -the characters \fBa\fR, \fBb\fR or \fBc\fR. -.RE -.RS +4 -.TP -3. -A \fInon-matching list\fR expression begins with a circumflex (^), and -specifies a list that matches any character or collating element except for the -expressions represented in the list after the leading circumflex. For example, -[^abc] is an RE that matches any character or collating element except the -characters \fBa,\fR \fBb\fR, or \fBc\fR. The circumflex will have this special -meaning only when it occurs first in the list, immediately following the -left-bracket. -.RE -.RS +4 -.TP -4. -A \fIcollating symbol\fR is a collating element enclosed within -bracket-period ([..]) delimiters. Multi-character collating elements must be -represented as collating symbols when it is necessary to distinguish them from -a list of the individual characters that make up the multi-character collating -element. For example, if the string \fBch\fR is a collating element in the -current collation sequence with the associated collating symbol <ch>, the -expression [[.ch.]] will be treated as an RE matching the character sequence -\fBch\fR, while [ch] will be treated as an RE matching \fBc\fR or \fBh\fR. -Collating symbols will be recognized only inside bracket expressions. This -implies that the RE \fB[[.ch.]]*c\fR matches the first to fifth character in -the string chchch. If the string is not a collating element in the current -collating sequence definition, or if the collating element has no characters -associated with it, the symbol will be treated as an invalid expression. -.RE -.RS +4 -.TP -5. -An \fIequivalence class expression\fR represents the set of collating -elements belonging to an equivalence class. Only primary equivalence classes -will be recognised. The class is expressed by enclosing any one of the -collating elements in the equivalence class within bracket-equal ([==]) -delimiters. For example, if a and b belong to the same equivalence class, then -[[=a=]b], [[==]b] and [[==]b] will each be equivalent to [ab]. If the collating -element does not belong to an equivalence class, the equivalence class -expression will be treated as a \fIcollating symbol\fR. -.RE -.RS +4 -.TP -6. -A \fIcharacter class expression\fR represents the set of characters -belonging to a character class, as defined in the \fBLC_CTYPE\fR category in -the current locale. All character classes specified in the current locale will -be recognized. A character class expression is expressed as a character class -name enclosed within bracket-colon ([::]) delimiters. -.sp +collating symbol +.Po such as +.Qq [.].] +.Pc +or is the ending right-bracket for a collating symbol, equivalence class, or +character class. +.Pp +The special characters +.Qq \&. , +.Qq * , +.Qq \&[ , +.Qq \&\e +.Pq period, asterisk, left-bracket and backslash, respectively +lose their special meaning within a bracket expression. +.Pp +The character sequences +.Qq [. , +.Qq [= , +.Qq [: +.Pq left-bracket followed by a period, equals-sign, or colon +are special inside a bracket expression and are used to delimit collating +symbols, equivalence class expressions, and character class expressions. +These symbols must be followed by a valid expression and the matching +terminating sequence +.Qq .] , +.Qq =] +or +.Qq :] , +as described in the following items. +.It +A +.Em matching list expression +specifies a list that matches any one of the expressions represented in the +list. +The first character in the list must not be the circumflex. +For example, +.Qq [abc] +is an RE that matches any of the characters +.Qq a , +.Qq b +or +.Qq c . +.It +A +.Em non-matching list expression +begins with a circumflex +.Pq Qq ^ , +and specifies a list that matches any character or collating element except for +the expressions represented in the list after the leading circumflex. +For example, +.Qq [^abc] +is an RE that matches any character or collating element except the characters +.Qq a , +.Qq b , +or +.Qq c . +The circumflex will have this special meaning only when it occurs first in the +list, immediately following the left-bracket. +.It +A +.Em collating symbol +is a collating element enclosed within bracket-period +.Pq Qq [..] +delimiters. +Multi-character collating elements must be represented as collating symbols when +it is necessary to distinguish them from a list of the individual characters +that make up the multi-character collating element. +For example, if the string +.Qq ch +is a collating element in the current collation sequence with the associated +collating symbol +.Qq Aq ch , +the expression +.Qq [[.ch.]] +will be treated as an RE matching the character sequence +.Qq ch , +while +.Qq [ch] +will be treated as an RE matching +.Qq c +or +.Qq h . +Collating symbols will be recognized only inside bracket expressions. +This implies that the RE +.Qq [[.ch.]]*c +matches the first to fifth character in the string +.Qq chchch. +If the string is not a collating element in the current collating sequence +definition, or if the collating element has no characters associated with it, +the symbol will be treated as an invalid expression. +.It +An +.Em equivalence class expression +represents the set of collating elements belonging to an equivalence class. +Only primary equivalence classes will be recognised. +The class is expressed by enclosing any one of the collating elements in the +equivalence class within bracket-equal +.Pq Qq [==] +delimiters. +For example, if +.Qq a +and +.Qq b +belong to the same equivalence class, then +.Qq [[=a=]b] , +.Qq [[==]a] +and +.Qq [[==]b] +will each be equivalent to +.Qq [ab] . +If the collating element does not belong to an equivalence class, the +equivalence class expression will be treated as a +.Em collating symbol . +.It +A +.Em character class expression +represents the set of characters belonging to a character class, as defined in +the +.Ev LC_CTYPE +category in the current locale. +All character classes specified in the current locale will be recognized. +A character class expression is expressed as a character class name enclosed +within bracket-colon +.Pq Qq [::] +delimiters. +.Pp The following character class expressions are supported in all locales: -.sp - -.sp -.TS -l l l l -l l l l . -\fB[:alnum:]\fR \fB[:cntrl:]\fR \fB[:lower:]\fR \fB[:space:]\fR -\fB[:alpha:]\fR \fB[:digit:]\fR \fB[:print:]\fR \fB[:upper:]\fR -\fB[:blank:]\fR \fB[:graph:]\fR \fB[:punct:]\fR \fB[:xdigit:]\fR -.TE - -In addition, character class expressions of the form: -.sp -.in +2 -.nf - \fB[:name:]\fR -.fi -.in -2 -.sp - -are recognized in those locales where the \fIname\fR keyword has been given a -\fBcharclass\fR definition in the \fBLC_CTYPE\fR category. -.RE -.RS +4 -.TP -7. -A \fIrange expression\fR represents the set of collating elements that fall -between two elements in the current collation sequence, inclusively. It is -expressed as the starting point and the ending point separated by a hyphen -(\fB-\fR). -.sp +.Bl -column "[:alnum:]" "[:cntrl:]" "[:lower:]" "[:xdigit:]" +.It [:alnum:] Ta [:cntrl:] Ta [:lower:] Ta [:space:] +.It [:alpha:] Ta [:digit:] Ta [:print:] Ta [:upper:] +.It [:blank:] Ta [:graph:] Ta [:punct:] Ta [:xdigit:] +.El +.Pp +In addition, character class expressions of the form +.Qq [:name:] +are recognized in those locales where the +.Em name +keyword has been given a +.Em charclass +definition in the +.Ev LC_CTYPE +category. +.It +A +.Em range expression +represents the set of collating elements that fall between two elements in the +current collation sequence, inclusively. +It is expressed as the starting point and the ending point separated by a hyphen +.Pq Qq - . +.Pp Range expressions must not be used in portable applications because their -behavior is dependent on the collating sequence. Ranges will be treated -according to the current collating sequence, and include such characters that -fall within the range based on that collating sequence, regardless of character -values. This, however, means that the interpretation will differ depending on -collating sequence. If, for instance, one collating sequence defines as a -variant of \fBa\fR, while another defines it as a letter following \fBz\fR, -then the expression [-z] is valid in the first language and invalid in the -second. +behavior is dependent on the collating sequence. +Ranges will be treated according to the current collating sequence, and include +such characters that fall within the range based on that collating sequence, +regardless of character values. +This, however, means that the interpretation will differ depending on collating +sequence. +If, for instance, one collating sequence defines as a variant of +.Qq a , +while another defines it as a letter following +.Qq z , +then the expression +.Qq [-z] +is valid in the first language and invalid in the second. .sp In the following, all examples assume the collation sequence specified for the POSIX locale, unless another collation sequence is specifically defined. -.sp +.Pp The starting range point and the ending range point must be a collating element -or collating symbol. An equivalence class expression used as a starting or -ending point of a range expression produces unspecified results. An equivalence -class can be used portably within a bracket expression, but only outside the -range. For example, the unspecified expression [[=e=]\(mif] should be given as -[[=e=]e\(mif]. The ending range point must collate equal to or higher than the -starting range point; otherwise, the expression will be treated as invalid. The -order used is the order in which the collating elements are specified in the -current collation definition. One-to-many mappings (see \fBlocale\fR(5)) will -not be performed. For example, assuming that the character eszet is placed in -the collation sequence after \fBr\fR and \fBs\fR, but before \fBt\fR, and that -it maps to the sequence \fBss\fR for collation purposes, then the expression -[r\(mis] matches only \fBr\fR and \fBs\fR, but the expression [s\(mit] matches -\fBs\fR, \fBbeta\fR, or \fBt\fR. -.sp +or collating symbol. +An equivalence class expression used as a starting or ending point of a range +expression produces unspecified results. +An equivalence class can be used portably within a bracket expression, but only +outside the range. +For example, the unspecified expression +.Qq [[=e=]-f] +should be given as +.Qq [[=e=]e-f] . +The ending range point must collate equal to or higher than the starting range +point; otherwise, the expression will be treated as invalid. +The order used is the order in which the collating elements are specified in the +current collation definition. +One-to-many mappings +.Po see +.Xr locale 5 +.Pc +will not be performed. +For example, assuming that the character +.Qq eszet +is placed in the collation sequence after +.Qq r +and +.Qq s , +but before +.Qq t , +and that it maps to the sequence +.Qq ss +for collation purposes, then the expression +.Qq [r-s] +matches only +.Qq r +and +.Qq s , +but the expression +.Qq [s-t] +matches +.Qq s , +.Qq beta , +or +.Qq t . +.Pp The interpretation of range expressions where the ending range point is also -the starting range point of a subsequent range expression (for instance -[a\(mim\(mio]) is undefined. -.sp -The hyphen character will be treated as itself if it occurs first (after an -initial ^, if any) or last in the list, or as an ending range point in a range -expression. As examples, the expressions [\(miac] and [ac\(mi] are equivalent -and match any of the characters \fBa\fR, \fBc\fR, or \fB\(mi;\fR [^\(miac] and -[^ac\(mi] are equivalent and match any characters except \fBa\fR, \fBc\fR, or -\(mi; the expression [%\(mi\|\(mi] matches any of the characters between % and -\(mi inclusive; the expression [\(mi\|\(mi@] matches any of the characters -between \(mi and @ inclusive; and the expression [a\(mi\|\(mi@] is invalid, -because the letter \fBa\fR follows the symbol \(mi in the POSIX locale. To use -a hyphen as the starting range point, it must either come first in the bracket -expression or be specified as a collating symbol, for example: -[][.\(mi.]\(mi0], which matches either a right bracket or any character or -collating element that collates between hyphen and 0, inclusive. -.sp -If a bracket expression must specify both \(mi and ], the ] must be placed -first (after the ^, if any) and the \(mi last within the bracket expression. -.RE -.sp -.LP -Note: Latin-1 characters such as \(ga or ^ are not printable in some locales, -for example, the \fBja\fR locale. -.SS "BREs Matching Multiple Characters" -.LP +the starting range point of a subsequent range expression +.Po for instance +.Qq [a-m-o] +.Pc +is undefined. +.Pp +The hyphen character will be treated as itself if it occurs first +.Po after an initial +.Qq ^ , +if any +.Pc +or last in the list, or as an ending range point in a range expression. +As examples, the expressions +.Qq [-ac] +and +.Qq [ac-] +are equivalent and match any of the characters +.Qq a , +.Qq c , +or +.Qq -; +.Qq [^-ac] +and +.Qq [^ac-] +are equivalent and match any characters except +.Qq a , +.Qq c , +or +.Qq -; +the expression +.Qq [%--] +matches any of the characters between +.Qq % +and +.Qq - +inclusive; the expression +.Qq [--@] +matches any of the characters between +.Qq - +and +.Qq @ +inclusive; and the expression +.Qq [a--@] +is invalid, because the letter +.Qq a +follows the symbol +.Qq - +in the POSIX locale. +To use a hyphen as the starting range point, it must either come first in the +bracket expression or be specified as a collating symbol, for example: +.Qq [][.-.]-0] , +which matches either a right bracket or any character or collating element that +collates between hyphen and 0, inclusive. +.Pp +If a bracket expression must specify both +.Qq - +and +.Qq \&] , +the +.Qq \&] +must be placed first +.Po after the +.Qq ^ , +if any +.Pc +and the +.Qq - +last within the bracket expression. +.El +.Pp +Note: Latin-1 characters such as +.Qq \(ga +or +.Qq ^ +are not printable in some locales, for example, the +.Em ja +locale. +.Ss BREs Matching Multiple Characters The following rules can be used to construct BREs matching multiple characters from BREs matching a single character: -.RS +4 -.TP -1. +.Bl -enum +.It The concatenation of BREs matches the concatenation of the strings matched by each component of the BRE. -.RE -.RS +4 -.TP -2. -A \fIsubexpression\fR can be defined within a BRE by enclosing it between -the character pairs \e( and \e) . Such a subexpression matches whatever it -would have matched without the \e( and \e), except that anchoring within -subexpressions is optional behavior; see \fBBRE Expression Anchoring\fR, below. +.It +A +.Em subexpression +can be defined within a BRE by enclosing it between the character pairs +.Qq \e( +and +.Qq \e) . +Such a subexpression matches whatever it would have matched without the +.Qq \e( +and +Qq \e) , +except that anchoring within subexpressions is optional behavior; see +.Sx BRE Expression Anchoring , +below. Subexpressions can be arbitrarily nested. -.RE -.RS +4 -.TP -3. -The \fIback-reference\fR expression \e\fIn\fR matches the same (possibly -empty) string of characters as was matched by a subexpression enclosed between -\e( and \e) preceding the \e\fIn\fR. The character \fIn\fR must be a digit from -1 to 9 inclusive, \fIn\fRth subexpression (the one that begins with the -\fIn\fRth \e( and ends with the corresponding paired \e)\|). The expression is -invalid if less than \fIn\fR subexpressions precede the \e\fIn\fR. For example, -the expression \|^\e(.*\e)\e1$ matches a line consisting of two adjacent -appearances of the same string, and the expression \e(a\e)*\e1 fails to match -\fBa\fR. The limit of nine back-references to subexpressions in the RE is based -on the use of a single digit identifier. This does not imply that only nine -subexpressions are allowed in REs. The following is a valid BRE with ten -subexpressions: -.sp -.in +2 -.nf -\fB\e(\e(\e(ab\e)*c\e)*d\e)\e(ef\e)*\e(gh\e)\e{2\e}\e(ij\e)*\e(kl\e)*\e(mn\e)*\e(op\e)*\e(qr\e)*\fR -.fi -.in -2 -.sp - -.RE -.RS +4 -.TP -4. -When a BRE matching a single character, a subexpression or a back-reference -is followed by the special character asterisk (*), together with that asterisk -it matches what zero or more consecutive occurrences of the BRE would match. -For example, \fB[ab]*\fR \fBand\fR \fB[ab][ab]\fR are equivalent when matching -the string \fBab\fR. -.RE -.RS +4 -.TP -5. +.It +The +.Em back-reference +expression +.Qq \e Ns Em n +matches the same +.Pq possibly empty +string of characters as was matched by a subexpression enclosed between +.Qq \e( +and +.Qq \e) +preceding the +.Qq \e Ns Em n . +The character +.Qq Em n +must be a digit from 1 to 9 inclusive, +.Em n Ns th +subexpression +.Po the one that begins with the +.Em n Ns th +.Qq \e( +and ends with the corresponding paired +.Qq \e) +.Pc . +The expression is invalid if less than +.Em n +subexpressions precede the +.Qq \e Ns Em n . +For example, the expression +.Qq ^\e(.*\e)\e1$ +matches a line consisting of two adjacent appearances of the same string, and +the expression +.Qq \e(a\e)*\e1 +fails to match +.Qq a . +The limit of nine back-references to subexpressions in the RE is based on the +use of a single digit identifier. +This does not imply that only nine subexpressions are allowed in REs. +.It +When a BRE matching a single character, a subexpression or a back-reference is +followed by the special character asterisk +.Pq Qq * , +together with that asterisk it matches what zero or more consecutive occurrences +of the BRE would match. +For example, +.Qq [ab]* +and +.Qq [ab][ab] +are equivalent when matching the string +.Qq ab . +.It When a BRE matching a single character, a subexpression, or a back-reference -is followed by an \fIinterval expression\fR of the format \e{\fIm\fR\e}, -\e{\fIm\fR,\e} or \e{\fIm\fR,\fIn\fR\e}, together with that interval expression -it matches what repeated consecutive occurrences of the BRE would match. The -values of \fIm\fR and \fIn\fR will be decimal integers in the range 0 \(<= -\fIm\fR \(<= \fIn\fR \(<= {\fBRE_DUP_MAX\fR}, where \fIm\fR specifies the exact -or minimum number of occurrences and \fIn\fR specifies the maximum number of -occurrences. The expression \e{\fIm\fR\e} matches exactly \fIm\fR occurrences -of the preceding BRE, \e{\fIm\fR,\e} matches at least \fIm\fR occurrences and -\e{\fIm,n\fR\e} matches any number of occurrences between \fIm\fR and \fIn\fR, +is followed by an +.Em interval expression +of the format +.Qq \e{ Ns Em m Ns \e} , +.Qq \e{ Ns Em m Ns ,\e} +or +.Qq \e{ Ns Em m Ns , Ns Em n Ns \e} , +together with that interval expression it matches what repeated consecutive +occurrences of the BRE would match. +The values of +.Em m +and +.Em n +will be decimal integers in the range 0 <= +.Em m +<= +.Em n +<= +.Dv BRE_DUP_MAX , +where +.Em m +specifies the exact or minimum number of occurrences and +.Em n +specifies the maximum number of occurrences. +The expression +.Qq \e{ Ns Em m Ns \e} +matches exactly +.Em m +occurrences of the preceding BRE, +.Qq \e{ Ns Em m Ns ,\e} +matches at least +.Em m +occurrences and +.Qq \e{ Ns Em m Ns , Ns Em n Ns \e} +matches any number of occurrences between +.Em m +and +.Em n , inclusive. -.sp -For example, in the string \fBabababccccccd\fR, the BRE c\e{3\e} is matched by -characters seven to nine, the BRE \e(ab\e)\e{4,\e} is not matched at all and -the BRE c\e{1,3\e}d is matched by characters ten to thirteen. -.RE -.sp -.LP -The behavior of multiple adjacent duplication symbols \fB(\fR \fB*\fR \fB\fR -and intervals) produces undefined results. -.SS "BRE Precedence" -.LP +.Pp +For example, in the string +.Qq abababccccccd , +the BRE +.Qq c\e{3\e} +is matched by characters seven to nine, the BRE +.Qq \e(ab\e)\e{4,\e} +is not matched at all and the BRE +.Qq c\e{1,3\e}d +is matched by characters ten to thirteen. +.El +.Pp +The behavior of multiple adjacent duplication symbols +.Po Qq * +and intervals +.Pc +produces undefined results. +.Ss BRE Precedence The order of precedence is as shown in the following table: -.sp -.in +2 -.nf - -.fi -.in -2 -.sp - -.sp - -.sp -.TS -box; -l l -l l . -\fBBRE Precedence (from high to low)\fR -collation-related bracket symbols [= =] [: :] [. .] -escaped characters \e<\fIspecial character\fR> -bracket expression [ ] -subexpressions/back-references \e( \e) \e\fIn\fR -single-character-BRE duplication * \e{\fIm\fR,\fIn\fR\e} -concatenation -anchoring ^ $ -.TE - -.SS "BRE Expression Anchoring" -.LP +.Bl -column "BRE Precedence (from high to low)" "" +.It Sy BRE Precedence (from high to low) Ta +.It collation-related bracket symbols Ta [= =] [: :] [. .] +.It escaped characters Ta \e< Ns Em special character Ns > +.It bracket expression Ta [ ] +.It subexpressions/back-references Ta \e( \e) \e Ns Em n +.It single-character-BRE duplication Ta * \e{ Ns Em m Ns , Ns Em n Ns \e} +.It concatenation Ta +.It anchoring Ta ^ $ +.El +.Ss BRE Expression Anchoring A BRE can be limited to matching strings that begin or end a line; this is -called \fIanchoring\fR. The circumflex and dollar sign special characters will -be considered BRE anchors in the following contexts: -.RS +4 -.TP -1. -A circumflex \fB(\fR \fB^\fR \fB)\fR is an anchor when used as the first -character of an entire BRE. The implementation may treat circumflex as an -anchor when used as the first character of a subexpression. The circumflex will -anchor the expression to the beginning of a string; only sequences starting at -the first character of a string will be matched by the BRE. For example, the -BRE ^ab matches \fBab\fR in the string \fBabcdef,\fR but fails to match in the -string \fBcdefab\fR. A portable BRE must escape a leading circumflex in a -subexpression to match a literal circumflex. -.RE -.RS +4 -.TP -2. -A dollar sign \fB(\fR \fB$\fR \fB)\fR is an anchor when used as the last -character of an entire BRE. The implementation may treat a dollar sign as an -anchor when used as the last character of a subexpression. The dollar sign will -anchor the expression to the end of the string being matched; the dollar sign -can be said to match the end-of-string following the last character. -.RE -.RS +4 -.TP -3. -A BRE anchored by both \fB^\fR and \fB$\fR matches only an entire string. -For example, the BRE \fB^abcdef$\fR matches strings consisting only of -\fBabcdef\fR. -.RE -.RS +4 -.TP -4. -\fB^\fR and \fB$\fR are not special in subexpressions. -.RE -.sp -.LP +called +.Em anchoring . +The circumflex and dollar sign special characters will be considered BRE anchors +in the following contexts: +.Bl -enum +.It +A circumflex +.Pq Qq ^ +is an anchor when used as the first character of an entire BRE. +The implementation may treat circumflex as an anchor when used as the first +character of a subexpression. +The circumflex will anchor the expression to the beginning of a string; +only sequences starting at the first character of a string will be matched by +the BRE. +For example, the BRE +.Qq ^ab +matches +.Qq ab +in the string +.Qq abcdef , +but fails to match in the string +.Qq cdefab . +A portable BRE must escape a leading circumflex in a subexpression to match a +literal circumflex. +.It +A dollar sign +.Pq Qq $ +is an anchor when used as the last character of an entire BRE. +The implementation may treat a dollar sign as an anchor when used as the last +character of a subexpression. +The dollar sign will anchor the expression to the end of the string being +matched; the dollar sign can be said to match the end-of-string following the +last character. +.It +A BRE anchored by both +.Qq ^ +and +.Qq $ +matches only an entire string. +For example, the BRE +^abcdef$ +matches strings consisting only of +.Qq abcdef . +.It +.Qq ^ +and +.Qq $ +are not special in subexpressions. +.El +.Pp Note: The Solaris implementation does not support anchoring in BRE subexpressions. -.SH EXTENDED REGULAR EXPRESSIONS -.LP -The rules specified for BREs apply to Extended Regular Expressions (EREs) with -the following exceptions: -.RS +4 -.TP -.ie t \(bu -.el o -The characters \fB|\fR, \fB+\fR, and \fB?\fR have special meaning, as defined -below. -.RE -.RS +4 -.TP -.ie t \(bu -.el o -The \fB{\fR and \fB}\fR characters, when used as the duplication operator, are -not preceded by backslashes. The constructs \fB\e{\fR and \fB\e}\fR simply -match the characters \fB{\fR and \fB}\fR, respectively. -.RE -.RS +4 -.TP -.ie t \(bu -.el o +.Sh EXTENDED REGULAR EXPRESSIONS +The rules specified for BREs apply to Extended Regular Expressions +.Pq EREs +with the following exceptions: +.Bl -bullet +.It +The characters +.Qq | , +.Qq + , +and +.Qq \&? +have special meaning, as defined below. +.It +The +.Qq { +and +.Qq } +characters, when used as the duplication operator, are not preceded by +backslashes. +The constructs +.Qq \e{ +and +.Qq \e} +simply match the characters +.Qq { +and +.Qq }, respectively. +.It The back reference operator is not supported. -.RE -.RS +4 -.TP -.ie t \(bu -.el o -Anchoring (\fB^$\fR) is supported in subexpressions. -.RE -.SS "EREs Matching a Single Character" -.LP +.It +Anchoring +.Pq Qq ^$ +is supported in subexpressions. +.El +.Ss EREs Matching a Single Character An ERE ordinary character, a special character preceded by a backslash, or a -period matches a single character. A bracket expression matches a single -character or a single collating element. An \fIERE\fR \fImatching\fR \fIa\fR -\fIsingle\fR \fIcharacter\fR enclosed in parentheses matches the same as the -ERE without parentheses would have matched. -.SS "ERE Ordinary Characters" -.LP -An \fIordinary character\fR is an ERE that matches itself. An ordinary -character is any character in the supported character set, except for the ERE -special characters listed in \fBERE\fR \fBSpecial\fR \fBCharacters\fR below. -The interpretation of an ordinary character preceded by a backslash (\fB\e\fR) +period matches a single character. +A bracket expression matches a single character or a single collating element. +An +.Em ERE matching a single character +enclosed in parentheses matches the same as the ERE without parentheses would +have matched. +.Ss ERE Ordinary Characters +An +.Em ordinary character +is an ERE that matches itself. +An ordinary character is any character in the supported character set, except +for the ERE special characters listed in +.Sx ERE Special Characters +below. +The interpretation of an ordinary character preceded by a backslash +.Pq Qq \&\e is undefined. -.SS "ERE Special Characters" -.LP -An \fIERE\fR \fIspecial\fR \fIcharacter\fR has special properties in certain -contexts. Outside those contexts, or when preceded by a backslash, such a -character is an ERE that matches the special character itself. The extended -regular expression special characters and the contexts in which they have their -special meaning are: -.sp -.ne 2 -.na -\fB\&. [ \e (\fR -.ad -.RS 14n +.Ss ERE Special Characters +An +.Em ERE special character +has special properties in certain contexts. +Outside those contexts, or when preceded by a backslash, such a character is an +ERE that matches the special character itself. +The extended regular expression special characters and the contexts in which +they have their special meaning are: +.Bl -tag -width Ds +.It Sy \&. \&[ \&\e \&( The period, left-bracket, backslash, and left-parenthesis are special except -when used in a bracket expression (see \fBRE Bracket Expression\fR, above). +when used in a bracket expression +.Po see +.Sx RE Bracket Expression , +above +.Pc . Outside a bracket expression, a left-parenthesis immediately followed by a right-parenthesis produces undefined results. -.RE - -.sp -.ne 2 -.na -\fB)\fR -.ad -.RS 14n +.It Sy \&) The right-parenthesis is special when matched with a preceding left-parenthesis, both outside a bracket expression. -.RE - -.sp -.ne 2 -.na -\fB* + ? {\fR -.ad -.RS 14n +.It Sy * + \&? { The asterisk, plus-sign, question-mark, and left-brace are special except when -used in a bracket expression (see \fBRE Bracket Expression\fR, above). Any of -the following uses produce undefined results: -.RS +4 -.TP -.ie t \(bu -.el o +used in a bracket expression +.Po see +.Sx RE Bracket Expression , +above +.Pc . +Any of the following uses produce undefined results: +.Bl -bullet +.It if these characters appear first in an ERE, or immediately following a vertical-line, circumflex or left-parenthesis -.RE -.RS +4 -.TP -.ie t \(bu -.el o +.It if a left-brace is not part of a valid interval expression. -.RE -.RE - -.sp -.ne 2 -.na -\fB|\fR -.ad -.RS 14n -The vertical-line is special except when used in a bracket expression (see -\fBRE Bracket Expression\fR, above). A vertical-line appearing first or last in -an ERE, or immediately following a vertical-line or a left-parenthesis, or -immediately preceding a right-parenthesis, produces undefined results. -.RE - -.sp -.ne 2 -.na -\fB^\fR -.ad -.RS 14n +.El +.It Sy \&| +The vertical-line is special except when used in a bracket expression +.Po see +.Sx RE Bracket Expression , +above +.Pc . +A vertical-line appearing first or last in an ERE, or immediately following a +vertical-line or a left-parenthesis, or immediately preceding a +right-parenthesis, produces undefined results. +.It Sy ^ The circumflex is special when used: -.RS +4 -.TP -.ie t \(bu -.el o -as an anchor (see \fBERE Expression Anchoring\fR, below). -.RE -.RS +4 -.TP -.ie t \(bu -.el o -as the first character of a bracket expression (see \fBRE Bracket -Expression\fR, above). -.RE -.RE - -.sp -.ne 2 -.na -\fB$\fR -.ad -.RS 14n +.Bl -bullet +.It +as an anchor +.Po see +.Sx ERE Expression Anchoring , +below +.Pc . +.It +as the first character of a bracket expression +.Po see +.Sx RE Bracket Expression , +above +.Pc . +.El +.It Sy $ The dollar sign is special when used as an anchor. -.RE - -.SS "Periods in EREs" -.LP -A period (\fB\&.\fR), when used outside a bracket expression, is an ERE that -matches any character in the supported character set except NUL. -.SS "ERE Bracket Expression" -.LP +.El +.Ss Periods in EREs +A period +.Pq Qq \&. , +when used outside a bracket expression, is an ERE that matches any character in +the supported character set except NUL. +.Ss ERE Bracket Expression The rules for ERE Bracket Expressions are the same as for Basic Regular -Expressions; see \fBRE Bracket Expression\fR, above). -.SS "EREs Matching Multiple Characters" -.LP +Expressions; see +.Sx RE Bracket Expression , +above. +.Ss EREs Matching Multiple Characters The following rules will be used to construct EREs matching multiple characters from EREs matching a single character: -.RS +4 -.TP -1. -A \fIconcatenation\fR \fIof\fR \fIEREs\fR matches the concatenation of the -character sequences matched by each component of the ERE. A concatenation of -EREs enclosed in parentheses matches whatever the concatenation without the -parentheses matches. For example, both the ERE \fBcd\fR and the ERE \fB(cd)\fR -are matched by the third and fourth character of the string \fBabcdefabcdef\fR. -.RE -.RS +4 -.TP -2. +.Bl -enum +.It +A +.Em concatenation of EREs +matches the concatenation of the character sequences matched by each component +of the ERE. +A concatenation of EREs enclosed in parentheses matches whatever the +concatenation without the parentheses matches. +For example, both the ERE +.Qq cd +and the ERE +.Qq (cd) +are matched by the third and fourth character of the string +.Qq abcdefabcdef . +.It When an ERE matching a single character or an ERE enclosed in parentheses is -followed by the special character plus-sign (+), together with that plus-sign -it matches what one or more consecutive occurrences of the ERE would match. For -example, the ERE \fBb+(bc)\fR matches the fourth to seventh characters in the -string \fBacabbbcde;\fR \fB[ab]\fR + and \fB[ab][ab]*\fR are equivalent. -.RE -.RS +4 -.TP -3. +followed by the special character plus-sign +.Pq Qq + , +together with that plus-sign it matches what one or more consecutive occurrences +of the ERE would match. +For example, the ERE +.Qq b+(bc) +matches the fourth to seventh characters in the string +.Qq acabbbcde ; +.Qq [ab]+ +and +.Qq [ab][ab]* +are equivalent. +.It When an ERE matching a single character or an ERE enclosed in parentheses is -followed by the special character asterisk (\fB*\fR), together with that -asterisk it matches what zero or more consecutive occurrences of the ERE would -match. For example, the ERE \fBb*c\fR matches the first character in the string -\fBcabbbcde,\fR and the ERE \fBb*cd\fR matches the third to seventh characters -in the string \fBcabbbcdebbbbbbcdbc\fR. And, \fB[ab]*\fR and \fB[ab][ab]\fR are -equivalent when matching the string \fBab\fR. -.RE -.RS +4 -.TP -4. +followed by the special character asterisk +.Pq Qq * , +together with that asterisk it matches what zero or more consecutive occurrences +of the ERE would match. +For example, the ERE +.Qq b*c +matches the first character in the string +.Qq cabbbcde , +and the ERE +.Qq b*cd +matches the third to seventh characters in the string +.Qq cabbbcdebbbbbbcdbc . +And, +.Qq [ab]* +and +.Qq [ab][ab] +are equivalent when matching the string +.Qq ab . +.It When an ERE matching a single character or an ERE enclosed in parentheses is -followed by the special character question-mark (\fB?\fR), together with that -question-mark it matches what zero or one consecutive occurrences of the ERE -would match. For example, the ERE \fBb?c\fR matches the second character in the -string \fBacabbbcde\fR. -.RE -.RS +4 -.TP -5. +followed by the special character question-mark +.Pq Qq \&? , +together with that question-mark it matches what zero or one consecutive +occurrences of the ERE would match. +For example, the ERE +.Qq b?c +matches the second character in the string +.Qq acabbbcde . +.It When an ERE matching a single character or an ERE enclosed in parentheses is -followed by an \fIinterval\fR \fIexpression\fR of the format {\fIm\fR}, -{\fIm\fR,} or {\fIm\fR,\fIn\fR}, together with that interval expression it -matches what repeated consecutive occurrences of the ERE would match. The -values of \fIm\fR and \fIn\fR will be decimal integers in the range 0 \(<= -\fIm\fR \(<= \fIn\fR \(<= \fB{RE_DUP_MAX}\fR, where \fIm\fR specifies the exact -or minimum number of occurrences and \fIn\fR specifies the maximum number of -occurrences. The expression {\fIm\fR} matches exactly \fIm\fR occurrences of -the preceding ERE, {\fIm\fR,} matches at least \fIm\fR occurrences and -{\fIm\fR,\fIn\fR} matches any number of occurrences between \fIm\fR and -\fIn\fR, inclusive. -.RE -.sp -.LP -For example, in the string \fBabababccccccd\fR the ERE c{3} is matched by -characters seven to nine and the ERE (ab){2,} is matched by characters one to -six. -.sp -.LP -The behavior of multiple adjacent duplication symbols \fB(\fR+\fB,\fR \fB*\fR, -\fB?\fR and intervals) produces undefined results. -.SS "ERE Alternation" -.LP -Two EREs separated by the special character vertical-line (|) match a string -that is matched by either. For example, the ERE a((bc)|d) matches the string -abc and the string ad. Single characters, or expressions matching single -characters, separated by the vertical bar and enclosed in parentheses, will be -treated as an ERE matching a single character. -.SS "ERE Precedence" -.LP +followed by an +.Em interval expression +of the format +.Qq { Ns Em m Ns } , +.Qq { Ns Em m Ns ,} +or +.Qq { Ns Em m Ns , Ns Em n Ns } , +together with that interval expression it matches what repeated consecutive +occurrences of the ERE would match. +The values of +.Em m +and +.Em n +will be decimal integers in the range 0 <= +.Em m +<= +.Em n +<= +.Dv RE_DUP_MAX , +where +.Em m +specifies the exact or minimum number of occurrences and +.Em n +specifies the maximum number of occurrences. +The expression +.Qq { Ns Em m Ns } +matches exactly +.Em m +occurrences of the preceding ERE, +.Qq { Ns Em m Ns ,} +matches at least +.Em m +occurrences and +.Qq { Ns m Ns , Ns Em n Ns } +matches any number of occurrences between +.Em m +and +.Em n , +inclusive. +.El +.Pp +For example, in the string +.Qq abababccccccd +the ERE +.Qq c{3} +is matched by characters seven to nine and the ERE +.Qq (ab){2,} +is matched by characters one to six. +.Pp +The behavior of multiple adjacent duplication symbols +.Po +.Qq + , +.Qq * , +.Qq \&? +and intervals +.Pc +produces undefined results. +.Ss ERE Alternation +Two EREs separated by the special character vertical-line +.Pq Qq | +match a string that is matched by either. +For example, the ERE +.Qq a((bc)|d) +matches the string +.Qq abc +and the string +.Qq ad . +Single characters, or expressions matching single characters, separated by the +vertical bar and enclosed in parentheses, will be treated as an ERE matching a +single character. +.Ss ERE Precedence The order of precedence will be as shown in the following table: -.sp - -.sp -.TS -box; -l l -l l . -\fBERE Precedence (from high to low)\fR -collation-related bracket symbols [= =] [: :] [. .] -escaped characters \e<\fIspecial character\fR> -bracket expression [ ] -grouping ( ) -single-character-ERE duplication * + ? {\fIm\fR,\fIn\fR} -concatenation -anchoring ^ $ -alternation | -.TE - -.sp -.LP -For example, the ERE \fBabba\||\|cde\fR matches either the string \fBabba\fR or -the string \fBcde\fR (rather than the string \fBabbade\fR or \fBabbcde\fR, -because concatenation has a higher order of precedence than alternation). -.SS "ERE Expression Anchoring" -.LP +.Bl -column "ERE Precedence (from high to low)" "" +.It Sy ERE Precedence (from high to low) Ta +.It collation-related bracket symbols Ta [= =] [: :] [. .] +.It escaped characters Ta \e< Ns Em special character Ns > +.It bracket expression Ta \&[ \&] +.It grouping Ta \&( \&) +.It single-character-ERE duplication Ta * + \&? { Ns Em m Ns , Ns Em n Ns} +.It concatenation Ta +.It anchoring Ta ^ $ +.It alternation Ta | +.El +.Pp +For example, the ERE +.Qq abba|cde +matches either the string +.Qq abba +or the string +.Qq cde +.Po rather than the string +.Qq abbade +or +.Qq abbcde , +because concatenation has a higher order of precedence than alternation +.Pc . +.Ss ERE Expression Anchoring An ERE can be limited to matching strings that begin or end a line; this is -called \fIanchoring\fR. The circumflex and dollar sign special characters are -considered ERE anchors when used anywhere outside a bracket expression. This -has the following effects: -.RS +4 -.TP -1. -A circumflex (^) outside a bracket expression anchors the expression or -subexpression it begins to the beginning of a string; such an expression or -subexpression can match only a sequence starting at the first character of a -string. For example, the EREs ^ab and (^ab) match ab in the string abcdef, but -fail to match in the string cdefab, and the ERE a^b is valid, but can never -match because the \fBa\fR prevents the expression ^b from matching starting at -the first character. -.RE -.RS +4 -.TP -2. -A dollar sign \fB(\fR \fB$\fR \fB)\fR outside a bracket expression anchors -the expression or subexpression it ends to the end of a string; such an -expression or subexpression can match only a sequence ending at the last -character of a string. For example, the EREs ef$ and (ef$) match ef in the -string abcdef, but fail to match in the string cdefab, and the ERE e$f is -valid, but can never match because the \fBf\fR prevents the expression e$ from -matching ending at the last character. -.RE -.SH SEE ALSO -.LP -\fBlocaledef\fR(1), \fBregcomp\fR(3C), \fBattributes\fR(5), \fBenviron\fR(5), -\fBlocale\fR(5), \fBregexp\fR(5) +called +.Em anchoring . +The circumflex and dollar sign special characters are considered ERE anchors +when used anywhere outside a bracket expression. +This has the following effects: +.Bl -enum +.It +A circumflex +.Pq Qq ^ +outside a bracket expression anchors the expression or subexpression it begins +to the beginning of a string; such an expression or subexpression can match only +a sequence starting at the first character of a string. +For example, the EREs +.Qq ^ab +and +.Qq (^ab) +match +.Qq ab +in the string +.Qq abcdef , +but fail to match in the string +.Qq cdefab , +and the ERE +.Qq a^b +is valid, but can never match because the +.Qq a +prevents the expression +.Qq ^b +from matching starting at the first character. +.It +A dollar sign +.Pq Qq $ +outside a bracket expression anchors the expression or subexpression it ends to +the end of a string; such an expression or subexpression can match only a +sequence ending at the last character of a string. +For example, the EREs +.Qq ef$ +and +.Qq (ef$) +match +.Qq ef +in the string +.Qq abcdef , +but fail to match in the string +.Qq cdefab , +and the ERE +.Qq e$f +is valid, but can never match because the +.Qq f +prevents the expression +.Qq e$ +from matching ending at the last character. +.El +.Sh SEE ALSO +.Xr localedef 1 , +.Xr regcomp 3C , +.Xr attributes 5 , +.Xr environ 5 , +.Xr locale 5 , +.Xr regexp 5 diff --git a/usr/src/pkg/manifests/system-file-system-zfs.mf b/usr/src/pkg/manifests/system-file-system-zfs.mf index e0474cd0d0..cbe4e4858e 100644 --- a/usr/src/pkg/manifests/system-file-system-zfs.mf +++ b/usr/src/pkg/manifests/system-file-system-zfs.mf @@ -21,7 +21,7 @@ # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright (c) 2012, 2016 by Delphix. All rights reserved. # Copyright (c) 2013 by Saso Kiselkov. All rights reserved. # @@ -135,6 +135,7 @@ $(i386_ONLY)file path=usr/sbin/$(ARCH32)/zdb mode=0555 file path=usr/sbin/$(ARCH64)/zdb mode=0555 file path=usr/sbin/zstreamdump mode=0555 file path=usr/share/man/man1m/zdb.1m +file path=usr/share/man/man1m/zfs-program.1m file path=usr/share/man/man1m/zfs.1m file path=usr/share/man/man1m/zpool.1m file path=usr/share/man/man1m/zstreamdump.1m diff --git a/usr/src/pkg/manifests/system-test-zfstest.mf b/usr/src/pkg/manifests/system-test-zfstest.mf index 62cc59b17a..0be29e52ae 100644 --- a/usr/src/pkg/manifests/system-test-zfstest.mf +++ b/usr/src/pkg/manifests/system-test-zfstest.mf @@ -37,6 +37,9 @@ dir path=opt/zfs-tests/tests/functional/bootfs dir path=opt/zfs-tests/tests/functional/cache dir path=opt/zfs-tests/tests/functional/cachefile dir path=opt/zfs-tests/tests/functional/casenorm +dir path=opt/zfs-tests/tests/functional/channel_program +dir path=opt/zfs-tests/tests/functional/channel_program/lua_core +dir path=opt/zfs-tests/tests/functional/channel_program/synctask_core dir path=opt/zfs-tests/tests/functional/checksum dir path=opt/zfs-tests/tests/functional/clean_mirror dir path=opt/zfs-tests/tests/functional/cli_root @@ -373,6 +376,180 @@ file path=opt/zfs-tests/tests/functional/casenorm/sensitive_none_delete \ file path=opt/zfs-tests/tests/functional/casenorm/sensitive_none_lookup \ mode=0555 file path=opt/zfs-tests/tests/functional/casenorm/setup mode=0555 +file path=opt/zfs-tests/tests/functional/channel_program/channel_common.kshlib \ + mode=0555 +file path=opt/zfs-tests/tests/functional/channel_program/lua_core/cleanup \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/nvlist_to_lua.exe \ + mode=0555 +file path=opt/zfs-tests/tests/functional/channel_program/lua_core/setup \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.out \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.err \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_illegal \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_overflow \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_neg \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_pos \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.out \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.memory_limit \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.nvlist_to_lua \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_neg \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_pos \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_neg \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_pos \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.zcp \ + mode=0444 +file path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.zcp \ + mode=0444 +file path=opt/zfs-tests/tests/functional/channel_program/synctask_core/cleanup \ + mode=0555 +file path=opt/zfs-tests/tests/functional/channel_program/synctask_core/setup \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_count_and_limit \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.out \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_mountpoint \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_neg \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.out \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.out \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_type \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_userquota \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_written \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_children \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_clones \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_snapshots \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_system_props \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.parse_args_neg \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.zcp \ + mode=0444 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple \ + mode=0555 +file \ + path=opt/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple \ + mode=0555 $(i386_ONLY)file path=opt/zfs-tests/tests/functional/checksum/edonr_test.amd64 \ mode=0555 $(i386_ONLY)file path=opt/zfs-tests/tests/functional/checksum/edonr_test.i386 \ @@ -1586,6 +1763,8 @@ file path=opt/zfs-tests/tests/functional/cli_user/misc/zdb_001_neg mode=0555 file path=opt/zfs-tests/tests/functional/cli_user/misc/zfs_001_neg mode=0555 file path=opt/zfs-tests/tests/functional/cli_user/misc/zfs_allow_001_neg \ mode=0555 +file path=opt/zfs-tests/tests/functional/cli_user/misc/zfs_channel_001_neg \ + mode=0555 file path=opt/zfs-tests/tests/functional/cli_user/misc/zfs_clone_001_neg \ mode=0555 file path=opt/zfs-tests/tests/functional/cli_user/misc/zfs_create_001_neg \ diff --git a/usr/src/test/test-runner/cmd/run b/usr/src/test/test-runner/cmd/run index c4c8394262..bc7ce6734e 100644 --- a/usr/src/test/test-runner/cmd/run +++ b/usr/src/test/test-runner/cmd/run @@ -13,7 +13,7 @@ # # Copyright 2017 Joyent, Inc. -# Copyright (c) 2012, 2015 by Delphix. All rights reserved. +# Copyright (c) 2012, 2016 by Delphix. All rights reserved. # import ConfigParser @@ -462,12 +462,8 @@ class TestGroup(Test): return False # If one of the tests is invalid, delete it, log it, and drive on. - for test in self.tests: - if not verify_file(os.path.join(self.pathname, test)): - del self.tests[self.tests.index(test)] - logger.info("Warning: Test '%s' removed from TestGroup '%s' " - "because it failed verification." % - (test, self.pathname)) + self.tests[:] = [f for f in self.tests if + verify_file(os.path.join(self.pathname, f))] return len(self.tests) is not 0 @@ -602,6 +598,22 @@ class TestRun(object): if testgroup.verify(logger): self.testgroups[section] = testgroup + + elif 'autotests' in config.options(section): + testgroup = TestGroup(section) + for prop in TestGroup.props: + for sect in ['DEFAULT', section]: + if config.has_option(sect, prop): + setattr(testgroup, prop, config.get(sect, prop)) + + filenames = os.listdir(section) + # only files starting with "tst." are considered tests + filenames = [f for f in filenames if f.startswith("tst.")] + testgroup.tests = sorted(filenames) + + if testgroup.verify(logger): + self.testgroups[section] = testgroup + else: test = Test(section) for prop in Test.props: diff --git a/usr/src/test/zfs-tests/include/commands.cfg b/usr/src/test/zfs-tests/include/commands.cfg index 462f0e55e3..6d87b7ebeb 100644 --- a/usr/src/test/zfs-tests/include/commands.cfg +++ b/usr/src/test/zfs-tests/include/commands.cfg @@ -102,6 +102,7 @@ export USR_BIN_FILES='awk svcs sync tail + tee timeout touch tr diff --git a/usr/src/test/zfs-tests/include/libtest.shlib b/usr/src/test/zfs-tests/include/libtest.shlib index 3a3c1c466d..f5b9d7c32e 100644 --- a/usr/src/test/zfs-tests/include/libtest.shlib +++ b/usr/src/test/zfs-tests/include/libtest.shlib @@ -195,12 +195,12 @@ function default_container_volume_setup # Create a snapshot on a filesystem or volume. Defaultly create a snapshot on # filesystem # -# $1 Existing filesystem or volume name. Default, $TESTFS +# $1 Existing filesystem or volume name. Default, $TESTPOOL/$TESTFS # $2 snapshot name. Default, $TESTSNAP # function create_snapshot { - typeset fs_vol=${1:-$TESTFS} + typeset fs_vol=${1:-$TESTPOOL/$TESTFS} typeset snap=${2:-$TESTSNAP} [[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined." diff --git a/usr/src/test/zfs-tests/runfiles/delphix.run b/usr/src/test/zfs-tests/runfiles/delphix.run index aaeb65de38..444add0b96 100644 --- a/usr/src/test/zfs-tests/runfiles/delphix.run +++ b/usr/src/test/zfs-tests/runfiles/delphix.run @@ -83,6 +83,12 @@ tests = ['case_all_values', 'norm_all_values', 'mixed_none_lookup', 'mixed_none_lookup_ci', 'mixed_none_delete', 'mixed_formd_lookup', 'mixed_formd_lookup_ci', 'mixed_formd_delete'] +[/opt/zfs-tests/tests/functional/channel_program/lua_core] +autotests = 1 + +[/opt/zfs-tests/tests/functional/channel_program/synctask_core] +autotests = 1 + [/opt/zfs-tests/tests/functional/checksum] tests = ['run_edonr_test', 'run_sha2_test', 'run_skein_test'] pre = @@ -317,6 +323,7 @@ tests = ['zdb_001_neg', 'zfs_001_neg', 'zfs_allow_001_neg', 'zfs_rollback_001_neg', 'zfs_send_001_neg', 'zfs_set_001_neg', 'zfs_share_001_neg', 'zfs_snapshot_001_neg', 'zfs_unallow_001_neg', 'zfs_unmount_001_neg', 'zfs_unshare_001_neg', 'zfs_upgrade_001_neg', + 'zfs_channel_001_neg', 'zpool_001_neg', 'zpool_add_001_neg', 'zpool_attach_001_neg', 'zpool_clear_001_neg', 'zpool_create_001_neg', 'zpool_destroy_001_neg', 'zpool_detach_001_neg', 'zpool_export_001_neg', 'zpool_get_001_neg', diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/Makefile b/usr/src/test/zfs-tests/tests/functional/channel_program/Makefile new file mode 100644 index 0000000000..e6c251094f --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/Makefile @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +.PARALLEL: $(SUBDIRS) + +include $(SRC)/Makefile.master + +ROOTOPTPKG = $(ROOT)/opt/zfs-tests +TESTDIR = $(ROOTOPTPKG)/tests/functional/channel_program + +PROGS = channel_common.kshlib + +CMDS = $(PROGS:%=$(TESTDIR)/%) +$(CMDS) := FILEMODE = 0555 + +all lint clean clobber: + +install: $(CMDS) + +$(CMDS): $(TESTDIR) + +$(TESTDIR): + $(INS.dir) + +$(TESTDIR)/%: % + $(INS.file) + +SUBDIRS = lua_core synctask_core + +include $(SRC)/test/Makefile.com diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/channel_common.kshlib b/usr/src/test/zfs-tests/tests/functional/channel_program/channel_common.kshlib new file mode 100644 index 0000000000..f3097940f9 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/channel_common.kshlib @@ -0,0 +1,100 @@ +#!/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +ZCP_ROOT=$STF_SUITE/tests/functional/channel_program + +# <exitcode> <expected error string> <zfs program args> +# e.g. log_program 0 $POOL foo.zcp arg1 arg2 +function log_program +{ + typeset expectexit=$1 + shift + typeset expecterror=$1 + shift + typeset cmdargs=$@ tmpout=$(mktemp) tmperr=$(mktemp) tmpin=$(mktemp) + + # Expected output/error filename is the same as the .zcp name + typeset basename + if [[ $2 != "-" ]]; then + basename=${2%.*} + fi + + log_note "running: zfs program $cmdargs:" + + tee $tmpin | zfs program $cmdargs >$tmpout 2>$tmperr + typeset ret=$? + + log_note "input:\n$(cat $tmpin)" + log_note "output:\n$(cat $tmpout)" + log_note "error:\n$(cat $tmperr)" + # verify correct return value + if [[ $ret -ne $expectexit ]]; then + log_fail "return mismatch: expected $expectexit, got $ret" + fi + + # + # Check the output or reported error for successful or error returns, + # respectively. + if [[ -f "$basename.out" ]] && [[ $expectexit -eq 0 ]]; then + + outdiff=$(diff "$basename.out" "$tmpout") + [[ $? -ne 0 ]] && log_fail "Output mismatch. Expected:\n" \ + "$(cat $basename.out)\nBut got:$(cat $tmpout)\n" \ + "Diff:\n$outdiff" + + elif [[ -f "$basename.err" ]] && [[ $expectexit -ne 0 ]]; then + + outdiff=$(diff "$basename.err" "$tmperr") + [[ $? -ne 0 ]] && log_fail "Error mismatch. Expected:\n" \ + "$(cat $basename.err)\nBut got:$(cat $tmpout)\n" \ + "Diff:\n$outdiff" + + elif [[ -n $expecterror ]] && [[ $expectexit -ne 0 ]]; then + + grep -q "$expecterror" $tmperr || \ + log_fail "Error mismatch. Expected to contain:\n" \ + "$expecterror\nBut got:$(cat $tmpout)\n" + + elif [[ $expectexit -ne 0 ]]; then + # + # If there's no expected output, error reporting is allowed to + # vary, but ensure that we didn't fail silently. + # + [[ -z "$(cat $tmperr)" ]] && \ + log_fail "error with no stderr output" + fi + + rm $tmpout $tmperr +} + +function log_must_program +{ + log_program 0 "" "$@" +} + +function log_mustnot_program +{ + log_program 1 "" "$@" +} + +function log_mustnot_checkerror_program +{ + typeset expecterror=$1 + shift + log_program 1 "$expecterror" "$@" +} diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/Makefile b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/Makefile new file mode 100644 index 0000000000..21f3ff6c39 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/Makefile @@ -0,0 +1,68 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +include $(SRC)/cmd/Makefile.cmd + +ROOTOPTPKG = $(ROOT)/opt/zfs-tests +TESTDIR = $(ROOTOPTPKG)/tests/functional/channel_program/lua_core + +KSHFILES :sh= ls *.ksh +KSHPROGS = $(KSHFILES:%.ksh=%) +SRCS :sh= ls *.c +CPROGS = $(SRCS:%.c=%.exe) +PROGS = $(KSHPROGS) $(CPROGS) +FILES :sh= ls *.zcp *.out *.err +LDLIBS = $(LDLIBS.cmd) +LDLIBS += -lzfs_core -lnvpair +C99MODE = -xc99=%all + +INSTPROGS = $(PROGS:%=$(TESTDIR)/%) +INSTFILES = $(FILES:%=$(TESTDIR)/%) + +OBJS = $(SRCS:%.c=%.o) + +$(INSTPROGS) := FILEMODE = 0555 +$(INSTFILES) := FILEMODE = 0444 + +all lint: $(CPROGS) + echo SRCS: $(SRCS) + echo PROGS: $(PROGS) + +install: $(INSTPROGS) $(INSTFILES) + +$(INSTPROGS): $(TESTDIR) +$(INSTFILES): $(TESTDIR) + +$(TESTDIR): + $(INS.dir) + +$(TESTDIR)/%: %.ksh + $(INS.rename) + +$(TESTDIR)/%: % + $(INS.file) + +%.o: ../%.c + $(COMPILE.c) $< + +%.exe: %.o + $(LINK.c) $< -o $@ $(LDLIBS) + $(POST_PROCESS) + +clobber: clean + -$(RM) $(PROGS) + +clean: + -$(RM) $(OBJS) diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/cleanup.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/cleanup.ksh new file mode 100644 index 0000000000..f442da2236 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/cleanup.ksh @@ -0,0 +1,19 @@ +#!/usr/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +default_cleanup diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/nvlist_to_lua.c b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/nvlist_to_lua.c new file mode 100644 index 0000000000..c66a7df3c8 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/nvlist_to_lua.c @@ -0,0 +1,304 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <libzfs_core.h> +#include <sys/nvpair.h> + +nvlist_t *nvl; +const char *pool; +boolean_t unexpected_failures; + +static boolean_t +nvlist_equal(nvlist_t *nvla, nvlist_t *nvlb) +{ + if (fnvlist_num_pairs(nvla) != fnvlist_num_pairs(nvlb)) + return (B_FALSE); + /* + * The nvlists have the same number of pairs and keys are unique, so + * if every key in A is also in B and assigned to the same value, the + * lists are identical. + */ + for (nvpair_t *pair = nvlist_next_nvpair(nvla, NULL); + pair != NULL; pair = nvlist_next_nvpair(nvla, pair)) { + char *key = nvpair_name(pair); + + if (!nvlist_exists(nvlb, key)) + return (B_FALSE); + + if (nvpair_type(pair) != + nvpair_type(fnvlist_lookup_nvpair(nvlb, key))) + return (B_FALSE); + + switch (nvpair_type(pair)) { + case DATA_TYPE_BOOLEAN_VALUE: + if (fnvpair_value_boolean_value(pair) != + fnvlist_lookup_boolean_value(nvlb, key)) { + return (B_FALSE); + } + break; + case DATA_TYPE_STRING: + if (strcmp(fnvpair_value_string(pair), + fnvlist_lookup_string(nvlb, key))) { + return (B_FALSE); + } + break; + case DATA_TYPE_INT64: + if (fnvpair_value_int64(pair) != + fnvlist_lookup_int64(nvlb, key)) { + return (B_FALSE); + } + break; + case DATA_TYPE_NVLIST: + if (!nvlist_equal(fnvpair_value_nvlist(pair), + fnvlist_lookup_nvlist(nvlb, key))) { + return (B_FALSE); + } + break; + default: + (void) printf("Unexpected type for nvlist_equal\n"); + return (B_FALSE); + } + } + return (B_TRUE); +} + +static void +test(const char *testname, boolean_t expect_success, boolean_t expect_match) +{ + char *progstr = "input = ...; return {output=input}"; + + nvlist_t *outnvl; + + (void) printf("\nrunning test '%s'; input:\n", testname); + dump_nvlist(nvl, 4); + + int err = lzc_channel_program(pool, progstr, + 10 * 1000 * 1000, 10 * 1024 * 1024, nvl, &outnvl); + + (void) printf("lzc_channel_program returned %u\n", err); + dump_nvlist(outnvl, 5); + + if (err == 0 && expect_match) { + /* + * Verify that outnvl is the same as input nvl, if we expect + * them to be. The input and output will never match if the + * input contains an array (since arrays are converted to lua + * tables), so this is only asserted for some test cases. + */ + nvlist_t *real_outnvl = fnvlist_lookup_nvlist(outnvl, "return"); + real_outnvl = fnvlist_lookup_nvlist(real_outnvl, "output"); + if (!nvlist_equal(nvl, real_outnvl)) { + unexpected_failures = B_TRUE; + (void) printf("unexpected input/output mismatch for " + "case: %s\n", testname); + } + } + if (err != 0 && expect_success) { + unexpected_failures = B_TRUE; + (void) printf("unexpected FAIL of case: %s\n", testname); + } + + fnvlist_free(nvl); + nvl = fnvlist_alloc(); +} + +static void +run_tests(void) +{ + const char *key = "key"; + + /* Note: maximum nvlist key length is 32KB */ + int len = 1024 * 31; + char *bigstring = malloc(len); + for (int i = 0; i < len; i++) + bigstring[i] = 'a' + i % 26; + bigstring[len - 1] = '\0'; + + nvl = fnvlist_alloc(); + + fnvlist_add_boolean(nvl, key); + test("boolean", B_TRUE, B_FALSE); + + fnvlist_add_boolean_value(nvl, key, B_TRUE); + test("boolean_value", B_FALSE, B_FALSE); + + fnvlist_add_byte(nvl, key, 1); + test("byte", B_FALSE, B_FALSE); + + fnvlist_add_int8(nvl, key, 1); + test("int8", B_FALSE, B_FALSE); + + fnvlist_add_uint8(nvl, key, 1); + test("uint8", B_FALSE, B_FALSE); + + fnvlist_add_int16(nvl, key, 1); + test("int16", B_FALSE, B_FALSE); + + fnvlist_add_uint16(nvl, key, 1); + test("uint16", B_FALSE, B_FALSE); + + fnvlist_add_int32(nvl, key, 1); + test("int32", B_FALSE, B_FALSE); + + fnvlist_add_uint32(nvl, key, 1); + test("uint32", B_FALSE, B_FALSE); + + fnvlist_add_int64(nvl, key, 1); + test("int64", B_TRUE, B_TRUE); + + fnvlist_add_uint64(nvl, key, 1); + test("uint64", B_FALSE, B_FALSE); + + fnvlist_add_string(nvl, key, "1"); + test("string", B_TRUE, B_TRUE); + + + { + nvlist_t *val = fnvlist_alloc(); + fnvlist_add_string(val, "subkey", "subvalue"); + fnvlist_add_nvlist(nvl, key, val); + fnvlist_free(val); + test("nvlist", B_TRUE, B_TRUE); + } + { + boolean_t val[2] = { B_FALSE, B_TRUE }; + fnvlist_add_boolean_array(nvl, key, val, 2); + test("boolean_array", B_FALSE, B_FALSE); + } + { + uchar_t val[2] = { 0, 1 }; + fnvlist_add_byte_array(nvl, key, val, 2); + test("byte_array", B_FALSE, B_FALSE); + } + { + int8_t val[2] = { 0, 1 }; + fnvlist_add_int8_array(nvl, key, val, 2); + test("int8_array", B_FALSE, B_FALSE); + } + { + uint8_t val[2] = { 0, 1 }; + fnvlist_add_uint8_array(nvl, key, val, 2); + test("uint8_array", B_FALSE, B_FALSE); + } + { + int16_t val[2] = { 0, 1 }; + fnvlist_add_int16_array(nvl, key, val, 2); + test("int16_array", B_FALSE, B_FALSE); + } + { + uint16_t val[2] = { 0, 1 }; + fnvlist_add_uint16_array(nvl, key, val, 2); + test("uint16_array", B_FALSE, B_FALSE); + } + { + int32_t val[2] = { 0, 1 }; + fnvlist_add_int32_array(nvl, key, val, 2); + test("int32_array", B_FALSE, B_FALSE); + } + { + uint32_t val[2] = { 0, 1 }; + fnvlist_add_uint32_array(nvl, key, val, 2); + test("uint32_array", B_FALSE, B_FALSE); + } + { + int64_t val[2] = { 0, 1 }; + fnvlist_add_int64_array(nvl, key, val, 2); + test("int64_array", B_TRUE, B_FALSE); + } + { + uint64_t val[2] = { 0, 1 }; + fnvlist_add_uint64_array(nvl, key, val, 2); + test("uint64_array", B_FALSE, B_FALSE); + } + { + char *const val[2] = { "0", "1" }; + fnvlist_add_string_array(nvl, key, val, 2); + test("string_array", B_TRUE, B_FALSE); + } + { + nvlist_t *val[2]; + val[0] = fnvlist_alloc(); + fnvlist_add_string(val[0], "subkey", "subvalue"); + val[1] = fnvlist_alloc(); + fnvlist_add_string(val[1], "subkey2", "subvalue2"); + fnvlist_add_nvlist_array(nvl, key, val, 2); + fnvlist_free(val[0]); + fnvlist_free(val[1]); + test("nvlist_array", B_FALSE, B_FALSE); + } + { + fnvlist_add_string(nvl, bigstring, "1"); + test("large_key", B_TRUE, B_TRUE); + } + { + fnvlist_add_string(nvl, key, bigstring); + test("large_value", B_TRUE, B_TRUE); + } + { + for (int i = 0; i < 1024; i++) { + char buf[32]; + (void) snprintf(buf, sizeof (buf), "key-%u", i); + fnvlist_add_int64(nvl, buf, i); + } + test("many_keys", B_TRUE, B_TRUE); + } + { + for (int i = 0; i < 10; i++) { + nvlist_t *newval = fnvlist_alloc(); + fnvlist_add_nvlist(newval, "key", nvl); + fnvlist_free(nvl); + nvl = newval; + } + test("deeply_nested_pos", B_TRUE, B_TRUE); + } + { + for (int i = 0; i < 90; i++) { + nvlist_t *newval = fnvlist_alloc(); + fnvlist_add_nvlist(newval, "key", nvl); + fnvlist_free(nvl); + nvl = newval; + } + test("deeply_nested_neg", B_FALSE, B_FALSE); + } + + free(bigstring); + fnvlist_free(nvl); +} + +int +main(int argc, const char *argv[]) +{ + (void) libzfs_core_init(); + + if (argc != 2) { + (void) printf("usage: %s <pool>\n", + argv[0]); + exit(2); + } + pool = argv[1]; + + run_tests(); + + libzfs_core_fini(); + return (unexpected_failures); +} diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/setup.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/setup.ksh new file mode 100644 index 0000000000..171fd40e41 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/setup.ksh @@ -0,0 +1,21 @@ +#!/usr/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +DISK=${DISKS%% *} + +default_setup ${DISK} diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.ksh new file mode 100644 index 0000000000..6ec5610639 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.ksh @@ -0,0 +1,30 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Passing arguments to lua programs should work correctly. +# + +verify_runnable "global" + +log_assert "Passing arguments to lua programs should work correctly." + +log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.args_to_lua.zcp foo bar + +log_pass "Passing arguments to lua programs should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.out b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.out new file mode 100644 index 0000000000..84191f8f80 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.out @@ -0,0 +1 @@ +Channel program fully executed with no return value. diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.zcp new file mode 100644 index 0000000000..f6f14e5222 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.zcp @@ -0,0 +1,25 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +-- This program should be invoked as "zfs program <pool> <prog> foo bar" + +arg = ... +argv = arg["argv"] + +assert(#argv == 2) +assert(argv[1] == "foo") +assert(argv[2] == "bar") + +return diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.err b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.err new file mode 100644 index 0000000000..fd67d09f73 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.err @@ -0,0 +1,4 @@ +Channel program execution failed: +[string "channel program"]:16: attempt to divide by zero +stack traceback: + [string "channel program"]:16: in main chunk diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.ksh new file mode 100644 index 0000000000..2587c594da --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.ksh @@ -0,0 +1,30 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Divide by zero should fail gracefully. +# + +verify_runnable "global" + +log_assert "Divide by zero should fail gracefully." + +log_mustnot_program $TESTPOOL $ZCP_ROOT/lua_core/tst.divide_by_zero.zcp + +log_pass "Divide by zero should fail gracefully." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.zcp new file mode 100644 index 0000000000..bd882d493b --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.zcp @@ -0,0 +1,16 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +return 1 / 0 diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_illegal.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_illegal.ksh new file mode 100644 index 0000000000..c34f2afd9e --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_illegal.ksh @@ -0,0 +1,41 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Constructing integers that are doubles, too large, or too +# small should fail gracefully. +# + +verify_runnable "global" + +log_assert "constructing illegal integer values should fail gracefully" + +set -A args "1.0" \ + "1.5" \ + "-1.5" + +typeset -i i=0 +while (( i < ${#args[*]} )); do + log_mustnot_checkerror_program "malformed number" $TESTPOOL - <<-EOF + return ${args[i]} + EOF + ((i = i + 1)) +done + +log_pass "constructing illegal integer values should fail gracefully" diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_overflow.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_overflow.ksh new file mode 100644 index 0000000000..c129bae51b --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_overflow.ksh @@ -0,0 +1,32 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Overflowing a 64-bit integer should wrap around. +# + +verify_runnable "global" + +log_assert "overflowing a 64-bit integer should wrap around" + +log_must_program $TESTPOOL - <<-EOF + assert(18446744073709551615 + 1 == (-18446744073709551616)) +EOF + +log_pass "overflowing a 64-bit integer should wrap around" diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_neg.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_neg.ksh new file mode 100644 index 0000000000..0125d76c70 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_neg.ksh @@ -0,0 +1,52 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Try channel programs with various lua runtime issues. +# the program should fail, but the system should not crash. +# Issues include: +# * syntax errors +# * misuse of language constructs (e.g. indexing non-tables) +# * the error() function +# * the assert() function +# + +verify_runnable "global" + +set -A args "{]" \ + "retrn 1" \ + "abc = nil; abc.deref" \ + "abc = nil; abc()" \ + "error(0)" \ + "error(\"string\")" \ + "error(true)" \ + "error({})" \ + "assert(false)" + +log_assert "Runtime errors in lua scripts fail as expected." + +typeset -i i=0 +while (( i < ${#args[*]} )); do + log_mustnot_checkerror_program "execution failed" $TESTPOOL - <<-EOF + ${args[i]} + EOF + ((i = i + 1)) +done + +log_pass "Runtime errors in lua scripts fail as expected." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_pos.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_pos.ksh new file mode 100644 index 0000000000..924d8e2c69 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_pos.ksh @@ -0,0 +1,42 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Try very simple programs that interact with the core lua +# runtime rather than ZFS functions, just to make sure the +# runtime is hooked up correctly. +# + +verify_runnable "global" + +set -A args "" \ + "assert(true)" \ + "x = 1 + 1" + +log_assert "Simple lua scripts pass." + +typeset -i i=0 +while (( i < ${#args[*]} )); do + log_must_program $TESTPOOL - <<-EOF + ${args[i]} + EOF + ((i = i + 1)) +done + +log_pass "Simple lua scripts pass." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.ksh new file mode 100644 index 0000000000..21ab69adb1 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.ksh @@ -0,0 +1,30 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Running a large program file should work correctly. +# + +verify_runnable "global" + +log_assert "Running a large program file should work correctly." + +log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.large_prog.zcp + +log_pass "Running a large program file should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.out b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.out new file mode 100644 index 0000000000..84191f8f80 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.out @@ -0,0 +1 @@ +Channel program fully executed with no return value. diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.zcp new file mode 100644 index 0000000000..add6897334 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.zcp @@ -0,0 +1,280 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +-- /sbin/zfs initially allocates 1KB for the channel program file. This +-- program file is larger than that, to test the reallocation and +-- passing of a large channel string to the kernel. +-- This file is at least 16KB long. + +--[[ +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +1234567890123456789012345678901234567890123456789012345678901234 +]] + +return diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.memory_limit.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.memory_limit.ksh new file mode 100644 index 0000000000..dbac583872 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.memory_limit.ksh @@ -0,0 +1,74 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + + +# +# DESCRIPTION: +# Passing memory limit options to channel programs should work correctly. +# Programs that exceed these limits should fail gracefully. + + +verify_runnable "global" + +log_mustnot_checkerror_program "Memory limit exhausted" \ + -t 100000000 $TESTPOOL - <<-EOF + a = {}; + i = 0; + while true do + i = i + 1 + a[i] = "Here is the " .. i .. "th entry of a" + end; + return a +EOF + +log_assert "memory limit options work" +log_mustnot_checkerror_program "Memory limit exhausted" \ + -m 100000 -t 100000000 $TESTPOOL - <<-EOF + a = {}; + i = 0; + while true do + i = i + 1 + a[i] = "Here is the " .. i .. "th entry of a" + end; + return a +EOF + +log_must_program -m 100000 $TESTPOOL - <<-EOF + s = "teststring" + s = s .. s .. s .. s + return s +EOF + +log_assert "very small memory limits fail correctly" +log_mustnot_checkerror_program "Memory limit exhausted" -m 1 $TESTPOOL - <<-EOF + s = "teststring" + s = s .. s .. s .. s + return s +EOF + +log_mustnot_checkerror_program "Invalid memory limit" \ + -m 1000000000000 $TESTPOOL - <<-EOF + return 1; +EOF + +log_mustnot_checkerror_program "Invalid memory limit" \ + -m 9223372036854775808 $TESTPOOL - <<-EOF + return 1; +EOF + +log_pass "Memory limits work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.ksh new file mode 100644 index 0000000000..9ec8f248d1 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.ksh @@ -0,0 +1,24 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +verify_runnable "global" + +log_mustnot_checkerror_program "too many C levels" \ + $TESTPOOL $ZCP_ROOT/lua_core/tst.nested_neg.zcp + +log_pass "Too many nested lua statements fail cleanly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.zcp new file mode 100644 index 0000000000..c41c714f27 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.zcp @@ -0,0 +1,769 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +-- This program contains deeply nested LUA statements, such that the LUA +-- parser can not process it, given the limited size of the C stack. + +i = 0 +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +return i diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.ksh new file mode 100644 index 0000000000..0c8b8dd8d0 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.ksh @@ -0,0 +1,23 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +verify_runnable "global" + +log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.nested_pos.zcp + +log_pass "Nested lua statements work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.zcp new file mode 100644 index 0000000000..02af95d99d --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.zcp @@ -0,0 +1,71 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +-- This program should work with LUAI_MAXCCALLS=20 + +i = 0 +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +if true then +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end +i = i + 1 +end + +return i diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nvlist_to_lua.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nvlist_to_lua.ksh new file mode 100644 index 0000000000..8575933cc9 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.nvlist_to_lua.ksh @@ -0,0 +1,30 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +verify_runnable "global" + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# run C program which tests passing different nvlists to lua +# + +log_assert "nvlist arguments can be passed to LUA." + +log_must $ZCP_ROOT/lua_core/nvlist_to_lua.exe $TESTPOOL + +log_pass "nvlist arguments can be passed to LUA." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive.zcp new file mode 100644 index 0000000000..9a13c1e7dc --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive.zcp @@ -0,0 +1,31 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +-- Test recursion in LUA. Deep recursion should work because it does +-- not use the C stack. The LUA stack is is allocated from the kernel's +-- heap, up to the specified memory limit. + +arg = ... +argv = arg["argv"] + +function f (x) + if (x == 0) then + return x + end + return f(x-1) + 1 +end + +r = f(argv[1]) +return r diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_neg.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_neg.ksh new file mode 100644 index 0000000000..c75048ae7d --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_neg.ksh @@ -0,0 +1,24 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +verify_runnable "global" + +log_mustnot_checkerror_program "Memory limit exhausted" \ + $TESTPOOL $ZCP_ROOT/lua_core/tst.recursive.zcp 10000000 + +log_pass "Nearly-infinite recursive LUA calls fail cleanly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_pos.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_pos.ksh new file mode 100644 index 0000000000..18d5d0f1ee --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_pos.ksh @@ -0,0 +1,23 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +verify_runnable "global" + +log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.recursive.zcp 10000 + +log_pass "Deeply recursive LUA calls succeed." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_neg.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_neg.ksh new file mode 100644 index 0000000000..b5626e549d --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_neg.ksh @@ -0,0 +1,65 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +verify_runnable "global" + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Try returning various values that lua allows you to construct, +# but that cannot be represented as nvlists and therefore should +# cause the script to fail (but not panic). Try sending the values +# back to userland from both "return" and "error()". +# + +verify_runnable "both" + +set -A args 'function() return 1 end' \ + '{[{}]=true}' \ + '{[function() return 1 end]=0}' \ + 'assert' \ + '0, assert' \ + 'true, {[{}]=0}' \ + '{val=true}, {val=false}' \ + '{1, 2, 3}, {[4]=5}' \ + 'nil, true, 1, "test", {}, {val=true}' \ + '{[false]=true, ["false"]=false}' \ + '{[true]=false, ["true"]=true}' \ + '{[0]=true, ["0"]=false}' \ + '{0,0,0,["1"]=0}' \ + '{0,0,0,["2"]=0}' \ + '{0,0,0,["3"]=0}' + +typeset -i last_index=$((${#args[*]} - 1)) +for i in $(seq 0 $last_index); do + log_note "running program: ${args[i]}" + log_mustnot_checkerror_program "execution failed" $TESTPOOL - <<-EOF + return ${args[i]} + EOF + ((i = i + 1)) +done + +for i in $(seq 0 $last_index); do + log_note "running program: ${args[i]}" + log_mustnot_checkerror_program "execution failed" $TESTPOOL - <<-EOF + error(${args[i]}) + EOF + ((i = i + 1)) +done + +log_pass "Returning lua constructs that cannot be converted to " \ + "nvlists fails as expected." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_pos.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_pos.ksh new file mode 100644 index 0000000000..dce2601132 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_pos.ksh @@ -0,0 +1,59 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +verify_runnable "global" + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Try returning various lua values that should be converted +# to nvlists. Also, try to pass them to error(). +# + +verify_runnable "global" + +set -A args "" \ + "nil" \ + "true" \ + "1" \ + "\"test\"" \ + "{}" \ + "{val=0}" \ + "{{1, {2, 3}, {val1={val2=true}}}, {test=\"yes\"}}" \ + "EINVAL" + +log_assert "Returning valid lua constructs works." + +typeset -i i=0 +while (( i < ${#args[*]} )); do + log_note "running program: return ${args[i]}" + log_must_program $TESTPOOL - <<-EOF + return ${args[i]} + EOF + ((i = i + 1)) +done + +typeset -i i=0 +while (( i < ${#args[*]} )); do + log_note "running program: error(${args[i]})" + log_mustnot_checkerror_program "in function 'error'" $TESTPOOL - <<-EOF + error(${args[i]}) + EOF + ((i = i + 1)) +done + +log_pass "Returning valid lua constructs works." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.ksh new file mode 100644 index 0000000000..78009dbbe5 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.ksh @@ -0,0 +1,33 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +verify_runnable "global" + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Returning a recursive table should fail gracefully +# + +verify_runnable "both" + +log_assert "Returning a recursive table should fail gracefully." + +log_mustnot_checkerror_program "Maximum table depth" \ + $TESTPOOL $ZCP_ROOT/lua_core/tst.return_recursive_table.zcp + +log_pass "Returning a recursive table should fail gracefully." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.zcp new file mode 100644 index 0000000000..21e6c60d8e --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.zcp @@ -0,0 +1,21 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +-- This program returns a self-referential data structure. + +a = {} +a["key"] = "val" +a["self"] = a +return a diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.ksh new file mode 100644 index 0000000000..e735139010 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.ksh @@ -0,0 +1,56 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +verify_runnable "global" + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Passing the instruction limit option to channel programs should work +# correctly. Programs that exceed these instruction limits should fail +# gracefully. +# + +verify_runnable "both" + +log_assert "Timeouts work correctly." + +log_mustnot_checkerror_program "Channel program timed out" \ + $TESTPOOL $ZCP_ROOT/lua_core/tst.timeout.zcp + +function test_instr_limit +{ + typeset lim=$1 + instrs_run=$(dtrace -q \ + -n 'zfs_ioc_channel_program:entry{x=0}' \ + -n 'zfs_ioc_channel_program:return{printf("%d", x)}' \ + -n 'zcp_lua_counthook:entry{x+=100}' \ + -c "zfs program -t $lim $TESTPOOL $ZCP_ROOT/lua_core/tst.timeout.zcp") + if [[ $instrs_run -lt $(( $lim - 100 )) ]]; then + log_fail "Runtime (${instrs_run} instr) < limit (${lim} - 100 instr)" + elif [[ $instrs_run -gt $(( $lim + 100 )) ]]; then + log_fail "Runtime (${instrs_run} instr) > limit (${lim} + 100 instr)" + fi +} + +test_instr_limit 1000 +test_instr_limit 10000 +test_instr_limit 100000 +test_instr_limit 1000000 +test_instr_limit 2000000 + +log_pass "Timeouts work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.zcp new file mode 100644 index 0000000000..d74b1a73c2 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.zcp @@ -0,0 +1,22 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +-- This program runs forever (until timed out). + +i = 1; +while true do + i = i + 1 +end; +return i diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/Makefile b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/Makefile new file mode 100644 index 0000000000..2034d39599 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/Makefile @@ -0,0 +1,45 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +include $(SRC)/Makefile.master + +ROOTOPTPKG = $(ROOT)/opt/zfs-tests +TESTDIR = $(ROOTOPTPKG)/tests/functional/channel_program/synctask_core + +KSHFILES :sh= ls *.ksh +PROGS = $(KSHFILES:.ksh=) +FILES :sh= ls *.zcp *.out *.err 2>/dev/null; true + +INSTPROGS = $(PROGS:%=$(TESTDIR)/%) +INSTFILES = $(FILES:%=$(TESTDIR)/%) + +$(INSTPROGS) := FILEMODE = 0555 +$(INSTFILES) := FILEMODE = 0444 + +all lint clean clobber: + +install: $(INSTPROGS) $(INSTFILES) + +$(INSTPROGS): $(TESTDIR) +$(INSTFILES): $(TESTDIR) + +$(TESTDIR): + $(INS.dir) + +$(TESTDIR)/%: %.ksh + $(INS.rename) + +$(TESTDIR)/%: % + $(INS.file) diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/cleanup.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/cleanup.ksh new file mode 100644 index 0000000000..f442da2236 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/cleanup.ksh @@ -0,0 +1,19 @@ +#!/usr/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +default_cleanup diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/setup.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/setup.ksh new file mode 100644 index 0000000000..171fd40e41 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/setup.ksh @@ -0,0 +1,21 @@ +#!/usr/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +DISK=${DISKS%% *} + +default_setup ${DISK} diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh new file mode 100644 index 0000000000..c9a2e64b7b --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh @@ -0,0 +1,45 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +verify_runnable "global" + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +fs=$TESTPOOL/$TESTFS/testchild + +function cleanup +{ + datasetexists $fs && log_must zfs destroy $fs +} + +log_onexit cleanup + +log_must zfs create $fs +log_must zfs unmount $fs + +log_must datasetexists $fs + +log_must_program $TESTPOOL - $fs <<-EOF + arg = ... + fs = arg["argv"][1] + err = zfs.sync.destroy(fs) + msg = "destroying " .. fs .. " err=" .. err + return msg +EOF + +log_mustnot datasetexists $fs + +log_pass "Destroying filesystem with channel program works." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh new file mode 100644 index 0000000000..d74b47a1dc --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh @@ -0,0 +1,44 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +verify_runnable "global" + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +snap=$TESTPOOL/$TESTFS@$TESTSNAP + +function cleanup +{ + datasetexists $snap && log_must zfs destroy $snap +} + +log_onexit cleanup + +create_snapshot $TESTPOOL/$TESTFS $TESTSNAP + +log_must snapexists $snap + +log_must_program $TESTPOOL - $snap <<-EOF + arg = ... + snap = arg["argv"][1] + err = zfs.sync.destroy(snap) + msg = "destroying " .. snap .. " err=" .. err + return msg +EOF + +log_mustnot snapexists $snap + +log_pass "Destroying snapshot with channel program works." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_count_and_limit.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_count_and_limit.ksh new file mode 100644 index 0000000000..eb559103be --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_count_and_limit.ksh @@ -0,0 +1,89 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib +. $STF_SUITE/tests/functional/zvol/zvol_common.shlib + +# +# DESCRIPTION: +# Getting filesystem and snapshot count/limit props should work correctly. +# + +verify_runnable "global" + +fs=$TESTPOOL/testchild +snap=$fs@$TESTSNAP + +function cleanup +{ + datasetexists $fs && log_must zfs destroy -R $fs + log_must rm -rf $fs/foo + log_must rm -rf $fs/bar +} + +log_onexit cleanup + +log_must zfs create $fs +log_must zfs create $fs/foo +create_snapshot $fs $TESTSNAP + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$fs", "snapshot_limit") + assert(ans == -1) + assert(src == 'default') + + ans, src = zfs.get_prop("$fs", "snapshot_count") + assert(ans == nil) + assert(src == nil) +EOF + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$fs", "filesystem_limit") + assert(ans == -1) + assert(src == 'default') + + ans, src = zfs.get_prop("$fs", "filesystem_count") + assert(ans == nil) + assert(src == nil) +EOF + +log_must zfs set snapshot_limit=10 $fs + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$fs", "snapshot_limit") + assert(ans == 10) + assert(src == '$fs') + + ans, src = zfs.get_prop("$fs", "snapshot_count") + assert(ans == 1) + assert(src == nil) +EOF + +log_must zfs set filesystem_limit=8 $fs + +log_must zfs create $fs/bar + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$fs", "filesystem_limit") + assert(ans == 8) + assert(src == '$fs') + + ans, src = zfs.get_prop("$fs", "filesystem_count") + assert(ans == 2) + assert(src == nil) +EOF + +log_pass "Getting filesystem and snapshot count/limit props should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.ksh new file mode 100644 index 0000000000..5fbe18e48b --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.ksh @@ -0,0 +1,40 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Getting index props should work correctly. +# + +verify_runnable "global" + +fs=$TESTPOOL/$TESTFS/testchild +snap=$fs@$TESTSNAP +function cleanup +{ + datasetexists $fs && log_must zfs destroy -R $fs +} + +log_onexit cleanup + +log_must zfs create -o version=5 $fs +create_snapshot $fs $TESTSNAP + +log_must_program $TESTPOOL $ZCP_ROOT/synctask_core/tst.get_index_props.zcp $fs $snap + +log_pass "Getting index props should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.out b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.out new file mode 100644 index 0000000000..40c58b4b3a --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.out @@ -0,0 +1,4 @@ +Channel program fully executed with return value: + return: + 1: + 2: diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.zcp new file mode 100644 index 0000000000..64105cd9b2 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.zcp @@ -0,0 +1,65 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +arg = ... +fs = arg["argv"][1] +snap = arg["argv"][2] + +props = {} + +-- prop filesystem snapshot +props['redundant_metadata'] = {{'all', 'default'}, {nil, nil}} +props['sync'] = {{'standard', 'default'}, {nil, nil}} +props['checksum'] = {{'on', 'default'}, {nil, nil}} +props['dedup'] = {{'off', 'default'}, {nil, nil}} +props['compression'] = {{'off', 'default'}, {nil, nil}} +props['snapdir'] = {{'hidden', 'default'}, {nil, nil}} +props['aclmode'] = {{'discard', 'default'}, {nil, nil}} +props['aclinherit'] = {{'restricted','default'}, {nil, nil}} +props['copies'] = {{'1', 'default'}, {nil, nil}} +props['primarycache'] = {{'all', 'default'}, {'all', 'default'}} +props['secondarycache'] = {{'all', 'default'}, {'all', 'default'}} +props['logbias'] = {{'latency', 'default'}, {nil, nil}} +props['atime'] = {{'on', 'default'}, {nil, nil}} +props['devices'] = {{'on', 'default'}, {'on', 'default'}} +props['exec'] = {{'on', 'default'}, {'on', 'default'}} +props['setuid'] = {{'on', 'default'}, {'on', 'default'}} +props['readonly'] = {{'off', 'default'}, {nil, nil}} +props['zoned'] = {{'off', 'default'}, {nil, nil}} +props['vscan'] = {{'off', 'default'}, {nil, nil}} +props['nbmand'] = {{'off', 'default'}, {'off', 'default'}} +props['version'] = {{'5', nil}, {'5', nil}} +props['canmount'] = {{'on', 'default'}, {nil, nil}} +props['mounted'] = {{nil, nil}, {nil, nil}} +props['defer_destroy'] = {{nil, nil}, {'off', nil}} +props['normalization'] = {{'none', nil}, {'none', nil}} +props['casesensitivity'] = {{'sensitive', nil}, {'sensitive', nil}} +props['utf8only'] = {{'off', nil}, {'off', nil}} + +fs_fails = {} +snap_fails = {} +for prop, expected in pairs(props) do + ans, src = zfs.get_prop(fs, prop) + if ((ans ~= expected[1][1]) or (src ~= expected[1][2])) then + fs_fails[prop] = {ans, src} + end + + ans, src = zfs.get_prop(snap, prop) + if ((ans ~= expected[2][1]) or (src ~= expected[2][2])) then + snap_fails[prop] = {ans, src} + end +end + +return {fs_fails, snap_fails} diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_mountpoint.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_mountpoint.ksh new file mode 100644 index 0000000000..77555a7e03 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_mountpoint.ksh @@ -0,0 +1,88 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Getting dataset mountpoint should work correctly. +# + +verify_runnable "global" + +fs=$TESTPOOL/testmount +snap=$fs@$TESTSNAP +clone=$TESTPOOL/$TESTCLONE +mnt1=/$fs/mnt1 +mnt2=/$fs/mnt2 + +function cleanup +{ + datasetexists $clone && log_must zfs destroy $clone + datasetexists $fs && log_must zfs destroy -R $fs + log_must rm -rf $mnt1 + log_must rm -rf $mnt2 +} + +log_onexit cleanup + +log_must zfs create $fs +create_snapshot $fs $TESTSNAP +create_clone $snap $clone + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$fs", "mountpoint") + assert(ans == '/$fs') + assert(src == 'default') +EOF + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$snap", "mountpoint") + assert(ans == nil) + assert(src == nil) +EOF + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$clone", "mountpoint") + assert(ans == '/$clone') + assert(src == 'default') +EOF + +log_must mkdir $mnt1 +log_must mkdir $mnt2 + +log_must zfs set mountpoint=$mnt1 $fs +log_must zfs set mountpoint=$mnt2 $clone + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$fs", "mountpoint") + assert(ans == '$mnt1') + assert(src == '$fs') +EOF + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$snap", "mountpoint") + assert(ans == nil) + assert(src == nil) +EOF + +log_must_program $TESTPOOL - <<-EOF + ans, src = zfs.get_prop("$clone", "mountpoint") + assert(ans == '$mnt2') + assert(src == '$clone') +EOF + +log_pass "Getting dataset mountpoint should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_neg.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_neg.ksh new file mode 100644 index 0000000000..b1a7b8ac82 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_neg.ksh @@ -0,0 +1,43 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Getting failures should work correctly. +# + +verify_runnable "global" +fs=$TESTPOOL/$TESTFS/testchild +function cleanup +{ + datasetexists $fs && log_must zfs destroy $fs +} + +log_onexit cleanup + +log_must zfs create $fs + +log_mustnot_program $TESTPOOL - <<-EOF + ans, setpoint = zfs.get_prop("$fs", "notaprop") +EOF + +log_mustnot_program $TESTPOOL - <<-EOF + ans, setpoint = zfs.get_prop("notadataset", "type") +EOF + +log_pass "Getting failures should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.ksh new file mode 100644 index 0000000000..a78873114e --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.ksh @@ -0,0 +1,52 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Getting number props should work correctly on filesystems, +# snapshots and volumes. +# + +verify_runnable "global" + +fs=$TESTPOOL/$TESTFS/testchild +snap=$fs@$TESTSNAP +vol=$TESTPOOL/$TESTVOL + +function cleanup +{ + datasetexists $fs && log_must zfs destroy -R $fs + datasetexists $vol && log_must zfs destroy $vol +} + +log_onexit cleanup + +log_must zfs create $fs +create_snapshot $fs $TESTSNAP +log_must zfs create -V $VOLSIZE $TESTPOOL/$TESTVOL + +# +# Set snapshot_limit and filesystem_limit for the filesystem so that the +# snapshot_count and filesystem_count properties return a value. +# +log_must zfs set snapshot_limit=10 filesystem_limit=10 $fs +log_must zfs set snapshot_limit=10 $vol + +log_must_program $TESTPOOL $ZCP_ROOT/synctask_core/tst.get_number_props.zcp $fs $snap $vol + +log_pass "Getting number props should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.out b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.out new file mode 100644 index 0000000000..be68500097 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.out @@ -0,0 +1,5 @@ +Channel program fully executed with return value: + return: + 1: + 2: + 3: diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.zcp new file mode 100644 index 0000000000..6bf23bbb89 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.zcp @@ -0,0 +1,100 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +arg = ... +fs = arg["argv"][1] +snap = arg["argv"][2] +vol = arg["argv"][3] + +props = {} + +-- The values indicate whether or not a property should be returned, +-- not the value of the property. A better approach might be to compare +-- their values to the output of 'zfs get <prop>' + +-- prop filesystem snapshot volume +props['used'] = {{true, nil}, {true, nil}, {true, nil}} +props['available'] = {{true, nil}, {nil, nil}, {true, nil}} +props['referenced'] = {{true, nil}, {true, nil}, {true, nil}} +props['compressratio'] = {{true, nil}, {true, nil}, {true, nil}} +props['refcompressratio'] = {{true, nil}, {true, nil}, {true, nil}} +props['volblocksize'] = {{nil, nil}, {nil, nil}, {true, nil}} +props['usedbysnapshots'] = {{true, nil}, {nil, nil}, {true, nil}} +props['usedbydataset'] = {{true, nil}, {nil, nil}, {true, nil}} +props['usedbychildren'] = {{true, nil}, {nil, nil}, {true, nil}} +props['usedbyrefreservation'] = {{true, nil}, {nil, nil}, {true, nil}} +props['userrefs'] = {{nil, nil}, {true, nil}, {nil, nil}} +props['written'] = {{true, nil}, {true, nil}, {true, nil}} +props['logicalused'] = {{true, nil}, {nil, nil}, {true, nil}} +props['logicalreferenced'] = {{true, nil}, {true, nil}, {true, nil}} +props['quota'] = {{true, 'default'}, {nil, nil}, {nil, nil}} +props['reservation'] = {{true, 'default'}, {nil, nil}, {true, 'default'}} +props['volsize'] = {{nil, nil}, {nil, nil}, {true, vol}} +props['refquota'] = {{true, 'default'}, {nil, nil}, {nil, nil}} +props['refreservation'] = {{true, 'default'}, {nil, nil}, {true, vol}} +props['filesystem_limit'] = {{true, fs}, {nil, nil}, {nil, nil}} +props['snapshot_limit'] = {{true, fs}, {nil, nil}, {true, vol}} +props['filesystem_count'] = {{true, nil}, {nil, nil}, {nil, nil}} +props['snapshot_count'] = {{true, nil}, {nil, nil}, {true, nil}} +props['recordsize'] = {{true, 'default'}, {nil, nil}, {nil, nil}} +props['creation'] = {{true, nil}, {true, nil}, {true, nil}} +-- hidden props +props['createtxg'] = {{true, nil}, {true, nil}, {true, nil}} +props['numclones'] = {{nil, nil}, {true, nil}, {nil, nil}} +props['guid'] = {{true, nil}, {true, nil}, {true, nil}} +props['useraccounting'] = {{true, nil}, {true, nil}, {true, nil}} +props['unique'] = {{true, nil}, {true, nil}, {true, nil}} +props['objsetid'] = {{true, nil}, {true, nil}, {true, nil}} +props['inconsistent'] = {{true, nil}, {true, nil}, {true, nil}} + + +fs_fails = {} +snap_fails = {} +vol_fails = {} + +function match(n, ans, src, expected) + if ((expected[n][1] == nil) and (ans ~= nil)) then + return false + end + + if ((expected[n][1] == true) and (ans == nil)) then + return false + end + + if (expected[n][2] ~= src) then + return false + end + + return true +end + +for prop, expected in pairs(props) do + ans, src = zfs.get_prop(fs, prop) + if not (match(1, ans, src, expected)) then + fs_fails[prop] = {ans, src} + end + + ans, src = zfs.get_prop(snap, prop) + if not (match(2, ans, src, expected)) then + snap_fails[prop] = {ans, src} + end + + ans, src = zfs.get_prop(vol, prop) + if not (match(3, ans, src, expected)) then + vol_fails[prop] = {ans, src} + end +end + +return {fs_fails, snap_fails, vol_fails} diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.ksh new file mode 100644 index 0000000000..b7d784489a --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.ksh @@ -0,0 +1,45 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Getting string props should work correctly. +# + +verify_runnable "global" + +fs=$TESTPOOL/testchild +snap=$fs@$TESTSNAP +clone=$TESTPOOL/$TESTCLONE + + +function cleanup +{ + datasetexists $clone && log_must zfs destroy $clone + datasetexists $fs && log_must zfs destroy -R $fs +} + +log_onexit cleanup + +log_must zfs create $fs +create_snapshot $fs $TESTSNAP +create_clone $snap $clone + +log_must_program $TESTPOOL $ZCP_ROOT/synctask_core/tst.get_string_props.zcp $fs $snap $clone + +log_pass "Getting string props should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.out b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.out new file mode 100644 index 0000000000..be68500097 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.out @@ -0,0 +1,5 @@ +Channel program fully executed with return value: + return: + 1: + 2: + 3: diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.zcp new file mode 100644 index 0000000000..899bdc0336 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.zcp @@ -0,0 +1,73 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +arg = ... +fs = arg["argv"][1] +snap = arg["argv"][2] +clone = arg["argv"][3] + +props = {} + +-- prop filesystem snapshot clone +props['origin'] = {{nil, nil}, {nil, nil}, {snap, nil}} +props['clones'] = {{nil, nil}, {{clone}, nil}, {nil, nil}} +props['mountpoint'] = {{'/' .. fs, 'default'}, {nil, nil}, {'/' .. clone, 'default'}} +props['sharenfs'] = {{'off', 'default'}, {nil, nil}, {'off', 'default'}} +props['type'] = {{'filesystem', nil}, {'snapshot', nil}, {'filesystem', nil}} +props['sharesmb'] = {{'off', 'default'}, {nil, nil}, {'off', 'default'}} +props['mlslabel'] = {{'none', 'default'}, {'none', 'default'}, {'none', 'default'}} +props['receive_resume_token'] = {{nil, nil}, {nil, nil}, {nil, nil}} +-- hidden props +props['name'] = {{fs, nil}, {snap, nil}, {clone, nil}} +props['iscsioptions'] = {{nil, nil}, {nil, nil}, {nil, nil}} +props['prevsnap'] = {{snap, nil}, {nil, nil}, {snap, nil}} + + +fs_fails = {} +snap_fails = {} +clone_fails = {} + + +function list_match(t1, t2) + if t1 == t2 then return true end + return (t1[1] == t2[1]) +end + +function match(n, prop, ans, src, expected) + if ((prop == 'clones') or (prop == 'redact_snaps')) then + return (list_match(ans, expected[n][1]) and (src == expected[n][2])) + else + return ((ans == expected[n][1]) and (src == expected[n][2])) + end +end + +for prop, expected in pairs(props) do + ans, src = zfs.get_prop(fs, prop) + if not (match(1, prop, ans, src, expected)) then + fs_fails[prop] = {ans, src} + end + + ans, src = zfs.get_prop(snap, prop) + if not (match(2, prop, ans, src, expected)) then + snap_fails[prop] = {ans, src} + end + + ans, src = zfs.get_prop(clone, prop) + if not (match(3, prop, ans, src, expected)) then + clone_fails[prop] = {ans, src} + end +end + +return {fs_fails, snap_fails, clone_fails} diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_type.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_type.ksh new file mode 100644 index 0000000000..f52eda75a0 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_type.ksh @@ -0,0 +1,54 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib +. $STF_SUITE/tests/functional/zvol/zvol_common.shlib + +# +# DESCRIPTION: +# Getting type should work correctly. +# + +verify_runnable "global" +fs=$TESTPOOL/$TESTFS/testchild +snap=$fs@$TESTSNAP +vol=$TESTPOOL/$TESTVOL +function cleanup +{ + datasetexists $snap && log_must zfs destroy $snap + datasetexists $fs && log_must zfs destroy $fs + datasetexists $vol && log_must zfs destroy $vol +} + +log_onexit cleanup + +log_must zfs create $fs +create_snapshot $fs $TESTSNAP +log_must zfs create -V $VOLSIZE $vol + +log_must_program $TESTPOOL - <<-EOF + ans, setpoint = zfs.get_prop("$fs", "type") + assert(ans == "filesystem") + + ans, setpoint = zfs.get_prop("$snap", "type") + assert(ans == "snapshot") + + ans, setpoint = zfs.get_prop("$vol", "type") + assert(ans == "volume") +EOF + + +log_pass "Getting type should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_userquota.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_userquota.ksh new file mode 100644 index 0000000000..a101544203 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_userquota.ksh @@ -0,0 +1,80 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib +. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib + +# +# DESCRIPTION: +# "Getting {user,group}{quota,used}, should work correctly." +# + +verify_runnable "global" + +fs=$TESTPOOL/$TESTFS/testchild +fs1=$TESTPOOL/$TESTFS/nextchild +userid='123' +groupid='456' + +function cleanup +{ + datasetexists $fs && log_must zfs destroy $fs + datasetexists $fs1 && log_must zfs destroy $fs1 +} + +log_onexit cleanup + +log_must zfs create -o userquota@$userid=$UQUOTA_SIZE \ + -o groupquota@$groupid=$GQUOTA_SIZE $fs + +log_must_program $TESTPOOL - <<-EOF + ans, setpoint = zfs.get_prop("$fs", "userquota@$userid") + assert(ans == $UQUOTA_SIZE) + assert(setpoint == "$fs") + + ans, setpoint = zfs.get_prop("$fs", "userused@$userid") + assert(ans == 0) + assert(setpoint == "$fs") + + ans, setpoint = zfs.get_prop("$fs", "groupquota@$groupid") + assert(ans == $GQUOTA_SIZE) + assert(setpoint == "$fs") + + ans, setpoint = zfs.get_prop("$fs", "groupused@$groupid") + assert(ans == 0) + assert(setpoint == "$fs") +EOF + +log_must zfs create $fs1 +log_must_program $TESTPOOL - <<-EOF + ans, setpoint = zfs.get_prop("$fs1", "userquota@$userid") + assert(ans == nil) + assert(setpoint == nil) + + ans, setpoint = zfs.get_prop("$fs1", "userused@$userid") + assert(ans == 0) + assert(setpoint == "$fs1") + + ans, setpoint = zfs.get_prop("$fs1", "groupquota@$groupid") + assert(ans == nil) + assert(setpoint == nil) + + ans, setpoint = zfs.get_prop("$fs1", "groupused@$groupid") + assert(ans == 0) + assert(setpoint == "$fs1") +EOF + +log_pass "Getting {user,group}{quota,used}, should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_written.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_written.ksh new file mode 100644 index 0000000000..7f94aea246 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_written.ksh @@ -0,0 +1,57 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib +. $STF_SUITE/tests/functional/zvol/zvol_common.shlib + +# +# DESCRIPTION: +# Getting written@ props should work correctly. +# + +verify_runnable "global" + +fs=$TESTPOOL/testchild +snap=$fs@$TESTSNAP +dir=/$fs/dir + +function cleanup +{ + datasetexists $fs && log_must zfs destroy -R $fs + log_must rm -rf $dir +} + +log_onexit cleanup + +log_must zfs create $fs +create_snapshot $fs $TESTSNAP + +log_must_program $TESTPOOL - <<-EOF + ans, setpoint = zfs.get_prop("$fs", "written@$TESTSNAP") + assert(ans == 0) + +EOF + +log_must mkdir $dir +sync + +log_must_program $TESTPOOL - <<-EOF + ans, setpoint = zfs.get_prop("$fs", "written@$TESTSNAP") + assert(ans > 0) + +EOF + +log_pass "Getting written@ props should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_children.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_children.ksh new file mode 100644 index 0000000000..2afd2e47d9 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_children.ksh @@ -0,0 +1,125 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Listing zfs children should work correctly. +# + +verify_runnable "global" + +log_assert "Listing zfs children should work correctly." + +TESTCHILD=$TESTPOOL/$TESTFS/testchild +TESTCHILD1=$TESTCHILD-1 +TESTCHILD2=$TESTCHILD-2 +TESTCHILD3=$TESTCHILD-3 + +function cleanup +{ + datasetexists $TESTCHILD && log_must zfs destroy $TESTCHILD + datasetexists $TESTCHILD1 && log_must zfs destroy $TESTCHILD1 + datasetexists $TESTCHILD2 && log_must zfs destroy $TESTCHILD2 + datasetexists $TESTCHILD3 && log_must zfs destroy $TESTCHILD3 +} + +log_onexit cleanup + +# 0 children handled correctly +log_must_program $TESTPOOL - <<-EOF + n = 0 + for s in zfs.list.children("$TESTPOOL/$TESTFS") do + n = n + 1 + end + assert(n == 0) + return 0 +EOF + +log_must_program $TESTPOOL - <<-EOF + n = 0 + for s in zfs.list.children("$TESTPOOL") do + assert(s == "$TESTPOOL/$TESTFS") + n = n + 1 + end + assert(n == 1) + return 0 +EOF + +# Create a child fs +log_must zfs create $TESTCHILD + +log_must_program $TESTPOOL - <<-EOF + n = 0 + for s in zfs.list.children("$TESTPOOL/$TESTFS") do + assert(s == "$TESTCHILD") + n = n + 1 + end + assert(n == 1) + return 0 +EOF + +log_must zfs create $TESTCHILD1 +log_must zfs create $TESTCHILD2 +log_must zfs create $TESTCHILD3 + +# All children appear exactly once +log_must_program $TESTPOOL - <<-EOF + a = {} + a["$TESTCHILD"] = false + a["$TESTCHILD1"] = false + a["$TESTCHILD2"] = false + a["$TESTCHILD3"] = false + n = 0 + for s in zfs.list.children("$TESTPOOL/$TESTFS") do + assert(not a[s]) + a[s] = true + n = n + 1 + end + assert(n == 4) + assert(a["$TESTCHILD"] and + a["$TESTCHILD1"] and + a["$TESTCHILD2"] and + a["$TESTCHILD3"]) + return 0 +EOF + +# Bad input +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.children("$TESTPOOL/not-a-fs") + return 0 +EOF + +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.children("not-a-pool/$TESTFS") + return 0 +EOF + +# Can't look in a different pool than the one specified on command line +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.children("rpool") + return 0 +EOF + +create_snapshot +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.children("$TESTPOOL/$TESTFS@$TESTSNAP") + return 0 +EOF +destroy_snapshot + +log_pass "Listing zfs children should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_clones.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_clones.ksh new file mode 100644 index 0000000000..ef2bd5ac0f --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_clones.ksh @@ -0,0 +1,117 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Listing zfs clones should work correctly. +# + +verify_runnable "global" + +log_assert "Listing zfs clones should work correctly." + +function cleanup +{ + datasetexists $TESTPOOL/$TESTFS@$TESTSNAP && \ + log_must zfs destroy -R $TESTPOOL/$TESTFS@$TESTSNAP +} + +log_onexit cleanup + +# Take snapshot to test with ($TESTSNAP) +create_snapshot + +# 0 clones handled correctly +log_must_program $TESTPOOL - <<-EOF + n = 0 + for s in zfs.list.clones("$TESTPOOL/$TESTFS@$TESTSNAP") do + n = n + 1 + end + assert(n == 0) + return 0 +EOF + +# Create a clone ($TESTCLONE) +create_clone + +log_must_program $TESTPOOL - <<-EOF + n = 0 + for s in zfs.list.clones("$TESTPOOL/$TESTFS@$TESTSNAP") do + assert(s == "$TESTPOOL/$TESTCLONE") + n = n + 1 + end + assert(n == 1) + return 0 +EOF + +TESTCLONE1=${TESTCLONE}-1 +TESTCLONE2=${TESTCLONE}-2 +TESTCLONE3=${TESTCLONE}-3 +create_clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE1 +create_clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE2 +create_clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE3 + +# All clones appear exactly once +log_must_program $TESTPOOL - <<-EOF + a = {} + a["$TESTPOOL/$TESTCLONE"] = false + a["$TESTPOOL/$TESTCLONE1"] = false + a["$TESTPOOL/$TESTCLONE2"] = false + a["$TESTPOOL/$TESTCLONE3"] = false + n = 0 + for s in zfs.list.clones("$TESTPOOL/$TESTFS@$TESTSNAP") do + assert(not a[s]) + a[s] = true + n = n + 1 + end + assert(n == 4) + assert(a["$TESTPOOL/$TESTCLONE"] and + a["$TESTPOOL/$TESTCLONE1"] and + a["$TESTPOOL/$TESTCLONE2"] and + a["$TESTPOOL/$TESTCLONE3"]) + return 0 +EOF + +# Bad input +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.clones("$TESTPOOL/not-a-fs@$TESTSNAP") + return 0 +EOF + +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.clones("$TESTPOOL/$TESTFS@not-a-snap") + return 0 +EOF + +# Can't look in a different pool than the one specified on command line +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.clones("rpool") + return 0 +EOF + +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.clones("not-a-pool/$TESTFS@$TESTSNAP") + return 0 +EOF + +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.clones("$TESTPOOL/$TESTFS") + return 0 +EOF + +log_pass "Listing zfs clones should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_snapshots.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_snapshots.ksh new file mode 100644 index 0000000000..8cd78c513a --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_snapshots.ksh @@ -0,0 +1,116 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Listing zfs snapshots should work correctly. +# + +verify_runnable "global" + +log_assert "Listing zfs snapshots should work correctly." + +function cleanup +{ + datasetexists $TESTPOOL/$TESTFS@$TESTSNAP && \ + log_must zfs destroy $TESTPOOL/$TESTFS@$TESTSNAP + datasetexists $TESTPOOL/$TESTFS@$TESTSNAP1 && \ + log_must zfs destroy $TESTPOOL/$TESTFS@$TESTSNAP1 + datasetexists $TESTPOOL/$TESTFS@$TESTSNAP2 && \ + log_must zfs destroy $TESTPOOL/$TESTFS@$TESTSNAP2 + datasetexists $TESTPOOL/$TESTFS@$TESTSNAP3 && \ + log_must zfs destroy $TESTPOOL/$TESTFS@$TESTSNAP3 +} + +log_onexit cleanup + +# 0 snapshots handled correctly +log_must_program $TESTPOOL - <<-EOF + n = 0 + for s in zfs.list.snapshots("$TESTPOOL/$TESTFS") do + zfs.debug("ERROR: found snapshot " .. s) + n = n + 1 + end + assert(n == 0) + return 0 +EOF + +# Take a snapshot, make sure it appears +create_snapshot $TESTPOOL/$TESTFS $TESTSNAP + +log_must_program $TESTPOOL - <<-EOF + n = 0 + for s in zfs.list.snapshots("$TESTPOOL/$TESTFS") do + assert(s == "$TESTPOOL/$TESTFS@$TESTSNAP") + n = n + 1 + end + assert(n == 1) + return 0 +EOF + +TESTSNAP1=${TESTSNAP}-1 +TESTSNAP2=${TESTSNAP}-2 +TESTSNAP3=${TESTSNAP}-3 +create_snapshot $TESTPOOL/$TESTFS $TESTSNAP1 +create_snapshot $TESTPOOL/$TESTFS $TESTSNAP2 +create_snapshot $TESTPOOL/$TESTFS $TESTSNAP3 + +# All snapshots appear exactly once +log_must_program $TESTPOOL - <<-EOF + a = {} + a["$TESTPOOL/$TESTFS@$TESTSNAP"] = false + a["$TESTPOOL/$TESTFS@$TESTSNAP1"] = false + a["$TESTPOOL/$TESTFS@$TESTSNAP2"] = false + a["$TESTPOOL/$TESTFS@$TESTSNAP3"] = false + n = 0 + for s in zfs.list.snapshots("$TESTPOOL/$TESTFS") do + assert(not a[s]) + a[s] = true + n = n + 1 + end + assert(n == 4) + assert(a["$TESTPOOL/$TESTFS@$TESTSNAP"] and + a["$TESTPOOL/$TESTFS@$TESTSNAP1"] and + a["$TESTPOOL/$TESTFS@$TESTSNAP2"] and + a["$TESTPOOL/$TESTFS@$TESTSNAP3"]) + return 0 +EOF + +# Bad input +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.snapshots("$TESTPOOL/not-a-fs") + return 0 +EOF + +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.snapshots("not-a-pool/$TESTFS") + return 0 +EOF + +# Can't look in a different pool than the one specified on command line +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.snapshots("rpool") + return 0 +EOF + +log_mustnot_program $TESTPOOL - <<-EOF + zfs.list.snapshots("$TESTPOOL/${TESTFS}@$TESTSNAP") + return 0 +EOF + +log_pass "Listing zfs snapshots should work correctly." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_system_props.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_system_props.ksh new file mode 100644 index 0000000000..cc0be6d908 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_system_props.ksh @@ -0,0 +1,54 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib +. $STF_SUITE/tests/functional/zvol/zvol_common.shlib + +# +# DESCRIPTION: +# Listing system properties should succeed +# + +verify_runnable "global" +fs=$TESTPOOL/$TESTFS/testchild +snap=$fs@$TESTSNAP +vol=$TESTPOOL/$TESTVOL +function cleanup +{ + datasetexists $snap && log_must zfs destroy $snap + datasetexists $fs && log_must zfs destroy $fs + datasetexists $vol && log_must zfs destroy $vol +} + +log_onexit cleanup + +log_must zfs create $fs +create_snapshot $fs $TESTSNAP +log_must zfs create -V $VOLSIZE $vol + +log_must_program $TESTPOOL - <<-EOF + zfs.list.system_properties("$fs") +EOF + +log_must_program $TESTPOOL - <<-EOF + zfs.list.system_properties("$snap") +EOF + +log_must_program $TESTPOOL - <<-EOF + zfs.list.system_properties("$vol") +EOF + +log_pass "Listing system properties should succeed." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.parse_args_neg.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.parse_args_neg.ksh new file mode 100644 index 0000000000..8c57bef4b0 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.parse_args_neg.ksh @@ -0,0 +1,48 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Try calling zfs.sync.destroy with various arguments that are not +# valid. The script should fail. +# + +verify_runnable "global" + +set -A progs "zfs.sync.destroy(\"foo\", \"bar\")" \ + "zfs.sync.destroy(\"foo\", 12345)" \ + "zfs.sync.destroy(12345)" \ + "zfs.sync.destroy()" \ + "zfs.sync.destroy{\"foo\", bar=true}" \ + "zfs.sync.destroy{\"foo\", defer=12345}" \ + "zfs.sync.destroy{\"foo\", defer=true, 12345}" \ + "zfs.sync.destroy{\"foo\", defer=true, bar=12345}" \ + "zfs.sync.destroy{\"foo\", bar=true, defer=true}" \ + "zfs.sync.destroy{defer=true}" \ + "zfs.sync.destroy{12345, defer=true}" + + +typeset -i i=0 +while (( i < ${#progs[*]} )); do + log_note "running program: ${progs[i]}" + # output should contain the usage message, which starts with "destroy{" + echo ${progs[i]} | log_mustnot_checkerror_program "destroy{" $TESTPOOL - + ((i = i + 1)) +done + +log_pass "Invalid arguments to zfs.sync.destroy generate errors." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh new file mode 100644 index 0000000000..d5d377b771 --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh @@ -0,0 +1,55 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Attempting to promote a clone when it shares a snapshot name with +# its parent filesystem should fail and return the name of the +# conflicting snapshot. +# + +verify_runnable "global" + +fs=$TESTPOOL/$TESTFS/testchild +clone=$TESTPOOL/$TESTFS/testchild_clone +snap=promote_conflict_snap + +function cleanup +{ + for to_destroy in $fs $clone; do + datasetexists $to_destroy && log_must zfs destroy -R $to_destroy + done +} + +log_onexit cleanup + +log_must zfs create $fs +log_must zfs snapshot $fs@$snap +log_must zfs clone $fs@$snap $clone +log_must zfs snapshot $clone@$snap + +# +# This channel program is expected to return successfully, but fail to execute +# the promote command since the snapshot names collide. It returns the error +# code and description, which should be EEXIST (17) and the name of the +# conflicting snapshot. +# +log_must_program $TESTPOOL \ + $ZCP_ROOT/synctask_core/tst.promote_conflict.zcp $clone + +log_pass "Promoting a clone with a conflicting snapshot fails." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.zcp b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.zcp new file mode 100644 index 0000000000..287328a40d --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.zcp @@ -0,0 +1,23 @@ +-- +-- This file and its contents are supplied under the terms of the +-- Common Development and Distribution License ("CDDL"), version 1.0. +-- You may only use this file in accordance with the terms of version +-- 1.0 of the CDDL. +-- +-- A full copy of the text of the CDDL should have accompanied this +-- source. A copy of the CDDL is also available via the Internet at +-- http://www.illumos.org/license/CDDL. +-- + +-- +-- Copyright (c) 2016 by Delphix. All rights reserved. +-- + +-- This program should be invoked as "zfs program <pool> <prog> <clone>" + +args = ... +argv = args["argv"] +err, desc = zfs.sync.promote(argv[1]) +assert(err == EEXIST) +assert(#desc == 1) +assert(desc[1] == "promote_conflict_snap") diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh new file mode 100644 index 0000000000..13ed119ece --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh @@ -0,0 +1,71 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Multiple interacting promotions in a single txg should succeed. +# + +verify_runnable "global" + +fs=$TESTPOOL/$TESTFS/testchild +clone1=$TESTPOOL/$TESTFS/testchild_clone1 +clone2=$TESTPOOL/$TESTFS/testchild_clone2 +snap1=$fs@testchild_snap1 +snap2=$clone1@testchild_snap2 + +function cleanup +{ + for to_destroy in $fs $clone1 $clone2; do + datasetexists $to_destroy && log_must zfs destroy -R $to_destroy + done +} + +log_onexit cleanup + +# +# Create a chain of clones and snapshots: +# +# snap1 -----------> fs +# \--> snap2 --> clone1 +# \---> clone2 +# +# Then promote clone2 twice, resulting in: +# +# snap1 --> snap2 --> clone2 +# \ \---> clone1 +# \------------> fs +# +# We then attempt to destroy clone1, which should succeed since it no +# longer has any dependents. +# +log_must zfs create $fs +log_must zfs snapshot $snap1 +log_must zfs clone $snap1 $clone1 +log_must zfs snapshot $snap2 +log_must zfs clone $snap2 $clone2 + +log_must zfs unmount -f $clone1 + +log_must_program $TESTPOOL - <<-EOF + assert(zfs.sync.promote("$clone2") == 0) + assert(zfs.sync.promote("$clone2") == 0) + assert(zfs.sync.destroy("$clone1") == 0) +EOF + +log_pass "Multiple promotes and destroying a demoted fs in one txg works." diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh new file mode 100644 index 0000000000..4d1ac5974a --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh @@ -0,0 +1,47 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib + +# +# DESCRIPTION: +# Promoting a clone should work correctly. +# + +verify_runnable "global" + +fs=$TESTPOOL/$TESTFS/testchild +clone=$TESTPOOL/$TESTFS/testchild_clone +snap=$fs@$TESTSNAP + +function cleanup +{ + for to_destroy in $fs $clone; do + datasetexists $to_destroy && log_must zfs destroy -R $to_destroy + done +} + +log_onexit cleanup + +log_must zfs create $fs +log_must zfs snapshot $snap +log_must zfs clone $snap $clone + +log_must_program $TESTPOOL - <<-EOF + assert(zfs.sync.promote("$clone") == 0) +EOF + +log_pass "Promoting a clone with a channel program works." diff --git a/usr/src/test/zfs-tests/tests/functional/cli_user/misc/zfs_channel_001_neg.ksh b/usr/src/test/zfs-tests/tests/functional/cli_user/misc/zfs_channel_001_neg.ksh new file mode 100644 index 0000000000..242860502b --- /dev/null +++ b/usr/src/test/zfs-tests/tests/functional/cli_user/misc/zfs_channel_001_neg.ksh @@ -0,0 +1,45 @@ +#!/usr/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 (c) 2016 by Delphix. All rights reserved. +# + +. $STF_SUITE/tests/functional/cli_user/misc/misc.cfg +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# +# zfs program returns an error when run as a user +# +# STRATEGY: +# 1. Attempt to start a channel program on a pool +# 2. Verify the command fails +# + +verify_runnable "global" + + +log_mustnot eval "echo return 0 | zfs program $TESTPOOL -" + +log_pass "zfs program returns an error when run as a user" diff --git a/usr/src/tools/quick/make-zfs b/usr/src/tools/quick/make-zfs index 681b152640..57f20f0251 100755 --- a/usr/src/tools/quick/make-zfs +++ b/usr/src/tools/quick/make-zfs @@ -256,12 +256,14 @@ do_mans() { (cd $SRC/man/man1m && make \ $ROOT/usr/share/man/man1m/zdb.1m \ $ROOT/usr/share/man/man1m/zfs.1m \ + $ROOT/usr/share/man/man1m/zfs-program.1m \ $ROOT/usr/share/man/man1m/zpool.1m ) (cd $SRC/man/man5 && make \ $ROOT/usr/share/man/man5/zpool-features.5 ) ;; lint) - (cd $SRC/man/man1m && make zdb.1m.check zfs.1m.check zpool.1m.check) + (cd $SRC/man/man1m && make zdb.1m.check zfs.1m.check zfs-program.1m.check \ + zpool.1m.check) (cd $SRC/man/man5 && make zpool-features.5.check) ;; *) @@ -339,6 +341,7 @@ usr/sbin/$arch64/zinject usr/sbin/zstreamdump usr/share/man/man1m/zdb.1m usr/share/man/man1m/zfs.1m +usr/share/man/man1m/zfs-program.1m usr/share/man/man1m/zpool.1m usr/share/man/man5/zpool-features.5 " diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 79cc7292d9..6cccc919d7 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1377,6 +1377,35 @@ SMBFS_OBJS += smbfs_vfsops.o smbfs_vnops.o smbfs_node.o \ BOOTFS_OBJS += bootfs_construct.o bootfs_vfsops.o bootfs_vnops.o +LUA_OBJS += \ + ldo.o \ + lvm.o \ + lbitlib.o \ + lopcodes.o \ + lstring.o \ + ltable.o \ + ltm.o \ + lcorolib.o \ + lauxlib.o \ + ldebug.o \ + lstate.o \ + lgc.o \ + lmem.o \ + lctype.o \ + lfunc.o \ + ldump.o \ + lundump.o \ + lstrlib.o \ + ltablib.o \ + lapi.o \ + lobject.o \ + lbaselib.o \ + lcompat.o \ + lzio.o \ + lcode.o \ + llex.o \ + lparser.o + ZFS_COMMON_OBJS += \ abd.o \ arc.o \ @@ -1444,6 +1473,11 @@ ZFS_COMMON_OBJS += \ zap.o \ zap_leaf.o \ zap_micro.o \ + zcp.o \ + zcp_get.o \ + zcp_global.o \ + zcp_iter.o \ + zcp_synctask.o \ zfs_byteswap.o \ zfs_debug.o \ zfs_fm.o \ @@ -2042,7 +2076,7 @@ IXGBE_INTC_OBJS = ixgbe_82598.o ixgbe_82599.o ixgbe_api.o ixgbe_common.o \ # illumos-written ones. I40E_OBJS = i40e_main.o i40e_osdep.o i40e_intr.o i40e_transceiver.o \ - i40e_stats.o i40e_gld.o + i40e_stats.o i40e_gld.o # Intel-written ones. I40E_INTC_OBJS = i40e_adminq.o i40e_common.o i40e_hmc.o i40e_lan_hmc.o \ i40e_nvm.o diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index b861833afa..6b8106c9b9 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -25,6 +25,7 @@ # Copyright 2013 Saso Kiselkov. All rights reserved. # Copyright 2016 Joyent, Inc. # Copyright 2016 Nexenta Systems, Inc. +# Copyright (c) 2016 by Delphix. All rights reserved. # # @@ -362,6 +363,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/zfs/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/zfs/lua/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/zut/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -388,7 +393,7 @@ $(OBJS_DIR)/%.o: $(COMMONBASE)/fsreparse/%.c KMECHKRB5_BASE=$(UTSBASE)/common/gssapi/mechs/krb5 -KGSSDFLAGS=-I $(UTSBASE)/common/gssapi/include +KGSSDFLAGS=-I $(UTSBASE)/common/gssapi/include # Note, KRB5_DEFS can be assigned various preprocessor flags, # typically -D defines on the make invocation. The standard compiler @@ -1943,6 +1948,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/vscan/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/zfs/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/zfs/lua/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/zut/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/fs/zfs/dsl_dataset.c b/usr/src/uts/common/fs/zfs/dsl_dataset.c index 90f4956e81..cf2f290f47 100644 --- a/usr/src/uts/common/fs/zfs/dsl_dataset.c +++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c @@ -1616,7 +1616,6 @@ dsl_dataset_snapshot_tmp(const char *fsname, const char *snapname, return (error); } - void dsl_dataset_sync(dsl_dataset_t *ds, zio_t *zio, dmu_tx_t *tx) { @@ -1684,30 +1683,17 @@ dsl_dataset_sync_done(dsl_dataset_t *ds, dmu_tx_t *tx) dmu_buf_rele(ds->ds_dbuf, ds); } -static void -get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv) +int +get_clones_stat_impl(dsl_dataset_t *ds, nvlist_t *val) { uint64_t count = 0; objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; zap_cursor_t zc; zap_attribute_t za; - nvlist_t *propval = fnvlist_alloc(); - nvlist_t *val; ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); /* - * We use nvlist_alloc() instead of fnvlist_alloc() because the - * latter would allocate the list with NV_UNIQUE_NAME flag. - * As a result, every time a clone name is appended to the list - * it would be (linearly) searched for for a duplicate name. - * We already know that all clone names must be unique and we - * want avoid the quadratic complexity of double-checking that - * because we can have a large number of clones. - */ - VERIFY0(nvlist_alloc(&val, 0, KM_SLEEP)); - - /* * There may be missing entries in ds_next_clones_obj * due to a bug in a previous version of the code. * Only trust it if it has the right number of entries. @@ -1716,8 +1702,9 @@ get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv) VERIFY0(zap_count(mos, dsl_dataset_phys(ds)->ds_next_clones_obj, &count)); } - if (count != dsl_dataset_phys(ds)->ds_num_children - 1) - goto fail; + if (count != dsl_dataset_phys(ds)->ds_num_children - 1) { + return (ENOENT); + } for (zap_cursor_init(&zc, mos, dsl_dataset_phys(ds)->ds_next_clones_obj); zap_cursor_retrieve(&zc, &za) == 0; @@ -1731,15 +1718,42 @@ get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv) dsl_dataset_rele(clone, FTAG); } zap_cursor_fini(&zc); - fnvlist_add_nvlist(propval, ZPROP_VALUE, val); - fnvlist_add_nvlist(nv, zfs_prop_to_name(ZFS_PROP_CLONES), propval); -fail: - nvlist_free(val); - nvlist_free(propval); + return (0); } -static void -get_receive_resume_stats(dsl_dataset_t *ds, nvlist_t *nv) +void +get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv) +{ + nvlist_t *propval = fnvlist_alloc(); + nvlist_t *val; + + /* + * We use nvlist_alloc() instead of fnvlist_alloc() because the + * latter would allocate the list with NV_UNIQUE_NAME flag. + * As a result, every time a clone name is appended to the list + * it would be (linearly) searched for for a duplicate name. + * We already know that all clone names must be unique and we + * want avoid the quadratic complexity of double-checking that + * because we can have a large number of clones. + */ + VERIFY0(nvlist_alloc(&val, 0, KM_SLEEP)); + + if (get_clones_stat_impl(ds, val) == 0) { + fnvlist_add_nvlist(propval, ZPROP_VALUE, val); + fnvlist_add_nvlist(nv, zfs_prop_to_name(ZFS_PROP_CLONES), + propval); + } else { + nvlist_free(val); + nvlist_free(propval); + } +} + +/* + * Returns a string that represents the receive resume stats token. It should + * be freed with strfree(). + */ +char * +get_receive_resume_stats_impl(dsl_dataset_t *ds) { dsl_pool_t *dp = ds->ds_dir->dd_pool; @@ -1807,84 +1821,359 @@ get_receive_resume_stats(dsl_dataset_t *ds, nvlist_t *nv) ZFS_SEND_RESUME_TOKEN_VERSION, (longlong_t)cksum.zc_word[0], (longlong_t)packed_size, str); - dsl_prop_nvlist_add_string(nv, - ZFS_PROP_RECEIVE_RESUME_TOKEN, propval); kmem_free(packed, packed_size); kmem_free(str, compressed_size * 2 + 1); kmem_free(compressed, packed_size); - strfree(propval); + return (propval); + } + return (strdup("")); +} + +/* + * Returns a string that represents the receive resume stats token of the + * dataset's child. It should be freed with strfree(). + */ +char * +get_child_receive_stats(dsl_dataset_t *ds) +{ + char recvname[ZFS_MAX_DATASET_NAME_LEN + 6]; + dsl_dataset_t *recv_ds; + dsl_dataset_name(ds, recvname); + if (strlcat(recvname, "/", sizeof (recvname)) < + sizeof (recvname) && + strlcat(recvname, recv_clone_name, sizeof (recvname)) < + sizeof (recvname) && + dsl_dataset_hold(ds->ds_dir->dd_pool, recvname, FTAG, + &recv_ds) == 0) { + char *propval = get_receive_resume_stats_impl(recv_ds); + dsl_dataset_rele(recv_ds, FTAG); + return (propval); } + return (strdup("")); +} + +static void +get_receive_resume_stats(dsl_dataset_t *ds, nvlist_t *nv) +{ + char *propval = get_receive_resume_stats_impl(ds); + if (strcmp(propval, "") != 0) { + dsl_prop_nvlist_add_string(nv, + ZFS_PROP_RECEIVE_RESUME_TOKEN, propval); + } else { + char *childval = get_child_receive_stats(ds); + if (strcmp(childval, "") != 0) { + dsl_prop_nvlist_add_string(nv, + ZFS_PROP_RECEIVE_RESUME_TOKEN, childval); + } + strfree(childval); + } + strfree(propval); +} + +uint64_t +dsl_get_refratio(dsl_dataset_t *ds) +{ + uint64_t ratio = dsl_dataset_phys(ds)->ds_compressed_bytes == 0 ? 100 : + (dsl_dataset_phys(ds)->ds_uncompressed_bytes * 100 / + dsl_dataset_phys(ds)->ds_compressed_bytes); + return (ratio); +} + +uint64_t +dsl_get_logicalreferenced(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_uncompressed_bytes); +} + +uint64_t +dsl_get_compressratio(dsl_dataset_t *ds) +{ + if (ds->ds_is_snapshot) { + return (dsl_get_refratio(ds)); + } else { + dsl_dir_t *dd = ds->ds_dir; + mutex_enter(&dd->dd_lock); + uint64_t val = dsl_dir_get_compressratio(dd); + mutex_exit(&dd->dd_lock); + return (val); + } +} + +uint64_t +dsl_get_used(dsl_dataset_t *ds) +{ + if (ds->ds_is_snapshot) { + return (dsl_dataset_phys(ds)->ds_unique_bytes); + } else { + dsl_dir_t *dd = ds->ds_dir; + mutex_enter(&dd->dd_lock); + uint64_t val = dsl_dir_get_used(dd); + mutex_exit(&dd->dd_lock); + return (val); + } +} + +uint64_t +dsl_get_creation(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_creation_time); +} + +uint64_t +dsl_get_creationtxg(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_creation_txg); +} + +uint64_t +dsl_get_refquota(dsl_dataset_t *ds) +{ + return (ds->ds_quota); +} + +uint64_t +dsl_get_refreservation(dsl_dataset_t *ds) +{ + return (ds->ds_reserved); +} + +uint64_t +dsl_get_guid(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_guid); +} + +uint64_t +dsl_get_unique(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_unique_bytes); +} + +uint64_t +dsl_get_objsetid(dsl_dataset_t *ds) +{ + return (ds->ds_object); +} + +uint64_t +dsl_get_userrefs(dsl_dataset_t *ds) +{ + return (ds->ds_userrefs); +} + +uint64_t +dsl_get_defer_destroy(dsl_dataset_t *ds) +{ + return (DS_IS_DEFER_DESTROY(ds) ? 1 : 0); +} + +uint64_t +dsl_get_referenced(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_referenced_bytes); +} + +uint64_t +dsl_get_numclones(dsl_dataset_t *ds) +{ + ASSERT(ds->ds_is_snapshot); + return (dsl_dataset_phys(ds)->ds_num_children - 1); +} + +uint64_t +dsl_get_inconsistent(dsl_dataset_t *ds) +{ + return ((dsl_dataset_phys(ds)->ds_flags & DS_FLAG_INCONSISTENT) ? + 1 : 0); +} + +uint64_t +dsl_get_available(dsl_dataset_t *ds) +{ + uint64_t refdbytes = dsl_get_referenced(ds); + uint64_t availbytes = dsl_dir_space_available(ds->ds_dir, + NULL, 0, TRUE); + if (ds->ds_reserved > dsl_dataset_phys(ds)->ds_unique_bytes) { + availbytes += + ds->ds_reserved - dsl_dataset_phys(ds)->ds_unique_bytes; + } + if (ds->ds_quota != 0) { + /* + * Adjust available bytes according to refquota + */ + if (refdbytes < ds->ds_quota) { + availbytes = MIN(availbytes, + ds->ds_quota - refdbytes); + } else { + availbytes = 0; + } + } + return (availbytes); +} + +int +dsl_get_written(dsl_dataset_t *ds, uint64_t *written) +{ + dsl_pool_t *dp = ds->ds_dir->dd_pool; + dsl_dataset_t *prev; + int err = dsl_dataset_hold_obj(dp, + dsl_dataset_phys(ds)->ds_prev_snap_obj, FTAG, &prev); + if (err == 0) { + uint64_t comp, uncomp; + err = dsl_dataset_space_written(prev, ds, written, + &comp, &uncomp); + dsl_dataset_rele(prev, FTAG); + } + return (err); +} + +/* + * 'snap' should be a buffer of size ZFS_MAX_DATASET_NAME_LEN. + */ +int +dsl_get_prev_snap(dsl_dataset_t *ds, char *snap) +{ + dsl_pool_t *dp = ds->ds_dir->dd_pool; + if (ds->ds_prev != NULL && ds->ds_prev != dp->dp_origin_snap) { + dsl_dataset_name(ds->ds_prev, snap); + return (0); + } else { + return (ENOENT); + } +} + +/* + * Returns the mountpoint property and source for the given dataset in the value + * and source buffers. The value buffer must be at least as large as MAXPATHLEN + * and the source buffer as least as large a ZFS_MAX_DATASET_NAME_LEN. + * Returns 0 on success and an error on failure. + */ +int +dsl_get_mountpoint(dsl_dataset_t *ds, const char *dsname, char *value, + char *source) +{ + int error; + dsl_pool_t *dp = ds->ds_dir->dd_pool; + + /* Retrieve the mountpoint value stored in the zap opbject */ + error = dsl_prop_get_ds(ds, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1, + ZAP_MAXVALUELEN, value, source); + if (error != 0) { + return (error); + } + + /* Process the dsname and source to find the full mountpoint string */ + if (value[0] == '/') { + char *buf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); + char *root = buf; + const char *relpath; + + /* + * If we inherit the mountpoint, even from a dataset + * with a received value, the source will be the path of + * the dataset we inherit from. If source is + * ZPROP_SOURCE_VAL_RECVD, the received value is not + * inherited. + */ + if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { + relpath = ""; + } else { + ASSERT0(strncmp(dsname, source, strlen(source))); + relpath = dsname + strlen(source); + if (relpath[0] == '/') + relpath++; + } + + spa_altroot(dp->dp_spa, root, ZAP_MAXVALUELEN); + + /* + * Special case an alternate root of '/'. This will + * avoid having multiple leading slashes in the + * mountpoint path. + */ + if (strcmp(root, "/") == 0) + root++; + + /* + * If the mountpoint is '/' then skip over this + * if we are obtaining either an alternate root or + * an inherited mountpoint. + */ + char *mnt = value; + if (value[1] == '\0' && (root[0] != '\0' || + relpath[0] != '\0')) + mnt = value + 1; + + if (relpath[0] == '\0') { + (void) snprintf(value, ZAP_MAXVALUELEN, "%s%s", + root, mnt); + } else { + (void) snprintf(value, ZAP_MAXVALUELEN, "%s%s%s%s", + root, mnt, relpath[0] == '@' ? "" : "/", + relpath); + } + kmem_free(buf, ZAP_MAXVALUELEN); + } else { + /* 'legacy' or 'none' */ + (void) snprintf(value, ZAP_MAXVALUELEN, "%s", value); + } + return (0); } void dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv) { dsl_pool_t *dp = ds->ds_dir->dd_pool; - uint64_t refd, avail, uobjs, aobjs, ratio; ASSERT(dsl_pool_config_held(dp)); - ratio = dsl_dataset_phys(ds)->ds_compressed_bytes == 0 ? 100 : - (dsl_dataset_phys(ds)->ds_uncompressed_bytes * 100 / - dsl_dataset_phys(ds)->ds_compressed_bytes); - - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRATIO, ratio); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRATIO, + dsl_get_refratio(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_LOGICALREFERENCED, - dsl_dataset_phys(ds)->ds_uncompressed_bytes); + dsl_get_logicalreferenced(ds)); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, + dsl_get_compressratio(ds)); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED, + dsl_get_used(ds)); if (ds->ds_is_snapshot) { - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, ratio); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED, - dsl_dataset_phys(ds)->ds_unique_bytes); get_clones_stat(ds, nv); } else { - if (ds->ds_prev != NULL && ds->ds_prev != dp->dp_origin_snap) { - char buf[ZFS_MAX_DATASET_NAME_LEN]; - dsl_dataset_name(ds->ds_prev, buf); - dsl_prop_nvlist_add_string(nv, ZFS_PROP_PREV_SNAP, buf); - } - + char buf[ZFS_MAX_DATASET_NAME_LEN]; + if (dsl_get_prev_snap(ds, buf) == 0) + dsl_prop_nvlist_add_string(nv, ZFS_PROP_PREV_SNAP, + buf); dsl_dir_stats(ds->ds_dir, nv); } - dsl_dataset_space(ds, &refd, &avail, &uobjs, &aobjs); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_AVAILABLE, avail); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFERENCED, refd); - + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_AVAILABLE, + dsl_get_available(ds)); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFERENCED, + dsl_get_referenced(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_CREATION, - dsl_dataset_phys(ds)->ds_creation_time); + dsl_get_creation(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_CREATETXG, - dsl_dataset_phys(ds)->ds_creation_txg); + dsl_get_creationtxg(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFQUOTA, - ds->ds_quota); + dsl_get_refquota(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRESERVATION, - ds->ds_reserved); + dsl_get_refreservation(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_GUID, - dsl_dataset_phys(ds)->ds_guid); + dsl_get_guid(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_UNIQUE, - dsl_dataset_phys(ds)->ds_unique_bytes); + dsl_get_unique(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_OBJSETID, - ds->ds_object); + dsl_get_objsetid(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USERREFS, - ds->ds_userrefs); + dsl_get_userrefs(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_DEFER_DESTROY, - DS_IS_DEFER_DESTROY(ds) ? 1 : 0); + dsl_get_defer_destroy(ds)); if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) { - uint64_t written, comp, uncomp; - dsl_pool_t *dp = ds->ds_dir->dd_pool; - dsl_dataset_t *prev; - - int err = dsl_dataset_hold_obj(dp, - dsl_dataset_phys(ds)->ds_prev_snap_obj, FTAG, &prev); - if (err == 0) { - err = dsl_dataset_space_written(prev, ds, &written, - &comp, &uncomp); - dsl_dataset_rele(prev, FTAG); - if (err == 0) { - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_WRITTEN, - written); - } + uint64_t written; + if (dsl_get_written(ds, &written) == 0) { + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_WRITTEN, + written); } } @@ -1921,27 +2210,19 @@ dsl_dataset_fast_stat(dsl_dataset_t *ds, dmu_objset_stats_t *stat) dsl_pool_t *dp = ds->ds_dir->dd_pool; ASSERT(dsl_pool_config_held(dp)); - stat->dds_creation_txg = dsl_dataset_phys(ds)->ds_creation_txg; - stat->dds_inconsistent = - dsl_dataset_phys(ds)->ds_flags & DS_FLAG_INCONSISTENT; - stat->dds_guid = dsl_dataset_phys(ds)->ds_guid; + stat->dds_creation_txg = dsl_get_creationtxg(ds); + stat->dds_inconsistent = dsl_get_inconsistent(ds); + stat->dds_guid = dsl_get_guid(ds); stat->dds_origin[0] = '\0'; if (ds->ds_is_snapshot) { stat->dds_is_snapshot = B_TRUE; - stat->dds_num_clones = - dsl_dataset_phys(ds)->ds_num_children - 1; + stat->dds_num_clones = dsl_get_numclones(ds); } else { stat->dds_is_snapshot = B_FALSE; stat->dds_num_clones = 0; if (dsl_dir_is_clone(ds->ds_dir)) { - dsl_dataset_t *ods; - - VERIFY0(dsl_dataset_hold_obj(dp, - dsl_dir_phys(ds->ds_dir)->dd_origin_obj, - FTAG, &ods)); - dsl_dataset_name(ods, stat->dds_origin); - dsl_dataset_rele(ods, FTAG); + dsl_dir_get_origin(ds->ds_dir, stat->dds_origin); } } } @@ -2333,22 +2614,12 @@ struct promotenode { dsl_dataset_t *ds; }; -typedef struct dsl_dataset_promote_arg { - const char *ddpa_clonename; - dsl_dataset_t *ddpa_clone; - list_t shared_snaps, origin_snaps, clone_snaps; - dsl_dataset_t *origin_origin; /* origin of the origin */ - uint64_t used, comp, uncomp, unique, cloneusedsnap, originusedsnap; - char *err_ds; - cred_t *cr; -} dsl_dataset_promote_arg_t; - static int snaplist_space(list_t *l, uint64_t mintxg, uint64_t *spacep); static int promote_hold(dsl_dataset_promote_arg_t *ddpa, dsl_pool_t *dp, void *tag); static void promote_rele(dsl_dataset_promote_arg_t *ddpa, void *tag); -static int +int dsl_dataset_promote_check(void *arg, dmu_tx_t *tx) { dsl_dataset_promote_arg_t *ddpa = arg; @@ -2360,14 +2631,19 @@ dsl_dataset_promote_check(void *arg, dmu_tx_t *tx) uint64_t unused; uint64_t ss_mv_cnt; size_t max_snap_len; + boolean_t conflicting_snaps; err = promote_hold(ddpa, dp, FTAG); if (err != 0) return (err); hds = ddpa->ddpa_clone; + snap = list_head(&ddpa->shared_snaps); + origin_ds = snap->ds; max_snap_len = MAXNAMELEN - strlen(ddpa->ddpa_clonename) - 1; + snap = list_head(&ddpa->origin_snaps); + if (dsl_dataset_phys(hds)->ds_flags & DS_FLAG_NOPROMOTE) { promote_rele(ddpa, FTAG); return (SET_ERROR(EXDEV)); @@ -2382,9 +2658,6 @@ dsl_dataset_promote_check(void *arg, dmu_tx_t *tx) return (0); } - snap = list_head(&ddpa->shared_snaps); - origin_ds = snap->ds; - /* compute origin's new unique space */ snap = list_tail(&ddpa->clone_snaps); ASSERT3U(dsl_dataset_phys(snap->ds)->ds_prev_snap_obj, ==, @@ -2408,6 +2681,7 @@ dsl_dataset_promote_check(void *arg, dmu_tx_t *tx) * Note however, if we stop before we reach the ORIGIN we get: * uN + kN + kN-1 + ... + kM - uM-1 */ + conflicting_snaps = B_FALSE; ss_mv_cnt = 0; ddpa->used = dsl_dataset_phys(origin_ds)->ds_referenced_bytes; ddpa->comp = dsl_dataset_phys(origin_ds)->ds_compressed_bytes; @@ -2436,12 +2710,12 @@ dsl_dataset_promote_check(void *arg, dmu_tx_t *tx) } err = dsl_dataset_snap_lookup(hds, ds->ds_snapname, &val); if (err == 0) { - (void) strcpy(ddpa->err_ds, snap->ds->ds_snapname); - err = SET_ERROR(EEXIST); + fnvlist_add_boolean(ddpa->err_ds, + snap->ds->ds_snapname); + conflicting_snaps = B_TRUE; + } else if (err != ENOENT) { goto out; } - if (err != ENOENT) - goto out; /* The very first snapshot does not have a deadlist */ if (dsl_dataset_phys(ds)->ds_prev_snap_obj == 0) @@ -2455,6 +2729,15 @@ dsl_dataset_promote_check(void *arg, dmu_tx_t *tx) } /* + * In order to return the full list of conflicting snapshots, we check + * whether there was a conflict after traversing all of them. + */ + if (conflicting_snaps) { + err = SET_ERROR(EEXIST); + goto out; + } + + /* * If we are a clone of a clone then we never reached ORIGIN, * so we need to subtract out the clone origin's used space. */ @@ -2516,7 +2799,7 @@ out: return (err); } -static void +void dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx) { dsl_dataset_promote_arg_t *ddpa = arg; @@ -2841,6 +3124,7 @@ dsl_dataset_promote(const char *name, char *conflsnap) dsl_dataset_promote_arg_t ddpa = { 0 }; uint64_t numsnaps; int error; + nvpair_t *snap_pair; objset_t *os; /* @@ -2858,12 +3142,22 @@ dsl_dataset_promote(const char *name, char *conflsnap) return (error); ddpa.ddpa_clonename = name; - ddpa.err_ds = conflsnap; + ddpa.err_ds = fnvlist_alloc(); ddpa.cr = CRED(); - return (dsl_sync_task(name, dsl_dataset_promote_check, + error = dsl_sync_task(name, dsl_dataset_promote_check, dsl_dataset_promote_sync, &ddpa, - 2 + numsnaps, ZFS_SPACE_CHECK_RESERVED)); + 2 + numsnaps, ZFS_SPACE_CHECK_RESERVED); + + /* + * Return the first conflicting snapshot found. + */ + snap_pair = nvlist_next_nvpair(ddpa.err_ds, NULL); + if (snap_pair != NULL && conflsnap != NULL) + (void) strcpy(conflsnap, nvpair_name(snap_pair)); + + fnvlist_free(ddpa.err_ds); + return (error); } int diff --git a/usr/src/uts/common/fs/zfs/dsl_destroy.c b/usr/src/uts/common/fs/zfs/dsl_destroy.c index 09fa5406ec..b632070e39 100644 --- a/usr/src/uts/common/fs/zfs/dsl_destroy.c +++ b/usr/src/uts/common/fs/zfs/dsl_destroy.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2015 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2013 by Joyent, Inc. All rights reserved. * Copyright (c) 2014 Integros [integros.com] @@ -30,6 +30,7 @@ #include <sys/dsl_userhold.h> #include <sys/dsl_dataset.h> #include <sys/dsl_synctask.h> +#include <sys/dsl_destroy.h> #include <sys/dmu_tx.h> #include <sys/dsl_pool.h> #include <sys/dsl_dir.h> @@ -41,13 +42,7 @@ #include <sys/zfs_ioctl.h> #include <sys/dsl_deleg.h> #include <sys/dmu_impl.h> - -typedef struct dmu_snapshots_destroy_arg { - nvlist_t *dsda_snaps; - nvlist_t *dsda_successful_snaps; - boolean_t dsda_defer; - nvlist_t *dsda_errlist; -} dmu_snapshots_destroy_arg_t; +#include <sys/zcp.h> int dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer) @@ -85,51 +80,33 @@ dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer) return (0); } -static int +int dsl_destroy_snapshot_check(void *arg, dmu_tx_t *tx) { - dmu_snapshots_destroy_arg_t *dsda = arg; + dsl_destroy_snapshot_arg_t *ddsa = arg; + const char *dsname = ddsa->ddsa_name; + boolean_t defer = ddsa->ddsa_defer; + dsl_pool_t *dp = dmu_tx_pool(tx); - nvpair_t *pair; int error = 0; + dsl_dataset_t *ds; - if (!dmu_tx_is_syncing(tx)) - return (0); - - for (pair = nvlist_next_nvpair(dsda->dsda_snaps, NULL); - pair != NULL; pair = nvlist_next_nvpair(dsda->dsda_snaps, pair)) { - dsl_dataset_t *ds; - - error = dsl_dataset_hold(dp, nvpair_name(pair), - FTAG, &ds); - - /* - * If the snapshot does not exist, silently ignore it - * (it's "already destroyed"). - */ - if (error == ENOENT) - continue; + error = dsl_dataset_hold(dp, dsname, FTAG, &ds); - if (error == 0) { - error = dsl_destroy_snapshot_check_impl(ds, - dsda->dsda_defer); - dsl_dataset_rele(ds, FTAG); - } + /* + * If the snapshot does not exist, silently ignore it, and + * dsl_destroy_snapshot_sync() will be a no-op + * (it's "already destroyed"). + */ + if (error == ENOENT) + return (0); - if (error == 0) { - fnvlist_add_boolean(dsda->dsda_successful_snaps, - nvpair_name(pair)); - } else { - fnvlist_add_int32(dsda->dsda_errlist, - nvpair_name(pair), error); - } + if (error == 0) { + error = dsl_destroy_snapshot_check_impl(ds, defer); + dsl_dataset_rele(ds, FTAG); } - pair = nvlist_next_nvpair(dsda->dsda_errlist, NULL); - if (pair != NULL) - return (fnvpair_value_int32(pair)); - - return (0); + return (error); } struct process_old_arg { @@ -473,23 +450,22 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx) dmu_object_free_zapified(mos, obj, tx); } -static void +void dsl_destroy_snapshot_sync(void *arg, dmu_tx_t *tx) { - dmu_snapshots_destroy_arg_t *dsda = arg; - dsl_pool_t *dp = dmu_tx_pool(tx); - nvpair_t *pair; + dsl_destroy_snapshot_arg_t *ddsa = arg; + const char *dsname = ddsa->ddsa_name; + boolean_t defer = ddsa->ddsa_defer; - for (pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, NULL); - pair != NULL; - pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, pair)) { - dsl_dataset_t *ds; - - VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); + dsl_pool_t *dp = dmu_tx_pool(tx); + dsl_dataset_t *ds; - dsl_destroy_snapshot_sync_impl(ds, dsda->dsda_defer, tx); - dsl_dataset_rele(ds, FTAG); - } + int error = dsl_dataset_hold(dp, dsname, FTAG, &ds); + if (error == ENOENT) + return; + ASSERT0(error); + dsl_destroy_snapshot_sync_impl(ds, defer, tx); + dsl_dataset_rele(ds, FTAG); } /* @@ -509,25 +485,85 @@ int dsl_destroy_snapshots_nvl(nvlist_t *snaps, boolean_t defer, nvlist_t *errlist) { - dmu_snapshots_destroy_arg_t dsda; - int error; - nvpair_t *pair; - - pair = nvlist_next_nvpair(snaps, NULL); - if (pair == NULL) + if (nvlist_next_nvpair(snaps, NULL) == NULL) return (0); - dsda.dsda_snaps = snaps; - dsda.dsda_successful_snaps = fnvlist_alloc(); - dsda.dsda_defer = defer; - dsda.dsda_errlist = errlist; - - error = dsl_sync_task(nvpair_name(pair), - dsl_destroy_snapshot_check, dsl_destroy_snapshot_sync, - &dsda, 0, ZFS_SPACE_CHECK_NONE); - fnvlist_free(dsda.dsda_successful_snaps); + nvlist_t *arg = fnvlist_alloc(); + nvlist_t *snaps_normalized = fnvlist_alloc(); + /* + * lzc_destroy_snaps() is documented to take an nvlist whose + * values "don't matter". We need to convert that nvlist to one + * that we know can be converted to LUA. + */ + for (nvpair_t *pair = nvlist_next_nvpair(snaps, NULL); + pair != NULL; pair = nvlist_next_nvpair(snaps, pair)) { + fnvlist_add_boolean_value(snaps_normalized, + nvpair_name(pair), B_TRUE); + } + fnvlist_add_nvlist(arg, "snaps", snaps_normalized); + fnvlist_add_boolean_value(arg, "defer", defer); + + nvlist_t *wrapper = fnvlist_alloc(); + fnvlist_add_nvlist(wrapper, ZCP_ARG_ARGLIST, arg); + fnvlist_free(arg); + + const char *program = + "arg = ...\n" + "snaps = arg['snaps']\n" + "defer = arg['defer']\n" + "errors = { }\n" + "has_errors = false\n" + "for snap, v in pairs(snaps) do\n" + " errno = zfs.check.destroy{snap, defer=defer}\n" + " zfs.debug('snap: ' .. snap .. ' errno: ' .. errno)\n" + " if errno == ENOENT then\n" + " snaps[snap] = nil\n" + " elseif errno ~= 0 then\n" + " errors[snap] = errno\n" + " has_errors = true\n" + " end\n" + "end\n" + "if has_errors then\n" + " return errors\n" + "end\n" + "for snap, v in pairs(snaps) do\n" + " errno = zfs.sync.destroy{snap, defer=defer}\n" + " assert(errno == 0)\n" + "end\n" + "return { }\n"; + + nvlist_t *result = fnvlist_alloc(); + int error = zcp_eval(nvpair_name(nvlist_next_nvpair(snaps, NULL)), + program, + 0, + zfs_lua_max_memlimit, + fnvlist_lookup_nvpair(wrapper, ZCP_ARG_ARGLIST), result); + if (error != 0) { + char *errorstr = NULL; + (void) nvlist_lookup_string(result, ZCP_RET_ERROR, &errorstr); + if (errorstr != NULL) { + zfs_dbgmsg(errorstr); + } + return (error); + } + fnvlist_free(wrapper); - return (error); + /* + * lzc_destroy_snaps() is documented to fill the errlist with + * int32 values, so we need to covert the int64 values that are + * returned from LUA. + */ + int rv = 0; + nvlist_t *errlist_raw = fnvlist_lookup_nvlist(result, ZCP_RET_RETURN); + for (nvpair_t *pair = nvlist_next_nvpair(errlist_raw, NULL); + pair != NULL; pair = nvlist_next_nvpair(errlist_raw, pair)) { + int32_t val = (int32_t)fnvpair_value_int64(pair); + if (rv == 0) + rv = val; + fnvlist_add_int32(errlist, nvpair_name(pair), val); + } + fnvlist_free(result); + return (rv); } int @@ -598,10 +634,6 @@ old_synchronous_dataset_destroy(dsl_dataset_t *ds, dmu_tx_t *tx) dsl_dataset_phys(ds)->ds_unique_bytes == 0); } -typedef struct dsl_destroy_head_arg { - const char *ddha_name; -} dsl_destroy_head_arg_t; - int dsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds) { @@ -647,7 +679,7 @@ dsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds) return (0); } -static int +int dsl_destroy_head_check(void *arg, dmu_tx_t *tx) { dsl_destroy_head_arg_t *ddha = arg; @@ -880,7 +912,7 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx) } } -static void +void dsl_destroy_head_sync(void *arg, dmu_tx_t *tx) { dsl_destroy_head_arg_t *ddha = arg; diff --git a/usr/src/uts/common/fs/zfs/dsl_dir.c b/usr/src/uts/common/fs/zfs/dsl_dir.c index da2c95b1e9..897c92a449 100644 --- a/usr/src/uts/common/fs/zfs/dsl_dir.c +++ b/usr/src/uts/common/fs/zfs/dsl_dir.c @@ -926,61 +926,139 @@ dsl_dir_is_clone(dsl_dir_t *dd) dd->dd_pool->dp_origin_snap->ds_object)); } + +uint64_t +dsl_dir_get_used(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_bytes); +} + +uint64_t +dsl_dir_get_quota(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_quota); +} + +uint64_t +dsl_dir_get_reservation(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_reserved); +} + +uint64_t +dsl_dir_get_compressratio(dsl_dir_t *dd) +{ + /* a fixed point number, 100x the ratio */ + return (dsl_dir_phys(dd)->dd_compressed_bytes == 0 ? 100 : + (dsl_dir_phys(dd)->dd_uncompressed_bytes * 100 / + dsl_dir_phys(dd)->dd_compressed_bytes)); +} + +uint64_t +dsl_dir_get_logicalused(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_uncompressed_bytes); +} + +uint64_t +dsl_dir_get_usedsnap(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_SNAP]); +} + +uint64_t +dsl_dir_get_usedds(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_HEAD]); +} + +uint64_t +dsl_dir_get_usedrefreserv(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_REFRSRV]); +} + +uint64_t +dsl_dir_get_usedchild(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_CHILD] + + dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_CHILD_RSRV]); +} + +void +dsl_dir_get_origin(dsl_dir_t *dd, char *buf) +{ + dsl_dataset_t *ds; + VERIFY0(dsl_dataset_hold_obj(dd->dd_pool, + dsl_dir_phys(dd)->dd_origin_obj, FTAG, &ds)); + + dsl_dataset_name(ds, buf); + + dsl_dataset_rele(ds, FTAG); +} + +int +dsl_dir_get_filesystem_count(dsl_dir_t *dd, uint64_t *count) +{ + if (dsl_dir_is_zapified(dd)) { + objset_t *os = dd->dd_pool->dp_meta_objset; + return (zap_lookup(os, dd->dd_object, DD_FIELD_FILESYSTEM_COUNT, + sizeof (*count), 1, count)); + } else { + return (ENOENT); + } +} + +int +dsl_dir_get_snapshot_count(dsl_dir_t *dd, uint64_t *count) +{ + if (dsl_dir_is_zapified(dd)) { + objset_t *os = dd->dd_pool->dp_meta_objset; + return (zap_lookup(os, dd->dd_object, DD_FIELD_SNAPSHOT_COUNT, + sizeof (*count), 1, count)); + } else { + return (ENOENT); + } +} + void dsl_dir_stats(dsl_dir_t *dd, nvlist_t *nv) { mutex_enter(&dd->dd_lock); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED, - dsl_dir_phys(dd)->dd_used_bytes); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_QUOTA, - dsl_dir_phys(dd)->dd_quota); + dsl_dir_get_quota(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_RESERVATION, - dsl_dir_phys(dd)->dd_reserved); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, - dsl_dir_phys(dd)->dd_compressed_bytes == 0 ? 100 : - (dsl_dir_phys(dd)->dd_uncompressed_bytes * 100 / - dsl_dir_phys(dd)->dd_compressed_bytes)); + dsl_dir_get_reservation(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_LOGICALUSED, - dsl_dir_phys(dd)->dd_uncompressed_bytes); + dsl_dir_get_logicalused(dd)); if (dsl_dir_phys(dd)->dd_flags & DD_FLAG_USED_BREAKDOWN) { dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDSNAP, - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_SNAP]); + dsl_dir_get_usedsnap(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDDS, - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_HEAD]); + dsl_dir_get_usedds(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDREFRESERV, - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_REFRSRV]); + dsl_dir_get_usedrefreserv(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDCHILD, - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_CHILD] + - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_CHILD_RSRV]); + dsl_dir_get_usedchild(dd)); } mutex_exit(&dd->dd_lock); - if (dsl_dir_is_zapified(dd)) { - uint64_t count; - objset_t *os = dd->dd_pool->dp_meta_objset; - - if (zap_lookup(os, dd->dd_object, DD_FIELD_FILESYSTEM_COUNT, - sizeof (count), 1, &count) == 0) { - dsl_prop_nvlist_add_uint64(nv, - ZFS_PROP_FILESYSTEM_COUNT, count); - } - if (zap_lookup(os, dd->dd_object, DD_FIELD_SNAPSHOT_COUNT, - sizeof (count), 1, &count) == 0) { - dsl_prop_nvlist_add_uint64(nv, - ZFS_PROP_SNAPSHOT_COUNT, count); - } + uint64_t count; + if (dsl_dir_get_filesystem_count(dd, &count) == 0) { + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_FILESYSTEM_COUNT, + count); + } + if (dsl_dir_get_snapshot_count(dd, &count) == 0) { + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_SNAPSHOT_COUNT, + count); } if (dsl_dir_is_clone(dd)) { - dsl_dataset_t *ds; char buf[ZFS_MAX_DATASET_NAME_LEN]; - - VERIFY0(dsl_dataset_hold_obj(dd->dd_pool, - dsl_dir_phys(dd)->dd_origin_obj, FTAG, &ds)); - dsl_dataset_name(ds, buf); - dsl_dataset_rele(ds, FTAG); + dsl_dir_get_origin(dd, buf); dsl_prop_nvlist_add_string(nv, ZFS_PROP_ORIGIN, buf); } + } void diff --git a/usr/src/uts/common/fs/zfs/lua/README.zfs b/usr/src/uts/common/fs/zfs/lua/README.zfs new file mode 100644 index 0000000000..0e22de7a4a --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/README.zfs @@ -0,0 +1,80 @@ +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2017 by Delphix. All rights reserved. +# + +Introduction +------------ + +This README describes the Lua interpreter source code that lives in the ZFS +source tree to enable execution of ZFS channel programs, including its +maintenance policy, the modifications that have been made to it, and how it +should (and should not) be used. + +For a description of the Lua language and features exposed by ZFS channel +programs, please refer to the zfs-program(1m) man page instead. + + +Maintenance policy +------------------ + +The Lua runtime is considered stable software. Channel programs don't need much +complicated logic, so updates to the Lua runtime from upstream are viewed as +nice-to-have, but not required for channel programs to be well-supported. As +such, the Lua runtime in ZFS should be updated on an as-needed basis for +security vulnerabilities, but not much else. + + +Modifications to Lua +-------------------- + +The version of the Lua runtime we're using in ZFS has been modified in a variety +of ways to make it more useful for the specific purpose of running channel +programs. These changes include: + +1. "Normal" Lua uses floating point for all numbers it stores, but those aren't + useful inside ZFS / the kernel. We have changed the runtime to use int64_t + throughout for all numbers. +2. Some of the Lua standard libraries do file I/O or spawn processes, but + neither of these make sense from inside channel programs. We have removed + those libraries rather than reimplementing them using kernel APIs. +3. The "normal" Lua runtime handles errors by failing fatally, but since this + version of Lua runs inside the kernel we must handle these failures and + return meaningful error codes to userland. We have customized the Lua + failure paths so that they aren't fatal. +4. Running poorly-vetted code inside the kernel is always a risk; even if the + ability to do so is restricted to the root user, it's still possible to write + an incorrect program that results in an infinite loop or massive memory use. + We've added new protections into the Lua interpreter to limit the runtime + (measured in number of Lua instructions run) and memory overhead of running + a channel program. +5. The Lua bytecode is not designed to be secure / safe, so it would be easy to + pass invalid bytecode which can panic the kernel. By comparison, the parser + is hardened and fails gracefully on invalid input. Therefore, we only accept + Lua source code at the ioctl level and then interpret it inside the kernel. + +Each of these modifications have been tested in the zfs-test suite. If / when +new modifications are made, new tests should be added to the suite located in +zfs-tests/tests/functional/channel_program/lua_core. + + +How to use this Lua interpreter +------------------------------- + +From the above, it should be clear that this is not a general-purpose Lua +interpreter. Additional work would be required to extricate this custom version +of Lua from ZFS and make it usable by other areas of the kernel. diff --git a/usr/src/uts/common/fs/zfs/lua/lapi.c b/usr/src/uts/common/fs/zfs/lua/lapi.c new file mode 100644 index 0000000000..34820a2d8b --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lapi.c @@ -0,0 +1,1283 @@ +/* +** $Id: lapi.c,v 2.171.1.1 2013/04/12 18:48:47 roberto Exp $ +** Lua API +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define lapi_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" + + + +const char lua_ident[] = + "$LuaVersion: " LUA_COPYRIGHT " $" + "$LuaAuthors: " LUA_AUTHORS " $"; + + +/* value at a non-valid index */ +#define NONVALIDVALUE cast(TValue *, luaO_nilobject) + +/* corresponding test */ +#define isvalid(o) ((o) != luaO_nilobject) + +/* test for pseudo index */ +#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) + +/* test for valid but not pseudo index */ +#define isstackindex(i, o) (isvalid(o) && !ispseudo(i)) + +#define api_checkvalidindex(L, o) api_check(L, isvalid(o), "invalid index") + +#define api_checkstackindex(L, i, o) \ + api_check(L, isstackindex(i, o), "index not in the stack") + + +static TValue *index2addr (lua_State *L, int idx) { + CallInfo *ci = L->ci; + if (idx > 0) { + TValue *o = ci->func + idx; + api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index"); + if (o >= L->top) return NONVALIDVALUE; + else return o; + } + else if (!ispseudo(idx)) { /* negative index */ + api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); + return L->top + idx; + } + else if (idx == LUA_REGISTRYINDEX) + return &G(L)->l_registry; + else { /* upvalues */ + idx = LUA_REGISTRYINDEX - idx; + api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); + if (ttislcf(ci->func)) /* light C function? */ + return NONVALIDVALUE; /* it has no upvalues */ + else { + CClosure *func = clCvalue(ci->func); + return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : NONVALIDVALUE; + } + } +} + + +/* +** to be called by 'lua_checkstack' in protected mode, to grow stack +** capturing memory errors +*/ +static void growstack (lua_State *L, void *ud) { + int size = *(int *)ud; + luaD_growstack(L, size); +} + + +LUA_API int lua_checkstack (lua_State *L, int size) { + int res; + CallInfo *ci = L->ci; + lua_lock(L); + if (L->stack_last - L->top > size) /* stack large enough? */ + res = 1; /* yes; check is OK */ + else { /* no; need to grow stack */ + int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; + if (inuse > LUAI_MAXSTACK - size) /* can grow without overflow? */ + res = 0; /* no */ + else /* try to grow stack */ + res = (luaD_rawrunprotected(L, &growstack, &size) == LUA_OK); + } + if (res && ci->top < L->top + size) + ci->top = L->top + size; /* adjust frame top */ + lua_unlock(L); + return res; +} + + +LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { + int i; + if (from == to) return; + lua_lock(to); + api_checknelems(from, n); + api_check(from, G(from) == G(to), "moving among independent states"); + api_check(from, to->ci->top - to->top >= n, "not enough elements to move"); + from->top -= n; + for (i = 0; i < n; i++) { + setobj2s(to, to->top++, from->top + i); + } + lua_unlock(to); +} + + +LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { + lua_CFunction old; + lua_lock(L); + old = G(L)->panic; + G(L)->panic = panicf; + lua_unlock(L); + return old; +} + + +LUA_API const lua_Number *lua_version (lua_State *L) { + static const lua_Number version = LUA_VERSION_NUM; + if (L == NULL) return &version; + else return G(L)->version; +} + + + +/* +** basic stack manipulation +*/ + + +/* +** convert an acceptable stack index into an absolute index +*/ +LUA_API int lua_absindex (lua_State *L, int idx) { + return (idx > 0 || ispseudo(idx)) + ? idx + : cast_int(L->top - L->ci->func + idx); +} + + +LUA_API int lua_gettop (lua_State *L) { + return cast_int(L->top - (L->ci->func + 1)); +} + + +LUA_API void lua_settop (lua_State *L, int idx) { + StkId func = L->ci->func; + lua_lock(L); + if (idx >= 0) { + api_check(L, idx <= L->stack_last - (func + 1), "new top too large"); + while (L->top < (func + 1) + idx) + setnilvalue(L->top++); + L->top = (func + 1) + idx; + } + else { + api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); + L->top += idx+1; /* `subtract' index (index is negative) */ + } + lua_unlock(L); +} + + +LUA_API void lua_remove (lua_State *L, int idx) { + StkId p; + lua_lock(L); + p = index2addr(L, idx); + api_checkstackindex(L, idx, p); + while (++p < L->top) setobjs2s(L, p-1, p); + L->top--; + lua_unlock(L); +} + + +LUA_API void lua_insert (lua_State *L, int idx) { + StkId p; + StkId q; + lua_lock(L); + p = index2addr(L, idx); + api_checkstackindex(L, idx, p); + for (q = L->top; q > p; q--) /* use L->top as a temporary */ + setobjs2s(L, q, q - 1); + setobjs2s(L, p, L->top); + lua_unlock(L); +} + + +static void moveto (lua_State *L, TValue *fr, int idx) { + TValue *to = index2addr(L, idx); + api_checkvalidindex(L, to); + setobj(L, to, fr); + if (idx < LUA_REGISTRYINDEX) /* function upvalue? */ + luaC_barrier(L, clCvalue(L->ci->func), fr); + /* LUA_REGISTRYINDEX does not need gc barrier + (collector revisits it before finishing collection) */ +} + + +LUA_API void lua_replace (lua_State *L, int idx) { + lua_lock(L); + api_checknelems(L, 1); + moveto(L, L->top - 1, idx); + L->top--; + lua_unlock(L); +} + + +LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { + TValue *fr; + lua_lock(L); + fr = index2addr(L, fromidx); + moveto(L, fr, toidx); + lua_unlock(L); +} + + +LUA_API void lua_pushvalue (lua_State *L, int idx) { + lua_lock(L); + setobj2s(L, L->top, index2addr(L, idx)); + api_incr_top(L); + lua_unlock(L); +} + + + +/* +** access functions (stack -> C) +*/ + + +LUA_API int lua_type (lua_State *L, int idx) { + StkId o = index2addr(L, idx); + return (isvalid(o) ? ttypenv(o) : LUA_TNONE); +} + + +LUA_API const char *lua_typename (lua_State *L, int t) { + UNUSED(L); + return ttypename(t); +} + + +LUA_API int lua_iscfunction (lua_State *L, int idx) { + StkId o = index2addr(L, idx); + return (ttislcf(o) || (ttisCclosure(o))); +} + + +LUA_API int lua_isnumber (lua_State *L, int idx) { + TValue n; + const TValue *o = index2addr(L, idx); + return tonumber(o, &n); +} + + +LUA_API int lua_isstring (lua_State *L, int idx) { + int t = lua_type(L, idx); + return (t == LUA_TSTRING || t == LUA_TNUMBER); +} + + +LUA_API int lua_isuserdata (lua_State *L, int idx) { + const TValue *o = index2addr(L, idx); + return (ttisuserdata(o) || ttislightuserdata(o)); +} + + +LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { + StkId o1 = index2addr(L, index1); + StkId o2 = index2addr(L, index2); + return (isvalid(o1) && isvalid(o2)) ? luaV_rawequalobj(o1, o2) : 0; +} + + +LUA_API void lua_arith (lua_State *L, int op) { + StkId o1; /* 1st operand */ + StkId o2; /* 2nd operand */ + lua_lock(L); + if (op != LUA_OPUNM) /* all other operations expect two operands */ + api_checknelems(L, 2); + else { /* for unary minus, add fake 2nd operand */ + api_checknelems(L, 1); + setobjs2s(L, L->top, L->top - 1); + L->top++; + } + o1 = L->top - 2; + o2 = L->top - 1; + if (ttisnumber(o1) && ttisnumber(o2)) { + setnvalue(o1, luaO_arith(op, nvalue(o1), nvalue(o2))); + } + else + luaV_arith(L, o1, o1, o2, cast(TMS, op - LUA_OPADD + TM_ADD)); + L->top--; + lua_unlock(L); +} + + +LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { + StkId o1, o2; + int i = 0; + lua_lock(L); /* may call tag method */ + o1 = index2addr(L, index1); + o2 = index2addr(L, index2); + if (isvalid(o1) && isvalid(o2)) { + switch (op) { + case LUA_OPEQ: i = equalobj(L, o1, o2); break; + case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break; + case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break; + default: api_check(L, 0, "invalid option"); + } + } + lua_unlock(L); + return i; +} + + +LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum) { + TValue n; + const TValue *o = index2addr(L, idx); + if (tonumber(o, &n)) { + if (isnum) *isnum = 1; + return nvalue(o); + } + else { + if (isnum) *isnum = 0; + return 0; + } +} + + +LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum) { + TValue n; + const TValue *o = index2addr(L, idx); + if (tonumber(o, &n)) { + lua_Integer res; + lua_Number num = nvalue(o); + lua_number2integer(res, num); + if (isnum) *isnum = 1; + return res; + } + else { + if (isnum) *isnum = 0; + return 0; + } +} + + +LUA_API lua_Unsigned lua_tounsignedx (lua_State *L, int idx, int *isnum) { + TValue n; + const TValue *o = index2addr(L, idx); + if (tonumber(o, &n)) { + lua_Unsigned res; + lua_Number num = nvalue(o); + lua_number2unsigned(res, num); + if (isnum) *isnum = 1; + return res; + } + else { + if (isnum) *isnum = 0; + return 0; + } +} + + +LUA_API int lua_toboolean (lua_State *L, int idx) { + const TValue *o = index2addr(L, idx); + return !l_isfalse(o); +} + + +LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { + StkId o = index2addr(L, idx); + if (!ttisstring(o)) { + lua_lock(L); /* `luaV_tostring' may create a new string */ + if (!luaV_tostring(L, o)) { /* conversion failed? */ + if (len != NULL) *len = 0; + lua_unlock(L); + return NULL; + } + luaC_checkGC(L); + o = index2addr(L, idx); /* previous call may reallocate the stack */ + lua_unlock(L); + } + if (len != NULL) *len = tsvalue(o)->len; + return svalue(o); +} + + +LUA_API size_t lua_rawlen (lua_State *L, int idx) { + StkId o = index2addr(L, idx); + switch (ttypenv(o)) { + case LUA_TSTRING: return tsvalue(o)->len; + case LUA_TUSERDATA: return uvalue(o)->len; + case LUA_TTABLE: return luaH_getn(hvalue(o)); + default: return 0; + } +} + + +LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { + StkId o = index2addr(L, idx); + if (ttislcf(o)) return fvalue(o); + else if (ttisCclosure(o)) + return clCvalue(o)->f; + else return NULL; /* not a C function */ +} + + +LUA_API void *lua_touserdata (lua_State *L, int idx) { + StkId o = index2addr(L, idx); + switch (ttypenv(o)) { + case LUA_TUSERDATA: return (rawuvalue(o) + 1); + case LUA_TLIGHTUSERDATA: return pvalue(o); + default: return NULL; + } +} + + +LUA_API lua_State *lua_tothread (lua_State *L, int idx) { + StkId o = index2addr(L, idx); + return (!ttisthread(o)) ? NULL : thvalue(o); +} + + +LUA_API const void *lua_topointer (lua_State *L, int idx) { + StkId o = index2addr(L, idx); + switch (ttype(o)) { + case LUA_TTABLE: return hvalue(o); + case LUA_TLCL: return clLvalue(o); + case LUA_TCCL: return clCvalue(o); + case LUA_TLCF: return cast(void *, cast(size_t, fvalue(o))); + case LUA_TTHREAD: return thvalue(o); + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + return lua_touserdata(L, idx); + default: return NULL; + } +} + + + +/* +** push functions (C -> stack) +*/ + + +LUA_API void lua_pushnil (lua_State *L) { + lua_lock(L); + setnilvalue(L->top); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { + lua_lock(L); + setnvalue(L->top, n); + luai_checknum(L, L->top, + luaG_runerror(L, "C API - attempt to push a signaling NaN")); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { + lua_lock(L); + setnvalue(L->top, cast_num(n)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushunsigned (lua_State *L, lua_Unsigned u) { + lua_Number n; + lua_lock(L); + n = lua_unsigned2number(u); + setnvalue(L->top, n); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { + TString *ts; + lua_lock(L); + luaC_checkGC(L); + ts = luaS_newlstr(L, s, len); + setsvalue2s(L, L->top, ts); + api_incr_top(L); + lua_unlock(L); + return getstr(ts); +} + + +LUA_API const char *lua_pushstring (lua_State *L, const char *s) { + if (s == NULL) { + lua_pushnil(L); + return NULL; + } + else { + TString *ts; + lua_lock(L); + luaC_checkGC(L); + ts = luaS_new(L, s); + setsvalue2s(L, L->top, ts); + api_incr_top(L); + lua_unlock(L); + return getstr(ts); + } +} + + +LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, + va_list argp) { + const char *ret; + lua_lock(L); + luaC_checkGC(L); + ret = luaO_pushvfstring(L, fmt, argp); + lua_unlock(L); + return ret; +} + + +LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { + const char *ret; + va_list argp; + lua_lock(L); + luaC_checkGC(L); + va_start(argp, fmt); + ret = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + lua_unlock(L); + return ret; +} + + +LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + lua_lock(L); + if (n == 0) { + setfvalue(L->top, fn); + } + else { + Closure *cl; + api_checknelems(L, n); + api_check(L, n <= MAXUPVAL, "upvalue index too large"); + luaC_checkGC(L); + cl = luaF_newCclosure(L, n); + cl->c.f = fn; + L->top -= n; + while (n--) + setobj2n(L, &cl->c.upvalue[n], L->top + n); + setclCvalue(L, L->top, cl); + } + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushboolean (lua_State *L, int b) { + lua_lock(L); + setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { + lua_lock(L); + setpvalue(L->top, p); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_pushthread (lua_State *L) { + lua_lock(L); + setthvalue(L, L->top, L); + api_incr_top(L); + lua_unlock(L); + return (G(L)->mainthread == L); +} + + + +/* +** get functions (Lua -> stack) +*/ + + +LUA_API void lua_getglobal (lua_State *L, const char *var) { + Table *reg = hvalue(&G(L)->l_registry); + const TValue *gt; /* global table */ + lua_lock(L); + gt = luaH_getint(reg, LUA_RIDX_GLOBALS); + setsvalue2s(L, L->top++, luaS_new(L, var)); + luaV_gettable(L, gt, L->top - 1, L->top - 1); + lua_unlock(L); +} + + +LUA_API void lua_gettable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2addr(L, idx); + luaV_gettable(L, t, L->top - 1, L->top - 1); + lua_unlock(L); +} + + +LUA_API void lua_getfield (lua_State *L, int idx, const char *k) { + StkId t; + lua_lock(L); + t = index2addr(L, idx); + setsvalue2s(L, L->top, luaS_new(L, k)); + api_incr_top(L); + luaV_gettable(L, t, L->top - 1, L->top - 1); + lua_unlock(L); +} + + +LUA_API void lua_rawget (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2addr(L, idx); + api_check(L, ttistable(t), "table expected"); + setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1)); + lua_unlock(L); +} + + +LUA_API void lua_rawgeti (lua_State *L, int idx, int n) { + StkId t; + lua_lock(L); + t = index2addr(L, idx); + api_check(L, ttistable(t), "table expected"); + setobj2s(L, L->top, luaH_getint(hvalue(t), n)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_rawgetp (lua_State *L, int idx, const void *p) { + StkId t; + TValue k; + lua_lock(L); + t = index2addr(L, idx); + api_check(L, ttistable(t), "table expected"); + setpvalue(&k, cast(void *, p)); + setobj2s(L, L->top, luaH_get(hvalue(t), &k)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { + Table *t; + lua_lock(L); + luaC_checkGC(L); + t = luaH_new(L); + sethvalue(L, L->top, t); + api_incr_top(L); + if (narray > 0 || nrec > 0) + luaH_resize(L, t, narray, nrec); + lua_unlock(L); +} + + +LUA_API int lua_getmetatable (lua_State *L, int objindex) { + const TValue *obj; + Table *mt = NULL; + int res; + lua_lock(L); + obj = index2addr(L, objindex); + switch (ttypenv(obj)) { + case LUA_TTABLE: + mt = hvalue(obj)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(obj)->metatable; + break; + default: + mt = G(L)->mt[ttypenv(obj)]; + break; + } + if (mt == NULL) + res = 0; + else { + sethvalue(L, L->top, mt); + api_incr_top(L); + res = 1; + } + lua_unlock(L); + return res; +} + + +LUA_API void lua_getuservalue (lua_State *L, int idx) { + StkId o; + lua_lock(L); + o = index2addr(L, idx); + api_check(L, ttisuserdata(o), "userdata expected"); + if (uvalue(o)->env) { + sethvalue(L, L->top, uvalue(o)->env); + } else + setnilvalue(L->top); + api_incr_top(L); + lua_unlock(L); +} + + +/* +** set functions (stack -> Lua) +*/ + + +LUA_API void lua_setglobal (lua_State *L, const char *var) { + Table *reg = hvalue(&G(L)->l_registry); + const TValue *gt; /* global table */ + lua_lock(L); + api_checknelems(L, 1); + gt = luaH_getint(reg, LUA_RIDX_GLOBALS); + setsvalue2s(L, L->top++, luaS_new(L, var)); + luaV_settable(L, gt, L->top - 1, L->top - 2); + L->top -= 2; /* pop value and key */ + lua_unlock(L); +} + + +LUA_API void lua_settable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2addr(L, idx); + luaV_settable(L, t, L->top - 2, L->top - 1); + L->top -= 2; /* pop index and value */ + lua_unlock(L); +} + + +LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { + StkId t; + lua_lock(L); + api_checknelems(L, 1); + t = index2addr(L, idx); + setsvalue2s(L, L->top++, luaS_new(L, k)); + luaV_settable(L, t, L->top - 1, L->top - 2); + L->top -= 2; /* pop value and key */ + lua_unlock(L); +} + + +LUA_API void lua_rawset (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2addr(L, idx); + api_check(L, ttistable(t), "table expected"); + setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1); + invalidateTMcache(hvalue(t)); + luaC_barrierback(L, gcvalue(t), L->top-1); + L->top -= 2; + lua_unlock(L); +} + + +LUA_API void lua_rawseti (lua_State *L, int idx, int n) { + StkId t; + lua_lock(L); + api_checknelems(L, 1); + t = index2addr(L, idx); + api_check(L, ttistable(t), "table expected"); + luaH_setint(L, hvalue(t), n, L->top - 1); + luaC_barrierback(L, gcvalue(t), L->top-1); + L->top--; + lua_unlock(L); +} + + +LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { + StkId t; + TValue k; + lua_lock(L); + api_checknelems(L, 1); + t = index2addr(L, idx); + api_check(L, ttistable(t), "table expected"); + setpvalue(&k, cast(void *, p)); + setobj2t(L, luaH_set(L, hvalue(t), &k), L->top - 1); + luaC_barrierback(L, gcvalue(t), L->top - 1); + L->top--; + lua_unlock(L); +} + + +LUA_API int lua_setmetatable (lua_State *L, int objindex) { + TValue *obj; + Table *mt; + lua_lock(L); + api_checknelems(L, 1); + obj = index2addr(L, objindex); + if (ttisnil(L->top - 1)) + mt = NULL; + else { + api_check(L, ttistable(L->top - 1), "table expected"); + mt = hvalue(L->top - 1); + } + switch (ttypenv(obj)) { + case LUA_TTABLE: { + hvalue(obj)->metatable = mt; + if (mt) { + luaC_objbarrierback(L, gcvalue(obj), mt); + luaC_checkfinalizer(L, gcvalue(obj), mt); + } + break; + } + case LUA_TUSERDATA: { + uvalue(obj)->metatable = mt; + if (mt) { + luaC_objbarrier(L, rawuvalue(obj), mt); + luaC_checkfinalizer(L, gcvalue(obj), mt); + } + break; + } + default: { + G(L)->mt[ttypenv(obj)] = mt; + break; + } + } + L->top--; + lua_unlock(L); + return 1; +} + + +LUA_API void lua_setuservalue (lua_State *L, int idx) { + StkId o; + lua_lock(L); + api_checknelems(L, 1); + o = index2addr(L, idx); + api_check(L, ttisuserdata(o), "userdata expected"); + if (ttisnil(L->top - 1)) + uvalue(o)->env = NULL; + else { + api_check(L, ttistable(L->top - 1), "table expected"); + uvalue(o)->env = hvalue(L->top - 1); + luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1)); + } + L->top--; + lua_unlock(L); +} + + +/* +** `load' and `call' functions (run Lua code) +*/ + + +#define checkresults(L,na,nr) \ + api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \ + "results from function overflow current stack size") + + +LUA_API int lua_getctx (lua_State *L, int *ctx) { + if (L->ci->callstatus & CIST_YIELDED) { + if (ctx) *ctx = L->ci->u.c.ctx; + return L->ci->u.c.status; + } + else return LUA_OK; +} + + +LUA_API void lua_callk (lua_State *L, int nargs, int nresults, int ctx, + lua_CFunction k) { + StkId func; + lua_lock(L); + api_check(L, k == NULL || !isLua(L->ci), + "cannot use continuations inside hooks"); + api_checknelems(L, nargs+1); + api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); + checkresults(L, nargs, nresults); + func = L->top - (nargs+1); + if (k != NULL && L->nny == 0) { /* need to prepare continuation? */ + L->ci->u.c.k = k; /* save continuation */ + L->ci->u.c.ctx = ctx; /* save context */ + luaD_call(L, func, nresults, 1); /* do the call */ + } + else /* no continuation or no yieldable */ + luaD_call(L, func, nresults, 0); /* just do the call */ + adjustresults(L, nresults); + lua_unlock(L); +} + + + +/* +** Execute a protected call. +*/ +struct CallS { /* data to `f_call' */ + StkId func; + int nresults; +}; + + +static void f_call (lua_State *L, void *ud) { + struct CallS *c = cast(struct CallS *, ud); + luaD_call(L, c->func, c->nresults, 0); +} + + + +LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, + int ctx, lua_CFunction k) { + struct CallS c; + int status; + ptrdiff_t func; + lua_lock(L); + api_check(L, k == NULL || !isLua(L->ci), + "cannot use continuations inside hooks"); + api_checknelems(L, nargs+1); + api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); + checkresults(L, nargs, nresults); + if (errfunc == 0) + func = 0; + else { + StkId o = index2addr(L, errfunc); + api_checkstackindex(L, errfunc, o); + func = savestack(L, o); + } + c.func = L->top - (nargs+1); /* function to be called */ + if (k == NULL || L->nny > 0) { /* no continuation or no yieldable? */ + c.nresults = nresults; /* do a 'conventional' protected call */ + status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); + } + else { /* prepare continuation (call is already protected by 'resume') */ + CallInfo *ci = L->ci; + ci->u.c.k = k; /* save continuation */ + ci->u.c.ctx = ctx; /* save context */ + /* save information for error recovery */ + ci->extra = savestack(L, c.func); + ci->u.c.old_allowhook = L->allowhook; + ci->u.c.old_errfunc = L->errfunc; + L->errfunc = func; + /* mark that function may do error recovery */ + ci->callstatus |= CIST_YPCALL; + luaD_call(L, c.func, nresults, 1); /* do the call */ + ci->callstatus &= ~CIST_YPCALL; + L->errfunc = ci->u.c.old_errfunc; + status = LUA_OK; /* if it is here, there were no errors */ + } + adjustresults(L, nresults); + lua_unlock(L); + return status; +} + + +LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, + const char *chunkname, const char *mode) { + ZIO z; + int status; + lua_lock(L); + if (!chunkname) chunkname = "?"; + luaZ_init(L, &z, reader, data); + status = luaD_protectedparser(L, &z, chunkname, mode); + if (status == LUA_OK) { /* no errors? */ + LClosure *f = clLvalue(L->top - 1); /* get newly created function */ + if (f->nupvalues == 1) { /* does it have one upvalue? */ + /* get global table from registry */ + Table *reg = hvalue(&G(L)->l_registry); + const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS); + /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ + setobj(L, f->upvals[0]->v, gt); + luaC_barrier(L, f->upvals[0], gt); + } + } + lua_unlock(L); + return status; +} + + +LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) { + int status; + TValue *o; + lua_lock(L); + api_checknelems(L, 1); + o = L->top - 1; + if (isLfunction(o)) + status = luaU_dump(L, getproto(o), writer, data, 0); + else + status = 1; + lua_unlock(L); + return status; +} + + +LUA_API int lua_status (lua_State *L) { + return L->status; +} + + +/* +** Garbage-collection function +*/ + +LUA_API int lua_gc (lua_State *L, int what, int data) { + int res = 0; + global_State *g; + lua_lock(L); + g = G(L); + switch (what) { + case LUA_GCSTOP: { + g->gcrunning = 0; + break; + } + case LUA_GCRESTART: { + luaE_setdebt(g, 0); + g->gcrunning = 1; + break; + } + case LUA_GCCOLLECT: { + luaC_fullgc(L, 0); + break; + } + case LUA_GCCOUNT: { + /* GC values are expressed in Kbytes: #bytes/2^10 */ + res = cast_int(gettotalbytes(g) >> 10); + break; + } + case LUA_GCCOUNTB: { + res = cast_int(gettotalbytes(g) & 0x3ff); + break; + } + case LUA_GCSTEP: { + if (g->gckind == KGC_GEN) { /* generational mode? */ + res = (g->GCestimate == 0); /* true if it will do major collection */ + luaC_forcestep(L); /* do a single step */ + } + else { + lu_mem debt = cast(lu_mem, data) * 1024 - GCSTEPSIZE; + if (g->gcrunning) + debt += g->GCdebt; /* include current debt */ + luaE_setdebt(g, debt); + luaC_forcestep(L); + if (g->gcstate == GCSpause) /* end of cycle? */ + res = 1; /* signal it */ + } + break; + } + case LUA_GCSETPAUSE: { + res = g->gcpause; + g->gcpause = data; + break; + } + case LUA_GCSETMAJORINC: { + res = g->gcmajorinc; + g->gcmajorinc = data; + break; + } + case LUA_GCSETSTEPMUL: { + res = g->gcstepmul; + g->gcstepmul = data; + break; + } + case LUA_GCISRUNNING: { + res = g->gcrunning; + break; + } + case LUA_GCGEN: { /* change collector to generational mode */ + luaC_changemode(L, KGC_GEN); + break; + } + case LUA_GCINC: { /* change collector to incremental mode */ + luaC_changemode(L, KGC_NORMAL); + break; + } + default: res = -1; /* invalid option */ + } + lua_unlock(L); + return res; +} + + + +/* +** miscellaneous functions +*/ + + +LUA_API int lua_error (lua_State *L) { + lua_lock(L); + api_checknelems(L, 1); + luaG_errormsg(L); + /* code unreachable; will unlock when control actually leaves the kernel */ + return 0; /* to avoid warnings */ +} + + +LUA_API int lua_next (lua_State *L, int idx) { + StkId t; + int more; + lua_lock(L); + t = index2addr(L, idx); + api_check(L, ttistable(t), "table expected"); + more = luaH_next(L, hvalue(t), L->top - 1); + if (more) { + api_incr_top(L); + } + else /* no more elements */ + L->top -= 1; /* remove key */ + lua_unlock(L); + return more; +} + + +LUA_API void lua_concat (lua_State *L, int n) { + lua_lock(L); + api_checknelems(L, n); + if (n >= 2) { + luaC_checkGC(L); + luaV_concat(L, n); + } + else if (n == 0) { /* push empty string */ + setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); + api_incr_top(L); + } + /* else n == 1; nothing to do */ + lua_unlock(L); +} + + +LUA_API void lua_len (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2addr(L, idx); + luaV_objlen(L, L->top, t); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { + lua_Alloc f; + lua_lock(L); + if (ud) *ud = G(L)->ud; + f = G(L)->frealloc; + lua_unlock(L); + return f; +} + + +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { + lua_lock(L); + G(L)->ud = ud; + G(L)->frealloc = f; + lua_unlock(L); +} + + +LUA_API void *lua_newuserdata (lua_State *L, size_t size) { + Udata *u; + lua_lock(L); + luaC_checkGC(L); + u = luaS_newudata(L, size, NULL); + setuvalue(L, L->top, u); + api_incr_top(L); + lua_unlock(L); + return u + 1; +} + + + +static const char *aux_upvalue (StkId fi, int n, TValue **val, + GCObject **owner) { + switch (ttype(fi)) { + case LUA_TCCL: { /* C closure */ + CClosure *f = clCvalue(fi); + if (!(1 <= n && n <= f->nupvalues)) return NULL; + *val = &f->upvalue[n-1]; + if (owner) *owner = obj2gco(f); + return ""; + } + case LUA_TLCL: { /* Lua closure */ + LClosure *f = clLvalue(fi); + TString *name; + Proto *p = f->p; + if (!(1 <= n && n <= p->sizeupvalues)) return NULL; + *val = f->upvals[n-1]->v; + if (owner) *owner = obj2gco(f->upvals[n - 1]); + name = p->upvalues[n-1].name; + return (name == NULL) ? "" : getstr(name); + } + default: return NULL; /* not a closure */ + } +} + + +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val = NULL; /* to avoid warnings */ + lua_lock(L); + name = aux_upvalue(index2addr(L, funcindex), n, &val, NULL); + if (name) { + setobj2s(L, L->top, val); + api_incr_top(L); + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val = NULL; /* to avoid warnings */ + GCObject *owner = NULL; /* to avoid warnings */ + StkId fi; + lua_lock(L); + fi = index2addr(L, funcindex); + api_checknelems(L, 1); + name = aux_upvalue(fi, n, &val, &owner); + if (name) { + L->top--; + setobj(L, val, L->top); + luaC_barrier(L, owner, L->top); + } + lua_unlock(L); + return name; +} + + +static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { + LClosure *f; + StkId fi = index2addr(L, fidx); + api_check(L, ttisLclosure(fi), "Lua function expected"); + f = clLvalue(fi); + api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index"); + if (pf) *pf = f; + return &f->upvals[n - 1]; /* get its upvalue pointer */ +} + + +LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) { + StkId fi = index2addr(L, fidx); + switch (ttype(fi)) { + case LUA_TLCL: { /* lua closure */ + return *getupvalref(L, fidx, n, NULL); + } + case LUA_TCCL: { /* C closure */ + CClosure *f = clCvalue(fi); + api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index"); + return &f->upvalue[n - 1]; + } + default: { + api_check(L, 0, "closure expected"); + return NULL; + } + } +} + + +LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, + int fidx2, int n2) { + LClosure *f1; + UpVal **up1 = getupvalref(L, fidx1, n1, &f1); + UpVal **up2 = getupvalref(L, fidx2, n2, NULL); + *up1 = *up2; + luaC_objbarrier(L, f1, *up2); +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lapi.h b/usr/src/uts/common/fs/zfs/lua/lapi.h new file mode 100644 index 0000000000..c7d34ad848 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lapi.h @@ -0,0 +1,24 @@ +/* +** $Id: lapi.h,v 2.7.1.1 2013/04/12 18:48:47 roberto Exp $ +** Auxiliary functions from Lua API +** See Copyright Notice in lua.h +*/ + +#ifndef lapi_h +#define lapi_h + + +#include "llimits.h" +#include "lstate.h" + +#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \ + "stack overflow");} + +#define adjustresults(L,nres) \ + { if ((nres) == LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } + +#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ + "not enough elements in the stack") + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lauxlib.c b/usr/src/uts/common/fs/zfs/lua/lauxlib.c new file mode 100644 index 0000000000..4bd13788b4 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lauxlib.c @@ -0,0 +1,791 @@ +/* +** $Id: lauxlib.c,v 1.248.1.1 2013/04/12 18:48:47 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +/* This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#define lauxlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" + + +/* +** {====================================================== +** Traceback +** ======================================================= +*/ + + +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ + + + +/* +** search for 'objidx' in table at index -1. +** return 1 + string at top if find a good name. +*/ +static int findfield (lua_State *L, int objidx, int level) { + if (level == 0 || !lua_istable(L, -1)) + return 0; /* not found */ + lua_pushnil(L); /* start 'next' loop */ + while (lua_next(L, -2)) { /* for each pair in table */ + if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ + if (lua_rawequal(L, objidx, -1)) { /* found object? */ + lua_pop(L, 1); /* remove value (but keep name) */ + return 1; + } + else if (findfield(L, objidx, level - 1)) { /* try recursively */ + lua_remove(L, -2); /* remove table (but keep name) */ + lua_pushliteral(L, "."); + lua_insert(L, -2); /* place '.' between the two names */ + lua_concat(L, 3); + return 1; + } + } + lua_pop(L, 1); /* remove value */ + } + return 0; /* not found */ +} + + +static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { + int top = lua_gettop(L); + lua_getinfo(L, "f", ar); /* push function */ + lua_pushglobaltable(L); + if (findfield(L, top + 1, 2)) { + lua_copy(L, -1, top + 1); /* move name to proper place */ + lua_pop(L, 2); /* remove pushed values */ + return 1; + } + else { + lua_settop(L, top); /* remove function and global table */ + return 0; + } +} + + +static void pushfuncname (lua_State *L, lua_Debug *ar) { + if (*ar->namewhat != '\0') /* is there a name? */ + lua_pushfstring(L, "function " LUA_QS, ar->name); + else if (*ar->what == 'm') /* main? */ + lua_pushliteral(L, "main chunk"); + else if (*ar->what == 'C') { + if (pushglobalfuncname(L, ar)) { + lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } + else + lua_pushliteral(L, "?"); + } + else + lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); +} + + +static int countlevels (lua_State *L) { + lua_Debug ar; + int li = 1, le = 1; + /* find an upper bound */ + while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } + /* do a binary search */ + while (li < le) { + int m = (li + le)/2; + if (lua_getstack(L, m, &ar)) li = m + 1; + else le = m; + } + return le - 1; +} + + +LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, + const char *msg, int level) { + lua_Debug ar; + int top = lua_gettop(L); + int numlevels = countlevels(L1); + int mark = (numlevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0; + if (msg) lua_pushfstring(L, "%s\n", msg); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (level == mark) { /* too many levels? */ + lua_pushliteral(L, "\n\t..."); /* add a '...' */ + level = numlevels - LEVELS2; /* and skip to last ones */ + } + else { + lua_getinfo(L1, "Slnt", &ar); + lua_pushfstring(L, "\n\t%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + lua_pushliteral(L, " in "); + pushfuncname(L, &ar); + if (ar.istailcall) + lua_pushliteral(L, "\n\t(...tail calls...)"); + lua_concat(L, lua_gettop(L) - top); + } + } + lua_concat(L, lua_gettop(L) - top); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + +LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { + lua_Debug ar; + if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ + return luaL_error(L, "bad argument #%d (%s)", narg, extramsg); + lua_getinfo(L, "n", &ar); + if (strcmp(ar.namewhat, "method") == 0) { + narg--; /* do not count `self' */ + if (narg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling " LUA_QS " on bad self (%s)", + ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; + return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)", + narg, ar.name, extramsg); +} + + +static int typeerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} + + +static void tag_error (lua_State *L, int narg, int tag) { + typeerror(L, narg, lua_typename(L, tag)); +} + + +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Sl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushliteral(L, ""); /* else, no information available... */ +} + + +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + + +#if !defined(inspectstat) /* { */ + +#if defined(LUA_USE_POSIX) + +#include <sys/wait.h> + +/* +** use appropriate macros to interpret 'pclose' return status +*/ +#define inspectstat(stat,what) \ + if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ + else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } + +#else + +#define inspectstat(stat,what) /* no op */ + +#endif + +#endif /* } */ + + +/* }====================================================== */ + + +/* +** {====================================================== +** Userdata's metatable manipulation +** ======================================================= +*/ + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + luaL_getmetatable(L, tname); /* try to get metatable */ + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_newtable(L); /* create metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ + return 1; +} + + +LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) { + luaL_getmetatable(L, tname); + lua_setmetatable(L, -2); +} + + +LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + luaL_getmetatable(L, tname); /* get correct metatable */ + if (!lua_rawequal(L, -1, -2)) /* not the same? */ + p = NULL; /* value is a userdata with wrong metatable */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + return NULL; /* value is not a userdata with a metatable */ +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + void *p = luaL_testudata(L, ud, tname); + if (p == NULL) typeerror(L, ud, tname); + return p; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Argument check functions +** ======================================================= +*/ + +LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def, + const char *const lst[]) { + const char *name = (def) ? luaL_optstring(L, narg, def) : + luaL_checkstring(L, narg); + int i; + for (i=0; lst[i]; i++) + if (strcmp(lst[i], name) == 0) + return i; + return luaL_argerror(L, narg, + lua_pushfstring(L, "invalid option " LUA_QS, name)); +} + + +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { + /* keep some extra space to run error routines, if needed */ + const int extra = LUA_MINSTACK; + if (!lua_checkstack(L, space + extra)) { + if (msg) + luaL_error(L, "stack overflow (%s)", msg); + else + luaL_error(L, "stack overflow"); + } +} + + +LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { + if (lua_type(L, narg) != t) + tag_error(L, narg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int narg) { + if (lua_type(L, narg) == LUA_TNONE) + luaL_argerror(L, narg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { + const char *s = lua_tolstring(L, narg, len); + if (!s) tag_error(L, narg, LUA_TSTRING); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, narg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, narg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { + int isnum; + lua_Number d = lua_tonumberx(L, narg, &isnum); + if (!isnum) + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, narg, def); +} + + +LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) { + int isnum; + lua_Integer d = lua_tointegerx(L, narg, &isnum); + if (!isnum) + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Unsigned luaL_checkunsigned (lua_State *L, int narg) { + int isnum; + lua_Unsigned d = lua_tounsignedx(L, narg, &isnum); + if (!isnum) + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg, + lua_Integer def) { + return luaL_opt(L, luaL_checkinteger, narg, def); +} + + +LUALIB_API lua_Unsigned luaL_optunsigned (lua_State *L, int narg, + lua_Unsigned def) { + return luaL_opt(L, luaL_checkunsigned, narg, def); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +/* +** check whether buffer is using a userdata on the stack as a temporary +** buffer +*/ +#define buffonstack(B) ((B)->b != (B)->initb) + + +/* +** returns a pointer to a free area with at least 'sz' bytes +*/ +LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { + lua_State *L = B->L; + if (B->size - B->n < sz) { /* not enough space? */ + char *newbuff; + size_t newsize = B->size * 2; /* double buffer size */ + if (newsize - B->n < sz) /* not big enough? */ + newsize = B->n + sz; + if (newsize < B->n || newsize - B->n < sz) + luaL_error(L, "buffer too large"); + /* create larger buffer */ + newbuff = (char *)lua_newuserdata(L, newsize * sizeof(char)); + /* move content to new buffer */ + memcpy(newbuff, B->b, B->n * sizeof(char)); + if (buffonstack(B)) + lua_remove(L, -2); /* remove old buffer */ + B->b = newbuff; + B->size = newsize; + } + return &B->b[B->n]; +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + char *b = luaL_prepbuffsize(B, l); + memcpy(b, s, l * sizeof(char)); + luaL_addsize(B, l); +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + lua_State *L = B->L; + lua_pushlstring(L, B->b, B->n); + if (buffonstack(B)) + lua_remove(L, -2); /* remove old buffer */ +} + + +LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) { + luaL_addsize(B, sz); + luaL_pushresult(B); +} + + +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t l; + const char *s = lua_tolstring(L, -1, &l); + if (buffonstack(B)) + lua_insert(L, -2); /* put value below buffer */ + luaL_addlstring(B, s, l); + lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */ +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->b = B->initb; + B->n = 0; + B->size = LUAL_BUFFERSIZE; +} + + +LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { + luaL_buffinit(L, B); + return luaL_prepbuffsize(B, sz); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Reference system +** ======================================================= +*/ + +/* index of free-list header */ +#define freelist 0 + + +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* `nil' has a unique fixed reference */ + } + t = lua_absindex(L, t); + lua_rawgeti(L, t, freelist); /* get first free element */ + ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */ + } + else /* no free elements */ + ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = lua_absindex(L, t); + lua_rawgeti(L, t, freelist); + lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */ + lua_pushinteger(L, ref); + lua_rawseti(L, t, freelist); /* t[freelist] = ref */ + } +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; /* not used */ + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size, + const char *name, const char *mode) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name, mode); +} + + +LUALIB_API int luaL_loadstring (lua_State *L, const char *s) { + return luaL_loadbuffer(L, s, strlen(s), s); +} + +/* }====================================================== */ + + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return 0; + lua_pushstring(L, event); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); /* remove metatable and metafield */ + return 0; + } + else { + lua_remove(L, -2); /* remove only metatable */ + return 1; + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = lua_absindex(L, obj); + if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API int luaL_len (lua_State *L, int idx) { + int l; + int isnum; + lua_len(L, idx); + l = (int)lua_tointegerx(L, -1, &isnum); + if (!isnum) + luaL_error(L, "object length is not a number"); + lua_pop(L, 1); /* remove object */ + return l; +} + + +LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { + if (!luaL_callmeta(L, idx, "__tostring")) { /* no metafield? */ + switch (lua_type(L, idx)) { + case LUA_TNUMBER: + case LUA_TSTRING: + lua_pushvalue(L, idx); + break; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false")); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + default: + lua_pushfstring(L, "%s: %p", luaL_typename(L, idx), + lua_topointer(L, idx)); + break; + } + } + return lua_tolstring(L, -1, len); +} + + +/* +** {====================================================== +** Compatibility with 5.1 module functions +** ======================================================= +*/ +#if defined(LUA_COMPAT_MODULE) + +static const char *luaL_findtable (lua_State *L, int idx, + const char *fname, int szhint) { + const char *e; + if (idx) lua_pushvalue(L, idx); + do { + e = strchr(fname, '.'); + if (e == NULL) e = fname + strlen(fname); + lua_pushlstring(L, fname, e - fname); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { /* no such field? */ + lua_pop(L, 1); /* remove this nil */ + lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ + lua_pushlstring(L, fname, e - fname); + lua_pushvalue(L, -2); + lua_settable(L, -4); /* set new table into field */ + } + else if (!lua_istable(L, -1)) { /* field has a non-table value? */ + lua_pop(L, 2); /* remove table and value */ + return fname; /* return problematic part of the name */ + } + lua_remove(L, -2); /* remove previous table */ + fname = e + 1; + } while (*e == '.'); + return NULL; +} + + +/* +** Count number of elements in a luaL_Reg list. +*/ +static int libsize (const luaL_Reg *l) { + int size = 0; + for (; l && l->name; l++) size++; + return size; +} + + +/* +** Find or create a module table with a given name. The function +** first looks at the _LOADED table and, if that fails, try a +** global variable with that name. In any case, leaves on the stack +** the module table. +*/ +LUALIB_API void luaL_pushmodule (lua_State *L, const char *modname, + int sizehint) { + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); /* get _LOADED table */ + lua_getfield(L, -1, modname); /* get _LOADED[modname] */ + if (!lua_istable(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + /* try global variable (and create one if it does not exist) */ + lua_pushglobaltable(L); + if (luaL_findtable(L, 0, modname, sizehint) != NULL) + luaL_error(L, "name conflict for module " LUA_QS, modname); + lua_pushvalue(L, -1); + lua_setfield(L, -3, modname); /* _LOADED[modname] = new table */ + } + lua_remove(L, -2); /* remove _LOADED table */ +} + + +LUALIB_API void luaL_openlib (lua_State *L, const char *libname, + const luaL_Reg *l, int nup) { + luaL_checkversion(L); + if (libname) { + luaL_pushmodule(L, libname, libsize(l)); /* get/create library table */ + lua_insert(L, -(nup + 1)); /* move library table to below upvalues */ + } + if (l) + luaL_setfuncs(L, l, nup); + else + lua_pop(L, nup); /* remove upvalues */ +} + +#endif +/* }====================================================== */ + +/* +** set functions from list 'l' into table at top - 'nup'; each +** function gets the 'nup' elements at the top as upvalues. +** Returns with only the table at the stack. +*/ +LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkversion(L); + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + + +/* +** ensure that stack[idx][fname] has a table and push that table +** into the stack +*/ +LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) { + lua_getfield(L, idx, fname); + if (lua_istable(L, -1)) return 1; /* table already there */ + else { + lua_pop(L, 1); /* remove previous result */ + idx = lua_absindex(L, idx); + lua_newtable(L); + lua_pushvalue(L, -1); /* copy to be left at top */ + lua_setfield(L, idx, fname); /* assign new table to field */ + return 0; /* false, because did not find table there */ + } +} + + +/* +** stripped-down 'require'. Calls 'openf' to open a module, +** registers the result in 'package.loaded' table and, if 'glb' +** is true, also registers the result in the global table. +** Leaves resulting module on the top. +*/ +LUALIB_API void luaL_requiref (lua_State *L, const char *modname, + lua_CFunction openf, int glb) { + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); /* argument to open function */ + lua_call(L, 1, 1); /* open module */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_pushvalue(L, -2); /* make copy of module (call result) */ + lua_setfield(L, -2, modname); /* _LOADED[modname] = module */ + lua_pop(L, 1); /* remove _LOADED table */ + if (glb) { + lua_pushvalue(L, -1); /* copy of 'mod' */ + lua_setglobal(L, modname); /* _G[modname] = module */ + } +} + + +LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, + const char *r) { + const char *wild; + size_t l = strlen(p); + luaL_Buffer b; + luaL_buffinit(L, &b); + while ((wild = strstr(s, p)) != NULL) { + luaL_addlstring(&b, s, wild - s); /* push prefix */ + luaL_addstring(&b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after `p' */ + } + luaL_addstring(&b, s); /* push last suffix */ + luaL_pushresult(&b); + return lua_tostring(L, -1); +} + + +LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver) { + const lua_Number *v = lua_version(L); + if (v != lua_version(NULL)) + luaL_error(L, "multiple Lua VMs detected"); + else if (*v != ver) + luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", + ver, *v); + /* check conversions number -> integer types */ + lua_pushnumber(L, -(lua_Number)0x1234); + if (lua_tointeger(L, -1) != -0x1234 || + lua_tounsigned(L, -1) != (lua_Unsigned)-0x1234) + luaL_error(L, "bad conversion number->int;" + " must recompile Lua with proper settings"); + lua_pop(L, 1); +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lauxlib.h b/usr/src/uts/common/fs/zfs/lua/lauxlib.h new file mode 100644 index 0000000000..f6fdac14f5 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lauxlib.h @@ -0,0 +1,176 @@ +/* +** $Id: lauxlib.h,v 1.120.1.1 2013/04/12 18:48:47 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include <sys/zfs_context.h> + +#include "lua.h" + + + +/* extra error code for `luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + +LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver); +#define luaL_checkversion(L) luaL_checkversion_(L, LUA_VERSION_NUM) + +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); +LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, + lua_Integer def); +LUALIB_API lua_Unsigned (luaL_checkunsigned) (lua_State *L, int numArg); +LUALIB_API lua_Unsigned (luaL_optunsigned) (lua_State *L, int numArg, + lua_Unsigned def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int narg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, + const char *const lst[]); + +/* pre-defined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, + const char *name, const char *mode); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API int (luaL_len) (lua_State *L, int idx); + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); + +LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname); + +LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1, + const char *msg, int level); + +LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, + lua_CFunction openf, int glb); + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + + +#define luaL_newlibtable(L,l) \ + lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) + +#define luaL_newlib(L,l) (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) + +#define luaL_argcheck(L, cond,numarg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +typedef struct luaL_Buffer { + char *b; /* buffer address */ + size_t size; /* buffer size */ + size_t n; /* number of characters in buffer */ + lua_State *L; + char initb[LUAL_BUFFERSIZE]; /* initial buffer */ +} luaL_Buffer; + + +#define luaL_addchar(B,c) \ + ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ + ((B)->b[(B)->n++] = (c))) + +#define luaL_addsize(B,s) ((B)->n += (s)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz); +LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); + +#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) + +/* }====================================================== */ + + +/* compatibility with old module system */ +#if defined(LUA_COMPAT_MODULE) + +LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname, + int sizehint); +LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup); + +#define luaL_register(L,n,l) (luaL_openlib(L,(n),(l),0)) + +#endif + + +#endif + + diff --git a/usr/src/uts/common/fs/zfs/lua/lbaselib.c b/usr/src/uts/common/fs/zfs/lua/lbaselib.c new file mode 100644 index 0000000000..ecd1a763d6 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lbaselib.c @@ -0,0 +1,292 @@ +/* +** $Id: lbaselib.c,v 1.276.1.1 2013/04/12 18:48:47 roberto Exp $ +** Basic library +** See Copyright Notice in lua.h +*/ + +/* The following built-in lua functions have been removed and are not available + * for use in ZFS channel programs: + * + * dofile + * loadfile + * load + * pcall + * print + * xpcall + */ + +#include <sys/zfs_context.h> +#include <sys/ctype.h> +#define toupper(C) (((C) >= 'a' && (C) <= 'z')? (C) - 'a' + 'A': (C)) + +#define lbaselib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + +#define SPACECHARS " \f\n\r\t\v" + +static int luaB_tonumber (lua_State *L) { + if (lua_isnoneornil(L, 2)) { /* standard conversion */ + int isnum; + lua_Number n = lua_tonumberx(L, 1, &isnum); + if (isnum) { + lua_pushnumber(L, n); + return 1; + } /* else not a number; must be something */ + luaL_checkany(L, 1); + } + else { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + const char *e = s + l; /* end point for 's' */ + int base = luaL_checkint(L, 2); + int neg = 0; + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + s += strspn(s, SPACECHARS); /* skip initial spaces */ + if (*s == '-') { s++; neg = 1; } /* handle signal */ + else if (*s == '+') s++; + if (isalnum((unsigned char)*s)) { + lua_Number n = 0; + do { + int digit = (isdigit((unsigned char)*s)) ? *s - '0' + : toupper((unsigned char)*s) - 'A' + 10; + if (digit >= base) break; /* invalid numeral; force a fail */ + n = n * (lua_Number)base + (lua_Number)digit; + s++; + } while (isalnum((unsigned char)*s)); + s += strspn(s, SPACECHARS); /* skip trailing spaces */ + if (s == e) { /* no invalid trailing characters? */ + lua_pushnumber(L, (neg) ? -n : n); + return 1; + } /* else not a number */ + } /* else not a number */ + } + lua_pushnil(L); /* not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = luaL_optint(L, 2, 1); + lua_settop(L, 1); + if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ + luaL_where(L, level); + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + if (luaL_getmetafield(L, 1, "__metatable")) + return luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawlen (lua_State *L) { + int t = lua_type(L, 1); + luaL_argcheck(L, t == LUA_TTABLE || t == LUA_TSTRING, 1, + "table or string expected"); + lua_pushinteger(L, lua_rawlen(L, 1)); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_settop(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int luaB_collectgarbage (lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "setpause", "setstepmul", + "setmajorinc", "isrunning", "generational", "incremental", NULL}; + static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, + LUA_GCSETMAJORINC, LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC}; + int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; + int ex = luaL_optint(L, 2, 0); + int res = lua_gc(L, o, ex); + switch (o) { + case LUA_GCCOUNT: { + int b = lua_gc(L, LUA_GCCOUNTB, 0); + lua_pushnumber(L, res + ((lua_Number)b/1024)); + lua_pushinteger(L, b); + return 2; + } + case LUA_GCSTEP: case LUA_GCISRUNNING: { + lua_pushboolean(L, res); + return 1; + } + default: { + lua_pushinteger(L, res); + return 1; + } + } +} + + +static int luaB_type (lua_State *L) { + luaL_checkany(L, 1); + lua_pushstring(L, luaL_typename(L, 1)); + return 1; +} + + +static int pairsmeta (lua_State *L, const char *method, int iszero, + lua_CFunction iter) { + if (!luaL_getmetafield(L, 1, method)) { /* no metamethod? */ + luaL_checktype(L, 1, LUA_TTABLE); /* argument must be a table */ + lua_pushcfunction(L, iter); /* will return generator, */ + lua_pushvalue(L, 1); /* state, */ + if (iszero) lua_pushinteger(L, 0); /* and initial value */ + else lua_pushnil(L); + } + else { + lua_pushvalue(L, 1); /* argument 'self' to metamethod */ + lua_call(L, 1, 3); /* get 3 values from metamethod */ + } + return 3; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int luaB_pairs (lua_State *L) { + return pairsmeta(L, "__pairs", 0, luaB_next); +} + + +static int ipairsaux (lua_State *L) { + int i = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + i++; /* next value */ + lua_pushinteger(L, i); + lua_rawgeti(L, 1, i); + return (lua_isnil(L, -1)) ? 1 : 2; +} + + +static int luaB_ipairs (lua_State *L) { + return pairsmeta(L, "__ipairs", 1, ipairsaux); +} + + +static int luaB_assert (lua_State *L) { + if (!lua_toboolean(L, 1)) + return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); + return lua_gettop(L); +} + + +static int luaB_select (lua_State *L) { + int n = lua_gettop(L); + if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { + lua_pushinteger(L, n-1); + return 1; + } + else { + int i = luaL_checkint(L, 1); + if (i < 0) i = n + i; + else if (i > n) i = n; + luaL_argcheck(L, 1 <= i, 1, "index out of range"); + return n - i; + } +} + +static int luaB_tostring (lua_State *L) { + luaL_checkany(L, 1); + luaL_tolstring(L, 1, NULL); + return 1; +} + +static const luaL_Reg base_funcs[] = { + {"assert", luaB_assert}, + {"collectgarbage", luaB_collectgarbage}, + {"error", luaB_error}, + {"getmetatable", luaB_getmetatable}, + {"ipairs", luaB_ipairs}, +#if defined(LUA_COMPAT_LOADSTRING) + {"loadstring", luaB_load}, +#endif + {"next", luaB_next}, + {"pairs", luaB_pairs}, + {"rawequal", luaB_rawequal}, + {"rawlen", luaB_rawlen}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"select", luaB_select}, + {"setmetatable", luaB_setmetatable}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_base (lua_State *L) { + /* set global _G */ + lua_pushglobaltable(L); + lua_pushglobaltable(L); + lua_setfield(L, -2, "_G"); + /* open lib into global table */ + luaL_setfuncs(L, base_funcs, 0); + lua_pushliteral(L, LUA_VERSION); + lua_setfield(L, -2, "_VERSION"); /* set global _VERSION */ + return 1; +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lbitlib.c b/usr/src/uts/common/fs/zfs/lua/lbitlib.c new file mode 100644 index 0000000000..31c7b66f12 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lbitlib.c @@ -0,0 +1,212 @@ +/* +** $Id: lbitlib.c,v 1.18.1.2 2013/07/09 18:01:41 roberto Exp $ +** Standard library for bitwise operations +** See Copyright Notice in lua.h +*/ + +#define lbitlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* number of bits to consider in a number */ +#if !defined(LUA_NBITS) +#define LUA_NBITS 32 +#endif + + +#define ALLONES (~(((~(lua_Unsigned)0) << (LUA_NBITS - 1)) << 1)) + +/* macro to trim extra bits */ +#define trim(x) ((x) & ALLONES) + + +/* builds a number with 'n' ones (1 <= n <= LUA_NBITS) */ +#define mask(n) (~((ALLONES << 1) << ((n) - 1))) + + +typedef lua_Unsigned b_uint; + + + +static b_uint andaux (lua_State *L) { + int i, n = lua_gettop(L); + b_uint r = ~(b_uint)0; + for (i = 1; i <= n; i++) + r &= luaL_checkunsigned(L, i); + return trim(r); +} + + +static int b_and (lua_State *L) { + b_uint r = andaux(L); + lua_pushunsigned(L, r); + return 1; +} + + +static int b_test (lua_State *L) { + b_uint r = andaux(L); + lua_pushboolean(L, r != 0); + return 1; +} + + +static int b_or (lua_State *L) { + int i, n = lua_gettop(L); + b_uint r = 0; + for (i = 1; i <= n; i++) + r |= luaL_checkunsigned(L, i); + lua_pushunsigned(L, trim(r)); + return 1; +} + + +static int b_xor (lua_State *L) { + int i, n = lua_gettop(L); + b_uint r = 0; + for (i = 1; i <= n; i++) + r ^= luaL_checkunsigned(L, i); + lua_pushunsigned(L, trim(r)); + return 1; +} + + +static int b_not (lua_State *L) { + b_uint r = ~luaL_checkunsigned(L, 1); + lua_pushunsigned(L, trim(r)); + return 1; +} + + +static int b_shift (lua_State *L, b_uint r, int i) { + if (i < 0) { /* shift right? */ + i = -i; + r = trim(r); + if (i >= LUA_NBITS) r = 0; + else r >>= i; + } + else { /* shift left */ + if (i >= LUA_NBITS) r = 0; + else r <<= i; + r = trim(r); + } + lua_pushunsigned(L, r); + return 1; +} + + +static int b_lshift (lua_State *L) { + return b_shift(L, luaL_checkunsigned(L, 1), luaL_checkint(L, 2)); +} + + +static int b_rshift (lua_State *L) { + return b_shift(L, luaL_checkunsigned(L, 1), -luaL_checkint(L, 2)); +} + + +static int b_arshift (lua_State *L) { + b_uint r = luaL_checkunsigned(L, 1); + int i = luaL_checkint(L, 2); + if (i < 0 || !(r & ((b_uint)1 << (LUA_NBITS - 1)))) + return b_shift(L, r, -i); + else { /* arithmetic shift for 'negative' number */ + if (i >= LUA_NBITS) r = ALLONES; + else + r = trim((r >> i) | ~(~(b_uint)0 >> i)); /* add signal bit */ + lua_pushunsigned(L, r); + return 1; + } +} + + +static int b_rot (lua_State *L, int i) { + b_uint r = luaL_checkunsigned(L, 1); + i &= (LUA_NBITS - 1); /* i = i % NBITS */ + r = trim(r); + if (i != 0) /* avoid undefined shift of LUA_NBITS when i == 0 */ + r = (r << i) | (r >> (LUA_NBITS - i)); + lua_pushunsigned(L, trim(r)); + return 1; +} + + +static int b_lrot (lua_State *L) { + return b_rot(L, luaL_checkint(L, 2)); +} + + +static int b_rrot (lua_State *L) { + return b_rot(L, -luaL_checkint(L, 2)); +} + + +/* +** get field and width arguments for field-manipulation functions, +** checking whether they are valid. +** ('luaL_error' called without 'return' to avoid later warnings about +** 'width' being used uninitialized.) +*/ +static int fieldargs (lua_State *L, int farg, int *width) { + int f = luaL_checkint(L, farg); + int w = luaL_optint(L, farg + 1, 1); + luaL_argcheck(L, 0 <= f, farg, "field cannot be negative"); + luaL_argcheck(L, 0 < w, farg + 1, "width must be positive"); + if (f + w > LUA_NBITS) + luaL_error(L, "trying to access non-existent bits"); + *width = w; + return f; +} + + +static int b_extract (lua_State *L) { + int w; + b_uint r = luaL_checkunsigned(L, 1); + int f = fieldargs(L, 2, &w); + r = (r >> f) & mask(w); + lua_pushunsigned(L, r); + return 1; +} + + +static int b_replace (lua_State *L) { + int w; + b_uint r = luaL_checkunsigned(L, 1); + b_uint v = luaL_checkunsigned(L, 2); + int f = fieldargs(L, 3, &w); + int m = mask(w); + v &= m; /* erase bits outside given width */ + r = (r & ~(m << f)) | (v << f); + lua_pushunsigned(L, r); + return 1; +} + + +static const luaL_Reg bitlib[] = { + {"arshift", b_arshift}, + {"band", b_and}, + {"bnot", b_not}, + {"bor", b_or}, + {"bxor", b_xor}, + {"btest", b_test}, + {"extract", b_extract}, + {"lrotate", b_lrot}, + {"lshift", b_lshift}, + {"replace", b_replace}, + {"rrotate", b_rrot}, + {"rshift", b_rshift}, + {NULL, NULL} +}; + + + +LUAMOD_API int luaopen_bit32 (lua_State *L) { + luaL_newlib(L, bitlib); + return 1; +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lcode.c b/usr/src/uts/common/fs/zfs/lua/lcode.c new file mode 100644 index 0000000000..f155014d12 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lcode.c @@ -0,0 +1,885 @@ +/* +** $Id: lcode.c,v 2.62.1.1 2013/04/12 18:48:47 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#include <sys/zfs_context.h> + +#define lcode_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstring.h" +#include "ltable.h" +#include "lvm.h" + + +#define hasjumps(e) ((e)->t != (e)->f) + + +static int isnumeral(expdesc *e) { + return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); +} + + +void luaK_nil (FuncState *fs, int from, int n) { + Instruction *previous; + int l = from + n - 1; /* last register to set nil */ + if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ + previous = &fs->f->code[fs->pc-1]; + if (GET_OPCODE(*previous) == OP_LOADNIL) { + int pfrom = GETARG_A(*previous); + int pl = pfrom + GETARG_B(*previous); + if ((pfrom <= from && from <= pl + 1) || + (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ + if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ + if (pl > l) l = pl; /* l = max(l, pl) */ + SETARG_A(*previous, from); + SETARG_B(*previous, l - from); + return; + } + } /* else go through */ + } + luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ +} + + +int luaK_jump (FuncState *fs) { + int jpc = fs->jpc; /* save list of jumps to here */ + int j; + fs->jpc = NO_JUMP; + j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); + luaK_concat(fs, &j, jpc); /* keep them on hold */ + return j; +} + + +void luaK_ret (FuncState *fs, int first, int nret) { + luaK_codeABC(fs, OP_RETURN, first, nret+1, 0); +} + + +static int condjump (FuncState *fs, OpCode op, int A, int B, int C) { + luaK_codeABC(fs, op, A, B, C); + return luaK_jump(fs); +} + + +static void fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest-(pc+1); + lua_assert(dest != NO_JUMP); + if (abs(offset) > MAXARG_sBx) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_sBx(*jmp, offset); +} + + +/* +** returns current `pc' and marks it as a jump target (to avoid wrong +** optimizations with consecutive instructions not in the same basic block). +*/ +int luaK_getlabel (FuncState *fs) { + fs->lasttarget = fs->pc; + return fs->pc; +} + + +static int getjump (FuncState *fs, int pc) { + int offset = GETARG_sBx(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +static Instruction *getjumpcontrol (FuncState *fs, int pc) { + Instruction *pi = &fs->f->code[pc]; + if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) + return pi-1; + else + return pi; +} + + +/* +** check whether list has any jump that do not produce a value +** (or produce an inverted value) +*/ +static int need_value (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +static int patchtestreg (FuncState *fs, int node, int reg) { + Instruction *i = getjumpcontrol(fs, node); + if (GET_OPCODE(*i) != OP_TESTSET) + return 0; /* cannot patch other instructions */ + if (reg != NO_REG && reg != GETARG_B(*i)) + SETARG_A(*i, reg); + else /* no register to put value or register already has the value */ + *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); + + return 1; +} + + +static void removevalues (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) + patchtestreg(fs, list, NO_REG); +} + + +static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, + int dtarget) { + while (list != NO_JUMP) { + int next = getjump(fs, list); + if (patchtestreg(fs, list, reg)) + fixjump(fs, list, vtarget); + else + fixjump(fs, list, dtarget); /* jump to default target */ + list = next; + } +} + + +static void dischargejpc (FuncState *fs) { + patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); + fs->jpc = NO_JUMP; +} + + +void luaK_patchlist (FuncState *fs, int list, int target) { + if (target == fs->pc) + luaK_patchtohere(fs, list); + else { + lua_assert(target < fs->pc); + patchlistaux(fs, list, target, NO_REG, target); + } +} + + +LUAI_FUNC void luaK_patchclose (FuncState *fs, int list, int level) { + level++; /* argument is +1 to reserve 0 as non-op */ + while (list != NO_JUMP) { + int next = getjump(fs, list); + lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP && + (GETARG_A(fs->f->code[list]) == 0 || + GETARG_A(fs->f->code[list]) >= level)); + SETARG_A(fs->f->code[list], level); + list = next; + } +} + + +void luaK_patchtohere (FuncState *fs, int list) { + luaK_getlabel(fs); + luaK_concat(fs, &fs->jpc, list); +} + + +void luaK_concat (FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; + else if (*l1 == NO_JUMP) + *l1 = l2; + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); + } +} + + +static int luaK_code (FuncState *fs, Instruction i) { + Proto *f = fs->f; + dischargejpc(fs); /* `pc' will change */ + /* put new instruction in code array */ + luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, + MAX_INT, "opcodes"); + f->code[fs->pc] = i; + /* save corresponding line information */ + luaM_growvector(fs->ls->L, f->lineinfo, fs->pc, f->sizelineinfo, int, + MAX_INT, "opcodes"); + f->lineinfo[fs->pc] = fs->ls->lastline; + return fs->pc++; +} + + +int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { + lua_assert(getOpMode(o) == iABC); + lua_assert(getBMode(o) != OpArgN || b == 0); + lua_assert(getCMode(o) != OpArgN || c == 0); + lua_assert(a <= MAXARG_A && b <= MAXARG_B && c <= MAXARG_C); + return luaK_code(fs, CREATE_ABC(o, a, b, c)); +} + + +int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { + lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); + lua_assert(getCMode(o) == OpArgN); + lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, a, bc)); +} + + +static int codeextraarg (FuncState *fs, int a) { + lua_assert(a <= MAXARG_Ax); + return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a)); +} + + +int luaK_codek (FuncState *fs, int reg, int k) { + if (k <= MAXARG_Bx) + return luaK_codeABx(fs, OP_LOADK, reg, k); + else { + int p = luaK_codeABx(fs, OP_LOADKX, reg, 0); + codeextraarg(fs, k); + return p; + } +} + + +void luaK_checkstack (FuncState *fs, int n) { + int newstack = fs->freereg + n; + if (newstack > fs->f->maxstacksize) { + if (newstack >= MAXSTACK) + luaX_syntaxerror(fs->ls, "function or expression too complex"); + fs->f->maxstacksize = cast_byte(newstack); + } +} + + +void luaK_reserveregs (FuncState *fs, int n) { + luaK_checkstack(fs, n); + fs->freereg += n; +} + + +static void freereg (FuncState *fs, int reg) { + if (!ISK(reg) && reg >= fs->nactvar) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + + +static void freeexp (FuncState *fs, expdesc *e) { + if (e->k == VNONRELOC) + freereg(fs, e->u.info); +} + + +static int addk (FuncState *fs, TValue *key, TValue *v) { + lua_State *L = fs->ls->L; + TValue *idx = luaH_set(L, fs->h, key); + Proto *f = fs->f; + int k, oldsize; + if (ttisnumber(idx)) { + lua_Number n = nvalue(idx); + lua_number2int(k, n); + if (luaV_rawequalobj(&f->k[k], v)) + return k; + /* else may be a collision (e.g., between 0.0 and "\0\0\0\0\0\0\0\0"); + go through and create a new entry for this value */ + } + /* constant not found; create a new entry */ + oldsize = f->sizek; + k = fs->nk; + /* numerical value does not need GC barrier; + table has no metatable, so it does not need to invalidate cache */ + setnvalue(idx, cast_num(k)); + luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); + while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); + setobj(L, &f->k[k], v); + fs->nk++; + luaC_barrier(L, f, v); + return k; +} + + +int luaK_stringK (FuncState *fs, TString *s) { + TValue o; + setsvalue(fs->ls->L, &o, s); + return addk(fs, &o, &o); +} + + +int luaK_numberK (FuncState *fs, lua_Number r) { + int n; + lua_State *L = fs->ls->L; + TValue o; + setnvalue(&o, r); + if (r == 0 || luai_numisnan(NULL, r)) { /* handle -0 and NaN */ + /* use raw representation as key to avoid numeric problems */ + setsvalue(L, L->top++, luaS_newlstr(L, (char *)&r, sizeof(r))); + n = addk(fs, L->top - 1, &o); + L->top--; + } + else + n = addk(fs, &o, &o); /* regular case */ + return n; +} + + +static int boolK (FuncState *fs, int b) { + TValue o; + setbvalue(&o, b); + return addk(fs, &o, &o); +} + + +static int nilK (FuncState *fs) { + TValue k, v; + setnilvalue(&v); + /* cannot use nil as key; instead use table itself to represent nil */ + sethvalue(fs->ls->L, &k, fs->h); + return addk(fs, &k, &v); +} + + +void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { + if (e->k == VCALL) { /* expression is an open function call? */ + SETARG_C(getcode(fs, e), nresults+1); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), nresults+1); + SETARG_A(getcode(fs, e), fs->freereg); + luaK_reserveregs(fs, 1); + } +} + + +void luaK_setoneret (FuncState *fs, expdesc *e) { + if (e->k == VCALL) { /* expression is an open function call? */ + e->k = VNONRELOC; + e->u.info = GETARG_A(getcode(fs, e)); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), 2); + e->k = VRELOCABLE; /* can relocate its simple result */ + } +} + + +void luaK_dischargevars (FuncState *fs, expdesc *e) { + switch (e->k) { + case VLOCAL: { + e->k = VNONRELOC; + break; + } + case VUPVAL: { + e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); + e->k = VRELOCABLE; + break; + } + case VINDEXED: { + OpCode op = OP_GETTABUP; /* assume 't' is in an upvalue */ + freereg(fs, e->u.ind.idx); + if (e->u.ind.vt == VLOCAL) { /* 't' is in a register? */ + freereg(fs, e->u.ind.t); + op = OP_GETTABLE; + } + e->u.info = luaK_codeABC(fs, op, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOCABLE; + break; + } + case VVARARG: + case VCALL: { + luaK_setoneret(fs, e); + break; + } + default: break; /* there is one value available (somewhere) */ + } +} + + +static int code_label (FuncState *fs, int A, int b, int jump) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); +} + + +static void discharge2reg (FuncState *fs, expdesc *e, int reg) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: { + luaK_nil(fs, reg, 1); + break; + } + case VFALSE: case VTRUE: { + luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); + break; + } + case VK: { + luaK_codek(fs, reg, e->u.info); + break; + } + case VKNUM: { + luaK_codek(fs, reg, luaK_numberK(fs, e->u.nval)); + break; + } + case VRELOCABLE: { + Instruction *pc = &getcode(fs, e); + SETARG_A(*pc, reg); + break; + } + case VNONRELOC: { + if (reg != e->u.info) + luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); + break; + } + default: { + lua_assert(e->k == VVOID || e->k == VJMP); + return; /* nothing to do... */ + } + } + e->u.info = reg; + e->k = VNONRELOC; +} + + +static void discharge2anyreg (FuncState *fs, expdesc *e) { + if (e->k != VNONRELOC) { + luaK_reserveregs(fs, 1); + discharge2reg(fs, e, fs->freereg-1); + } +} + + +static void exp2reg (FuncState *fs, expdesc *e, int reg) { + discharge2reg(fs, e, reg); + if (e->k == VJMP) + luaK_concat(fs, &e->t, e->u.info); /* put this jump in `t' list */ + if (hasjumps(e)) { + int final; /* position after whole expression */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ + if (need_value(fs, e->t) || need_value(fs, e->f)) { + int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); + p_f = code_label(fs, reg, 0, 1); + p_t = code_label(fs, reg, 1, 0); + luaK_patchtohere(fs, fj); + } + final = luaK_getlabel(fs); + patchlistaux(fs, e->f, final, reg, p_f); + patchlistaux(fs, e->t, final, reg, p_t); + } + e->f = e->t = NO_JUMP; + e->u.info = reg; + e->k = VNONRELOC; +} + + +void luaK_exp2nextreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + luaK_reserveregs(fs, 1); + exp2reg(fs, e, fs->freereg - 1); +} + + +int luaK_exp2anyreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + if (e->k == VNONRELOC) { + if (!hasjumps(e)) return e->u.info; /* exp is already in a register */ + if (e->u.info >= fs->nactvar) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.info); /* put value on it */ + return e->u.info; + } + } + luaK_exp2nextreg(fs, e); /* default */ + return e->u.info; +} + + +void luaK_exp2anyregup (FuncState *fs, expdesc *e) { + if (e->k != VUPVAL || hasjumps(e)) + luaK_exp2anyreg(fs, e); +} + + +void luaK_exp2val (FuncState *fs, expdesc *e) { + if (hasjumps(e)) + luaK_exp2anyreg(fs, e); + else + luaK_dischargevars(fs, e); +} + + +int luaK_exp2RK (FuncState *fs, expdesc *e) { + luaK_exp2val(fs, e); + switch (e->k) { + case VTRUE: + case VFALSE: + case VNIL: { + if (fs->nk <= MAXINDEXRK) { /* constant fits in RK operand? */ + e->u.info = (e->k == VNIL) ? nilK(fs) : boolK(fs, (e->k == VTRUE)); + e->k = VK; + return RKASK(e->u.info); + } + else break; + } + case VKNUM: { + e->u.info = luaK_numberK(fs, e->u.nval); + e->k = VK; + /* go through */ + } + case VK: { + if (e->u.info <= MAXINDEXRK) /* constant fits in argC? */ + return RKASK(e->u.info); + else break; + } + default: break; + } + /* not a constant in the right range: put it in a register */ + return luaK_exp2anyreg(fs, e); +} + + +void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { + switch (var->k) { + case VLOCAL: { + freeexp(fs, ex); + exp2reg(fs, ex, var->u.info); + return; + } + case VUPVAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); + break; + } + case VINDEXED: { + OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE : OP_SETTABUP; + int e = luaK_exp2RK(fs, ex); + luaK_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e); + break; + } + default: { + lua_assert(0); /* invalid var kind to store */ + break; + } + } + freeexp(fs, ex); +} + + +void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { + int ereg; + luaK_exp2anyreg(fs, e); + ereg = e->u.info; /* register where 'e' was placed */ + freeexp(fs, e); + e->u.info = fs->freereg; /* base register for op_self */ + e->k = VNONRELOC; + luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ + luaK_codeABC(fs, OP_SELF, e->u.info, ereg, luaK_exp2RK(fs, key)); + freeexp(fs, key); +} + + +static void invertjump (FuncState *fs, expdesc *e) { + Instruction *pc = getjumpcontrol(fs, e->u.info); + lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && + GET_OPCODE(*pc) != OP_TEST); + SETARG_A(*pc, !(GETARG_A(*pc))); +} + + +static int jumponcond (FuncState *fs, expdesc *e, int cond) { + if (e->k == VRELOCABLE) { + Instruction ie = getcode(fs, e); + if (GET_OPCODE(ie) == OP_NOT) { + fs->pc--; /* remove previous OP_NOT */ + return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); + } + /* else go through */ + } + discharge2anyreg(fs, e); + freeexp(fs, e); + return condjump(fs, OP_TESTSET, NO_REG, e->u.info, cond); +} + + +void luaK_goiftrue (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VJMP: { + invertjump(fs, e); + pc = e->u.info; + break; + } + case VK: case VKNUM: case VTRUE: { + pc = NO_JUMP; /* always true; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 0); + break; + } + } + luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */ + luaK_patchtohere(fs, e->t); + e->t = NO_JUMP; +} + + +void luaK_goiffalse (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VJMP: { + pc = e->u.info; + break; + } + case VNIL: case VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 1); + break; + } + } + luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */ + luaK_patchtohere(fs, e->f); + e->f = NO_JUMP; +} + + +static void codenot (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: case VFALSE: { + e->k = VTRUE; + break; + } + case VK: case VKNUM: case VTRUE: { + e->k = VFALSE; + break; + } + case VJMP: { + invertjump(fs, e); + break; + } + case VRELOCABLE: + case VNONRELOC: { + discharge2anyreg(fs, e); + freeexp(fs, e); + e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0); + e->k = VRELOCABLE; + break; + } + default: { + lua_assert(0); /* cannot happen */ + break; + } + } + /* interchange true and false lists */ + { int temp = e->f; e->f = e->t; e->t = temp; } + removevalues(fs, e->f); + removevalues(fs, e->t); +} + + +void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + lua_assert(!hasjumps(t)); + t->u.ind.t = t->u.info; + t->u.ind.idx = luaK_exp2RK(fs, k); + t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL + : check_exp(vkisinreg(t->k), VLOCAL); + t->k = VINDEXED; +} + + +static int constfolding (OpCode op, expdesc *e1, expdesc *e2) { + lua_Number r; + if (!isnumeral(e1) || !isnumeral(e2)) return 0; + if ((op == OP_DIV || op == OP_MOD) && e2->u.nval == 0) + return 0; /* do not attempt to divide by 0 */ + /* + * Patched: check for MIN_INT / -1 + */ + if (op == OP_DIV && e1->u.nval == INT64_MIN && e2->u.nval == -1) + return 0; + r = luaO_arith(op - OP_ADD + LUA_OPADD, e1->u.nval, e2->u.nval); + e1->u.nval = r; + return 1; +} + + +static void codearith (FuncState *fs, OpCode op, + expdesc *e1, expdesc *e2, int line) { + if (constfolding(op, e1, e2)) + return; + else { + int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; + int o1 = luaK_exp2RK(fs, e1); + if (o1 > o2) { + freeexp(fs, e1); + freeexp(fs, e2); + } + else { + freeexp(fs, e2); + freeexp(fs, e1); + } + e1->u.info = luaK_codeABC(fs, op, 0, o1, o2); + e1->k = VRELOCABLE; + luaK_fixline(fs, line); + } +} + + +static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, + expdesc *e2) { + int o1 = luaK_exp2RK(fs, e1); + int o2 = luaK_exp2RK(fs, e2); + freeexp(fs, e2); + freeexp(fs, e1); + if (cond == 0 && op != OP_EQ) { + int temp; /* exchange args to replace by `<' or `<=' */ + temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ + cond = 1; + } + e1->u.info = condjump(fs, op, cond, o1, o2); + e1->k = VJMP; +} + + +void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { + expdesc e2; + e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; + switch (op) { + case OPR_MINUS: { + if (isnumeral(e)) /* minus constant? */ + e->u.nval = luai_numunm(NULL, e->u.nval); /* fold it */ + else { + luaK_exp2anyreg(fs, e); + codearith(fs, OP_UNM, e, &e2, line); + } + break; + } + case OPR_NOT: codenot(fs, e); break; + case OPR_LEN: { + luaK_exp2anyreg(fs, e); /* cannot operate on constants */ + codearith(fs, OP_LEN, e, &e2, line); + break; + } + default: lua_assert(0); + } +} + + +void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { + switch (op) { + case OPR_AND: { + luaK_goiftrue(fs, v); + break; + } + case OPR_OR: { + luaK_goiffalse(fs, v); + break; + } + case OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */ + break; + } + case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: + case OPR_MOD: case OPR_POW: { + if (!isnumeral(v)) luaK_exp2RK(fs, v); + break; + } + default: { + luaK_exp2RK(fs, v); + break; + } + } +} + + +void luaK_posfix (FuncState *fs, BinOpr op, + expdesc *e1, expdesc *e2, int line) { + switch (op) { + case OPR_AND: { + lua_assert(e1->t == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->f, e1->f); + *e1 = *e2; + break; + } + case OPR_OR: { + lua_assert(e1->f == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->t, e1->t); + *e1 = *e2; + break; + } + case OPR_CONCAT: { + luaK_exp2val(fs, e2); + if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { + lua_assert(e1->u.info == GETARG_B(getcode(fs, e2))-1); + freeexp(fs, e1); + SETARG_B(getcode(fs, e2), e1->u.info); + e1->k = VRELOCABLE; e1->u.info = e2->u.info; + } + else { + luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ + codearith(fs, OP_CONCAT, e1, e2, line); + } + break; + } + case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: + case OPR_MOD: case OPR_POW: { + codearith(fs, cast(OpCode, op - OPR_ADD + OP_ADD), e1, e2, line); + break; + } + case OPR_EQ: case OPR_LT: case OPR_LE: { + codecomp(fs, cast(OpCode, op - OPR_EQ + OP_EQ), 1, e1, e2); + break; + } + case OPR_NE: case OPR_GT: case OPR_GE: { + codecomp(fs, cast(OpCode, op - OPR_NE + OP_EQ), 0, e1, e2); + break; + } + default: lua_assert(0); + } +} + + +void luaK_fixline (FuncState *fs, int line) { + fs->f->lineinfo[fs->pc - 1] = line; +} + + +void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { + int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; + int b = (tostore == LUA_MULTRET) ? 0 : tostore; + lua_assert(tostore != 0); + if (c <= MAXARG_C) + luaK_codeABC(fs, OP_SETLIST, base, b, c); + else if (c <= MAXARG_Ax) { + luaK_codeABC(fs, OP_SETLIST, base, b, 0); + codeextraarg(fs, c); + } + else + luaX_syntaxerror(fs->ls, "constructor too long"); + fs->freereg = base + 1; /* free registers with list values */ +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lcode.h b/usr/src/uts/common/fs/zfs/lua/lcode.h new file mode 100644 index 0000000000..6a1424cf5a --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lcode.h @@ -0,0 +1,83 @@ +/* +** $Id: lcode.h,v 1.58.1.1 2013/04/12 18:48:47 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lcode_h +#define lcode_h + +#include "llex.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" + + +/* +** Marks the end of a patch list. It is an invalid value both as an absolute +** address, and as a list link (would link an element to itself). +*/ +#define NO_JUMP (-1) + + +/* +** grep "ORDER OPR" if you change these enums (ORDER OP) +*/ +typedef enum BinOpr { + OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, + OPR_CONCAT, + OPR_EQ, OPR_LT, OPR_LE, + OPR_NE, OPR_GT, OPR_GE, + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + + +typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; + + +#define getcode(fs,e) ((fs)->f->code[(e)->u.info]) + +#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) + +#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) + +#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) + +LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); +LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C); +LUAI_FUNC int luaK_codek (FuncState *fs, int reg, int k); +LUAI_FUNC void luaK_fixline (FuncState *fs, int line); +LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); +LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); +LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); +LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r); +LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); +LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); +LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); +LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); +LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_jump (FuncState *fs); +LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); +LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); +LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); +LUAI_FUNC void luaK_patchclose (FuncState *fs, int list, int level); +LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); +LUAI_FUNC int luaK_getlabel (FuncState *fs); +LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); +LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); +LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, + expdesc *v2, int line); +LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lcompat.c b/usr/src/uts/common/fs/zfs/lua/lcompat.c new file mode 100644 index 0000000000..55564ddbd9 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lcompat.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +#include "lua.h" + +#include <sys/zfs_context.h> + +ssize_t +lcompat_sprintf(char *buf, const char *fmt, ...) +{ + ssize_t res; + va_list args; + + va_start(args, fmt); + res = vsnprintf(buf, INT_MAX, fmt, args); + va_end(args); + + return (res); +} + +int64_t +lcompat_strtoll(const char *str, char **ptr) +{ + int base; + const char *cp; + int digits; + int64_t value; + boolean_t is_negative; + + cp = str; + while (*cp == ' ' || *cp == '\t' || *cp == '\n') { + cp++; + } + is_negative = (*cp == '-'); + if (is_negative) { + cp++; + } + base = 10; + + if (*cp == '0') { + base = 8; + cp++; + if (*cp == 'x' || *cp == 'X') { + base = 16; + cp++; + } + } + + value = 0; + for (; *cp != '\0'; cp++) { + if (*cp >= '0' && *cp <= '9') { + digits = *cp - '0'; + } else if (*cp >= 'a' && *cp <= 'f') { + digits = *cp - 'a' + 10; + } else if (*cp >= 'A' && *cp <= 'F') { + digits = *cp - 'A' + 10; + } else { + break; + } + if (digits >= base) { + break; + } + value = (value * base) + digits; + } + + if (ptr != NULL) { + *ptr = (char *)cp; + } + if (is_negative) { + value = -value; + } + return (value); +} + +int64_t +lcompat_pow(int64_t x, int64_t y) +{ + int64_t result = 1; + if (y < 0) + return (0); + + while (y) { + if (y & 1) + result *= x; + y >>= 1; + x *= x; + } + return (result); +} + +int +lcompat_hashnum(int64_t x) +{ + x = (~x) + (x << 18); + x = x ^ (x >> 31); + x = x * 21; + x = x ^ (x >> 11); + x = x + (x << 6); + x = x ^ (x >> 22); + return ((int)x); +} diff --git a/usr/src/uts/common/fs/zfs/lua/lcorolib.c b/usr/src/uts/common/fs/zfs/lua/lcorolib.c new file mode 100644 index 0000000000..405350bb14 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lcorolib.c @@ -0,0 +1,154 @@ +/* +** $Id: lcorolib.c,v 1.5.1.1 2013/04/12 18:48:47 roberto Exp $ +** Coroutine Library +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define lcorolib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status; + if (!lua_checkstack(co, narg)) { + lua_pushliteral(L, "too many arguments to resume"); + return -1; /* error flag */ + } + if (lua_status(co) == LUA_OK && lua_gettop(co) == 0) { + lua_pushliteral(L, "cannot resume dead coroutine"); + return -1; /* error flag */ + } + lua_xmove(L, co, narg); + status = lua_resume(co, L, narg); + if (status == LUA_OK || status == LUA_YIELD) { + int nres = lua_gettop(co); + if (!lua_checkstack(L, nres + 1)) { + lua_pop(co, nres); /* remove results anyway */ + lua_pushliteral(L, "too many results to resume"); + return -1; /* error flag */ + } + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + int r; + luaL_argcheck(L, co, 1, "coroutine expected"); + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + `resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { + if (lua_isstring(L, -1)) { /* error object is a string? */ + luaL_where(L, 1); /* add extra info */ + lua_insert(L, -2); + lua_concat(L, 2); + } + return lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL; + luaL_checktype(L, 1, LUA_TFUNCTION); + NL = lua_newthread(L); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argcheck(L, co, 1, "coroutine expected"); + if (L == co) lua_pushliteral(L, "running"); + else { + switch (lua_status(co)) { + case LUA_YIELD: + lua_pushliteral(L, "suspended"); + break; + case LUA_OK: { + lua_Debug ar; + if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ + lua_pushliteral(L, "normal"); /* it is running */ + else if (lua_gettop(co) == 0) + lua_pushliteral(L, "dead"); + else + lua_pushliteral(L, "suspended"); /* initial state */ + break; + } + default: /* some error occurred */ + lua_pushliteral(L, "dead"); + break; + } + } + return 1; +} + + +static int luaB_corunning (lua_State *L) { + int ismain = lua_pushthread(L); + lua_pushboolean(L, ismain); + return 2; +} + + +static const luaL_Reg co_funcs[] = { + {"create", luaB_cocreate}, + {"resume", luaB_coresume}, + {"running", luaB_corunning}, + {"status", luaB_costatus}, + {"wrap", luaB_cowrap}, + {"yield", luaB_yield}, + {NULL, NULL} +}; + + + +LUAMOD_API int luaopen_coroutine (lua_State *L) { + luaL_newlib(L, co_funcs); + return 1; +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lctype.c b/usr/src/uts/common/fs/zfs/lua/lctype.c new file mode 100644 index 0000000000..107859811b --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lctype.c @@ -0,0 +1,52 @@ +/* +** $Id: lctype.c,v 1.11.1.1 2013/04/12 18:48:47 roberto Exp $ +** 'ctype' functions for Lua +** See Copyright Notice in lua.h +*/ + +#define lctype_c +#define LUA_CORE + +#include "lctype.h" + +#if !LUA_USE_CTYPE /* { */ + +#include <sys/zfs_context.h> + +LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { + 0x00, /* EOZ */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ + 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#endif /* } */ diff --git a/usr/src/uts/common/fs/zfs/lua/lctype.h b/usr/src/uts/common/fs/zfs/lua/lctype.h new file mode 100644 index 0000000000..299a59b92e --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lctype.h @@ -0,0 +1,93 @@ +/* +** $Id: lctype.h,v 1.12.1.1 2013/04/12 18:48:47 roberto Exp $ +** 'ctype' functions for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lctype_h +#define lctype_h + +#include "lua.h" + + +/* +** WARNING: the functions defined here do not necessarily correspond +** to the similar functions in the standard C ctype.h. They are +** optimized for the specific needs of Lua +*/ + +#if !defined(LUA_USE_CTYPE) + +#if 'A' == 65 && '0' == 48 +/* ASCII case: can use its own tables; faster and fixed */ +#define LUA_USE_CTYPE 0 +#else +/* must use standard C ctype */ +#define LUA_USE_CTYPE 1 +#endif + +#endif + + +#if !LUA_USE_CTYPE /* { */ + +#include "llimits.h" + + +#define ALPHABIT 0 +#define DIGITBIT 1 +#define PRINTBIT 2 +#define SPACEBIT 3 +#define XDIGITBIT 4 + + +#define MASK(B) (1 << (B)) + + +/* +** add 1 to char to allow index -1 (EOZ) +*/ +#define testprop(c,p) (luai_ctype_[(c)+1] & (p)) + +/* +** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' +*/ +#define lislalpha(c) testprop(c, MASK(ALPHABIT)) +#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) +#define lisdigit(c) testprop(c, MASK(DIGITBIT)) +#define lisspace(c) testprop(c, MASK(SPACEBIT)) +#define lisprint(c) testprop(c, MASK(PRINTBIT)) +#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) + +/* +** this 'ltolower' only works for alphabetic characters +*/ +#define ltolower(c) ((c) | ('A' ^ 'a')) + + +/* two more entries for 0 and -1 (EOZ) */ +LUAI_DDEC const lu_byte luai_ctype_[UCHAR_MAX + 2]; + + +#else /* }{ */ + +/* +** use standard C ctypes +*/ + +#include <ctype.h> + + +#define lislalpha(c) (isalpha(c) || (c) == '_') +#define lislalnum(c) (isalnum(c) || (c) == '_') +#define lisdigit(c) (isdigit(c)) +#define lisspace(c) (isspace(c)) +#define lisprint(c) (isprint(c)) +#define lisxdigit(c) (isxdigit(c)) + +#define ltolower(c) (tolower(c)) + +#endif /* } */ + +#endif + diff --git a/usr/src/uts/common/fs/zfs/lua/ldebug.c b/usr/src/uts/common/fs/zfs/lua/ldebug.c new file mode 100644 index 0000000000..b8ddcff3c6 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ldebug.c @@ -0,0 +1,607 @@ +/* +** $Id: ldebug.c,v 2.90.1.4 2015/02/19 17:05:13 roberto Exp $ +** Debug Interface +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define ldebug_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL) + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name); + + +static int currentpc (CallInfo *ci) { + lua_assert(isLua(ci)); + return pcRel(ci->u.l.savedpc, ci_func(ci)->p); +} + + +static int currentline (CallInfo *ci) { + return getfuncline(ci_func(ci)->p, currentpc(ci)); +} + + +static void swapextra (lua_State *L) { + if (L->status == LUA_YIELD) { + CallInfo *ci = L->ci; /* get function that yielded */ + StkId temp = ci->func; /* exchange its 'func' and 'extra' values */ + ci->func = restorestack(L, ci->extra); + ci->extra = savestack(L, temp); + } +} + + +/* +** this function can be called asynchronous (e.g. during a signal) +*/ +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { + if (func == NULL || mask == 0) { /* turn off hooks? */ + mask = 0; + func = NULL; + } + if (isLua(L->ci)) + L->oldpc = L->ci->u.l.savedpc; + L->hook = func; + L->basehookcount = count; + resethookcount(L); + L->hookmask = cast_byte(mask); + return 1; +} + + +LUA_API lua_Hook lua_gethook (lua_State *L) { + return L->hook; +} + + +LUA_API int lua_gethookmask (lua_State *L) { + return L->hookmask; +} + + +LUA_API int lua_gethookcount (lua_State *L) { + return L->basehookcount; +} + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { + int status; + CallInfo *ci; + if (level < 0) return 0; /* invalid (negative) level */ + lua_lock(L); + for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous) + level--; + if (level == 0 && ci != &L->base_ci) { /* level found? */ + status = 1; + ar->i_ci = ci; + } + else status = 0; /* no such level */ + lua_unlock(L); + return status; +} + + +static const char *upvalname (Proto *p, int uv) { + TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name); + if (s == NULL) return "?"; + else return getstr(s); +} + + +static const char *findvararg (CallInfo *ci, int n, StkId *pos) { + int nparams = clLvalue(ci->func)->p->numparams; + if (n >= ci->u.l.base - ci->func - nparams) + return NULL; /* no such vararg */ + else { + *pos = ci->func + nparams + n; + return "(*vararg)"; /* generic name for any vararg */ + } +} + + +static const char *findlocal (lua_State *L, CallInfo *ci, int n, + StkId *pos) { + const char *name = NULL; + StkId base; + if (isLua(ci)) { + if (n < 0) /* access to vararg values? */ + return findvararg(ci, -n, pos); + else { + base = ci->u.l.base; + name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); + } + } + else + base = ci->func + 1; + if (name == NULL) { /* no 'standard' name? */ + StkId limit = (ci == L->ci) ? L->top : ci->next->func; + if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */ + name = "(*temporary)"; /* generic name for any valid slot */ + else + return NULL; /* no name */ + } + *pos = base + (n - 1); + return name; +} + + +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { + const char *name; + lua_lock(L); + swapextra(L); + if (ar == NULL) { /* information about non-active function? */ + if (!isLfunction(L->top - 1)) /* not a Lua function? */ + name = NULL; + else /* consider live variables at function start (parameters) */ + name = luaF_getlocalname(clLvalue(L->top - 1)->p, n, 0); + } + else { /* active function; get information through 'ar' */ + StkId pos = 0; /* to avoid warnings */ + name = findlocal(L, ar->i_ci, n, &pos); + if (name) { + setobj2s(L, L->top, pos); + api_incr_top(L); + } + } + swapextra(L); + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { + StkId pos = 0; /* to avoid warnings */ + const char *name; + lua_lock(L); + swapextra(L); + name = findlocal(L, ar->i_ci, n, &pos); + if (name) + setobjs2s(L, pos, L->top - 1); + L->top--; /* pop value */ + swapextra(L); + lua_unlock(L); + return name; +} + + +static void funcinfo (lua_Debug *ar, Closure *cl) { + if (noLuaClosure(cl)) { + ar->source = "=[C]"; + ar->linedefined = -1; + ar->lastlinedefined = -1; + ar->what = "C"; + } + else { + Proto *p = cl->l.p; + ar->source = p->source ? getstr(p->source) : "=?"; + ar->linedefined = p->linedefined; + ar->lastlinedefined = p->lastlinedefined; + ar->what = (ar->linedefined == 0) ? "main" : "Lua"; + } + luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); +} + + +static void collectvalidlines (lua_State *L, Closure *f) { + if (noLuaClosure(f)) { + setnilvalue(L->top); + api_incr_top(L); + } + else { + int i; + TValue v; + int *lineinfo = f->l.p->lineinfo; + Table *t = luaH_new(L); /* new table to store active lines */ + sethvalue(L, L->top, t); /* push it on stack */ + api_incr_top(L); + setbvalue(&v, 1); /* boolean 'true' to be the value of all indices */ + for (i = 0; i < f->l.p->sizelineinfo; i++) /* for all lines with code */ + luaH_setint(L, t, lineinfo[i], &v); /* table[line] = true */ + } +} + + +static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, + Closure *f, CallInfo *ci) { + int status = 1; + for (; *what; what++) { + switch (*what) { + case 'S': { + funcinfo(ar, f); + break; + } + case 'l': { + ar->currentline = (ci && isLua(ci)) ? currentline(ci) : -1; + break; + } + case 'u': { + ar->nups = (f == NULL) ? 0 : f->c.nupvalues; + if (noLuaClosure(f)) { + ar->isvararg = 1; + ar->nparams = 0; + } + else { + ar->isvararg = f->l.p->is_vararg; + ar->nparams = f->l.p->numparams; + } + break; + } + case 't': { + ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0; + break; + } + case 'n': { + /* calling function is a known Lua function? */ + if (ci && !(ci->callstatus & CIST_TAIL) && isLua(ci->previous)) + ar->namewhat = getfuncname(L, ci->previous, &ar->name); + else + ar->namewhat = NULL; + if (ar->namewhat == NULL) { + ar->namewhat = ""; /* not found */ + ar->name = NULL; + } + break; + } + case 'L': + case 'f': /* handled by lua_getinfo */ + break; + default: status = 0; /* invalid option */ + } + } + return status; +} + + +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { + int status; + Closure *cl; + CallInfo *ci; + StkId func; + lua_lock(L); + swapextra(L); + if (*what == '>') { + ci = NULL; + func = L->top - 1; + api_check(L, ttisfunction(func), "function expected"); + what++; /* skip the '>' */ + L->top--; /* pop function */ + } + else { + ci = ar->i_ci; + func = ci->func; + lua_assert(ttisfunction(ci->func)); + } + cl = ttisclosure(func) ? clvalue(func) : NULL; + status = auxgetinfo(L, what, ar, cl, ci); + if (strchr(what, 'f')) { + setobjs2s(L, L->top, func); + api_incr_top(L); + } + swapextra(L); + if (strchr(what, 'L')) + collectvalidlines(L, cl); + lua_unlock(L); + return status; +} + + +/* +** {====================================================== +** Symbolic Execution +** ======================================================= +*/ + +static const char *getobjname (Proto *p, int lastpc, int reg, + const char **name); + + +/* +** find a "name" for the RK value 'c' +*/ +static void kname (Proto *p, int pc, int c, const char **name) { + if (ISK(c)) { /* is 'c' a constant? */ + TValue *kvalue = &p->k[INDEXK(c)]; + if (ttisstring(kvalue)) { /* literal constant? */ + *name = svalue(kvalue); /* it is its own name */ + return; + } + /* else no reasonable name found */ + } + else { /* 'c' is a register */ + const char *what = getobjname(p, pc, c, name); /* search for 'c' */ + if (what && *what == 'c') { /* found a constant name? */ + return; /* 'name' already filled */ + } + /* else no reasonable name found */ + } + *name = "?"; /* no reasonable name found */ +} + + +static int filterpc (int pc, int jmptarget) { + if (pc < jmptarget) /* is code conditional (inside a jump)? */ + return -1; /* cannot know who sets that register */ + else return pc; /* current position sets that register */ +} + + +/* +** try to find last instruction before 'lastpc' that modified register 'reg' +*/ +static int findsetreg (Proto *p, int lastpc, int reg) { + int pc; + int setreg = -1; /* keep last instruction that changed 'reg' */ + int jmptarget = 0; /* any code before this address is conditional */ + for (pc = 0; pc < lastpc; pc++) { + Instruction i = p->code[pc]; + OpCode op = GET_OPCODE(i); + int a = GETARG_A(i); + switch (op) { + case OP_LOADNIL: { + int b = GETARG_B(i); + if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */ + setreg = filterpc(pc, jmptarget); + break; + } + case OP_TFORCALL: { + if (reg >= a + 2) /* affect all regs above its base */ + setreg = filterpc(pc, jmptarget); + break; + } + case OP_CALL: + case OP_TAILCALL: { + if (reg >= a) /* affect all registers above base */ + setreg = filterpc(pc, jmptarget); + break; + } + case OP_JMP: { + int b = GETARG_sBx(i); + int dest = pc + 1 + b; + /* jump is forward and do not skip `lastpc'? */ + if (pc < dest && dest <= lastpc) { + if (dest > jmptarget) + jmptarget = dest; /* update 'jmptarget' */ + } + break; + } + case OP_TEST: { + if (reg == a) /* jumped code can change 'a' */ + setreg = filterpc(pc, jmptarget); + break; + } + default: + if (testAMode(op) && reg == a) /* any instruction that set A */ + setreg = filterpc(pc, jmptarget); + break; + } + } + return setreg; +} + + +static const char *getobjname (Proto *p, int lastpc, int reg, + const char **name) { + int pc; + *name = luaF_getlocalname(p, reg + 1, lastpc); + if (*name) /* is a local? */ + return "local"; + /* else try symbolic execution */ + pc = findsetreg(p, lastpc, reg); + if (pc != -1) { /* could find instruction? */ + Instruction i = p->code[pc]; + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_MOVE: { + int b = GETARG_B(i); /* move from 'b' to 'a' */ + if (b < GETARG_A(i)) + return getobjname(p, pc, b, name); /* get name for 'b' */ + break; + } + case OP_GETTABUP: + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + int t = GETARG_B(i); /* table index */ + const char *vn = (op == OP_GETTABLE) /* name of indexed variable */ + ? luaF_getlocalname(p, t + 1, pc) + : upvalname(p, t); + kname(p, pc, k, name); + return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "field"; + } + case OP_GETUPVAL: { + *name = upvalname(p, GETARG_B(i)); + return "upvalue"; + } + case OP_LOADK: + case OP_LOADKX: { + int b = (op == OP_LOADK) ? GETARG_Bx(i) + : GETARG_Ax(p->code[pc + 1]); + if (ttisstring(&p->k[b])) { + *name = svalue(&p->k[b]); + return "constant"; + } + break; + } + case OP_SELF: { + int k = GETARG_C(i); /* key index */ + kname(p, pc, k, name); + return "method"; + } + default: break; /* go through to return NULL */ + } + } + return NULL; /* could not find reasonable name */ +} + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { + TMS tm; + Proto *p = ci_func(ci)->p; /* calling function */ + int pc = currentpc(ci); /* calling instruction index */ + Instruction i = p->code[pc]; /* calling instruction */ + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: /* get function name */ + return getobjname(p, pc, GETARG_A(i), name); + case OP_TFORCALL: { /* for iterator */ + *name = "for iterator"; + return "for iterator"; + } + /* all other instructions can call only through metamethods */ + case OP_SELF: + case OP_GETTABUP: + case OP_GETTABLE: tm = TM_INDEX; break; + case OP_SETTABUP: + case OP_SETTABLE: tm = TM_NEWINDEX; break; + case OP_EQ: tm = TM_EQ; break; + case OP_ADD: tm = TM_ADD; break; + case OP_SUB: tm = TM_SUB; break; + case OP_MUL: tm = TM_MUL; break; + case OP_DIV: tm = TM_DIV; break; + case OP_MOD: tm = TM_MOD; break; + case OP_POW: tm = TM_POW; break; + case OP_UNM: tm = TM_UNM; break; + case OP_LEN: tm = TM_LEN; break; + case OP_LT: tm = TM_LT; break; + case OP_LE: tm = TM_LE; break; + case OP_CONCAT: tm = TM_CONCAT; break; + default: + return NULL; /* else no useful name can be found */ + } + *name = getstr(G(L)->tmname[tm]); + return "metamethod"; +} + +/* }====================================================== */ + + + +/* +** only ANSI way to check whether a pointer points to an array +** (used only for error messages, so efficiency is not a big concern) +*/ +static int isinstack (CallInfo *ci, const TValue *o) { + StkId p; + for (p = ci->u.l.base; p < ci->top; p++) + if (o == p) return 1; + return 0; +} + + +static const char *getupvalname (CallInfo *ci, const TValue *o, + const char **name) { + LClosure *c = ci_func(ci); + int i; + for (i = 0; i < c->nupvalues; i++) { + if (c->upvals[i]->v == o) { + *name = upvalname(c->p, i); + return "upvalue"; + } + } + return NULL; +} + + +l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + CallInfo *ci = L->ci; + const char *name = NULL; + const char *t = objtypename(o); + const char *kind = NULL; + if (isLua(ci)) { + kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ + if (!kind && isinstack(ci, o)) /* no? try a register */ + kind = getobjname(ci_func(ci)->p, currentpc(ci), + cast_int(o - ci->u.l.base), &name); + } + if (kind) + luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)", + op, kind, name, t); + else + luaG_runerror(L, "attempt to %s a %s value", op, t); +} + + +l_noret luaG_concaterror (lua_State *L, StkId p1, StkId p2) { + if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; + lua_assert(!ttisstring(p1) && !ttisnumber(p1)); + luaG_typeerror(L, p1, "concatenate"); +} + + +l_noret luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) { + TValue temp; + if (luaV_tonumber(p1, &temp) == NULL) + p2 = p1; /* first operand is wrong */ + luaG_typeerror(L, p2, "perform arithmetic on"); +} + + +l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { + const char *t1 = objtypename(p1); + const char *t2 = objtypename(p2); + if (t1 == t2) + luaG_runerror(L, "attempt to compare two %s values", t1); + else + luaG_runerror(L, "attempt to compare %s with %s", t1, t2); +} + + +static void addinfo (lua_State *L, const char *msg) { + CallInfo *ci = L->ci; + if (isLua(ci)) { /* is Lua code? */ + char buff[LUA_IDSIZE]; /* add file:line information */ + int line = currentline(ci); + TString *src = ci_func(ci)->p->source; + if (src) + luaO_chunkid(buff, getstr(src), LUA_IDSIZE); + else { /* no source available; use "?" instead */ + buff[0] = '?'; buff[1] = '\0'; + } + luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); + } +} + + +l_noret luaG_errormsg (lua_State *L) { + if (L->errfunc != 0) { /* is there an error handling function? */ + StkId errfunc = restorestack(L, L->errfunc); + if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR); + setobjs2s(L, L->top, L->top - 1); /* move argument */ + setobjs2s(L, L->top - 1, errfunc); /* push function */ + L->top++; + luaD_call(L, L->top - 2, 1, 0); /* call it */ + } + luaD_throw(L, LUA_ERRRUN); +} + + +l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + addinfo(L, luaO_pushvfstring(L, fmt, argp)); + va_end(argp); + luaG_errormsg(L); +} + diff --git a/usr/src/uts/common/fs/zfs/lua/ldebug.h b/usr/src/uts/common/fs/zfs/lua/ldebug.h new file mode 100644 index 0000000000..6445c763ea --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ldebug.h @@ -0,0 +1,34 @@ +/* +** $Id: ldebug.h,v 2.7.1.1 2013/04/12 18:48:47 roberto Exp $ +** Auxiliary functions from Debug Interface module +** See Copyright Notice in lua.h +*/ + +#ifndef ldebug_h +#define ldebug_h + + +#include "lstate.h" + + +#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) + +#define getfuncline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) + +#define resethookcount(L) (L->hookcount = L->basehookcount) + +/* Active Lua function (given call info) */ +#define ci_func(ci) (clLvalue((ci)->func)) + + +LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, + const char *opname); +LUAI_FUNC l_noret luaG_concaterror (lua_State *L, StkId p1, StkId p2); +LUAI_FUNC l_noret luaG_aritherror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); +LUAI_FUNC l_noret luaG_errormsg (lua_State *L); + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/ldo.c b/usr/src/uts/common/fs/zfs/lua/ldo.c new file mode 100644 index 0000000000..d6fcf49b15 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ldo.c @@ -0,0 +1,685 @@ +/* +** $Id: ldo.c,v 2.108.1.3 2013/11/08 18:22:50 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define ldo_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" +#include "lzio.h" + + + + +/* +** {====================================================== +** Error-recovery functions +** ======================================================= +*/ + +/* +** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By +** default, Lua handles errors with exceptions when compiling as +** C++ code, with _longjmp/_setjmp when asked to use them, and with +** longjmp/setjmp otherwise. +*/ +#if !defined(LUAI_THROW) + +#ifdef _KERNEL +#define LUAI_THROW(L,c) longjmp(&(c)->b) +#define LUAI_TRY(L,c,a) if (setjmp(&(c)->b) == 0) { a } +#define luai_jmpbuf label_t +#else +#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) +#define LUAI_TRY(L,c,a) \ + try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ + +#elif defined(LUA_USE_ULONGJMP) +/* in Unix, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#else +/* default handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif + +#endif + +#endif + + +/* chain list of long jump buffers */ +struct lua_longjmp { + struct lua_longjmp *previous; + luai_jmpbuf b; + volatile int status; /* error code */ +}; + + +static void seterrorobj (lua_State *L, int errcode, StkId oldtop) { + switch (errcode) { + case LUA_ERRMEM: { /* memory error? */ + setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ + break; + } + case LUA_ERRERR: { + setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); + break; + } + default: { + setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ + break; + } + } + L->top = oldtop + 1; +} + + +l_noret luaD_throw (lua_State *L, int errcode) { + if (L->errorJmp) { /* thread has an error handler? */ + L->errorJmp->status = errcode; /* set status */ + LUAI_THROW(L, L->errorJmp); /* jump to it */ + } + else { /* thread has no error handler */ + L->status = cast_byte(errcode); /* mark it as dead */ + if (G(L)->mainthread->errorJmp) { /* main thread has a handler? */ + setobjs2s(L, G(L)->mainthread->top++, L->top - 1); /* copy error obj. */ + luaD_throw(G(L)->mainthread, errcode); /* re-throw in main thread */ + } + else { /* no handler at all; abort */ + if (G(L)->panic) { /* panic function? */ + lua_unlock(L); + G(L)->panic(L); /* call it (last chance to jump out) */ + } + panic("no error handler"); + } + } +} + + +int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { + unsigned short oldnCcalls = L->nCcalls; + struct lua_longjmp lj; + lj.status = LUA_OK; + lj.previous = L->errorJmp; /* chain new error handler */ + L->errorJmp = &lj; + LUAI_TRY(L, &lj, + (*f)(L, ud); + ); + L->errorJmp = lj.previous; /* restore old error handler */ + L->nCcalls = oldnCcalls; + return lj.status; +} + +/* }====================================================== */ + + +static void correctstack (lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top = (ci->top - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + if (isLua(ci)) + ci->u.l.base = (ci->u.l.base - oldstack) + L->stack; + } +} + + +/* some space for error handling */ +#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) + + +void luaD_reallocstack (lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int lim = L->stacksize; + lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK); + luaM_reallocvector(L, L->stack, L->stacksize, newsize, TValue); + for (; lim < newsize; lim++) + setnilvalue(L->stack + lim); /* erase new segment */ + L->stacksize = newsize; + L->stack_last = L->stack + newsize - EXTRA_STACK; + correctstack(L, oldstack); +} + + +void luaD_growstack (lua_State *L, int n) { + int size = L->stacksize; + if (size > LUAI_MAXSTACK) /* error after extra size? */ + luaD_throw(L, LUA_ERRERR); + else { + int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK; + int newsize = 2 * size; + if (newsize > LUAI_MAXSTACK) newsize = LUAI_MAXSTACK; + if (newsize < needed) newsize = needed; + if (newsize > LUAI_MAXSTACK) { /* stack overflow? */ + luaD_reallocstack(L, ERRORSTACKSIZE); + luaG_runerror(L, "stack overflow"); + } + else + luaD_reallocstack(L, newsize); + } +} + + +static int stackinuse (lua_State *L) { + CallInfo *ci; + StkId lim = L->top; + for (ci = L->ci; ci != NULL; ci = ci->previous) { + lua_assert(ci->top <= L->stack_last); + if (lim < ci->top) lim = ci->top; + } + return cast_int(lim - L->stack) + 1; /* part of stack in use */ +} + + +void luaD_shrinkstack (lua_State *L) { + int inuse = stackinuse(L); + int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK; + if (goodsize > LUAI_MAXSTACK) goodsize = LUAI_MAXSTACK; + if (inuse > LUAI_MAXSTACK || /* handling stack overflow? */ + goodsize >= L->stacksize) /* would grow instead of shrink? */ + condmovestack(L); /* don't change stack (change only for debugging) */ + else + luaD_reallocstack(L, goodsize); /* shrink it */ +} + + +void luaD_hook (lua_State *L, int event, int line) { + lua_Hook hook = L->hook; + if (hook && L->allowhook) { + CallInfo *ci = L->ci; + ptrdiff_t top = savestack(L, L->top); + ptrdiff_t ci_top = savestack(L, ci->top); + lua_Debug ar; + ar.event = event; + ar.currentline = line; + ar.i_ci = ci; + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + ci->top = L->top + LUA_MINSTACK; + lua_assert(ci->top <= L->stack_last); + L->allowhook = 0; /* cannot call hooks inside a hook */ + ci->callstatus |= CIST_HOOKED; + lua_unlock(L); + (*hook)(L, &ar); + lua_lock(L); + lua_assert(!L->allowhook); + L->allowhook = 1; + ci->top = restorestack(L, ci_top); + L->top = restorestack(L, top); + ci->callstatus &= ~CIST_HOOKED; + } +} + + +static void callhook (lua_State *L, CallInfo *ci) { + int hook = LUA_HOOKCALL; + ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ + if (isLua(ci->previous) && + GET_OPCODE(*(ci->previous->u.l.savedpc - 1)) == OP_TAILCALL) { + ci->callstatus |= CIST_TAIL; + hook = LUA_HOOKTAILCALL; + } + luaD_hook(L, hook, -1); + ci->u.l.savedpc--; /* correct 'pc' */ +} + + +static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { + int i; + int nfixargs = p->numparams; + StkId base, fixed; + lua_assert(actual >= nfixargs); + /* move fixed parameters to final position */ + luaD_checkstack(L, p->maxstacksize); /* check again for new 'base' */ + fixed = L->top - actual; /* first fixed argument */ + base = L->top; /* final position of first argument */ + for (i=0; i<nfixargs; i++) { + setobjs2s(L, L->top++, fixed + i); + setnilvalue(fixed + i); + } + return base; +} + + +static StkId tryfuncTM (lua_State *L, StkId func) { + const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); + StkId p; + ptrdiff_t funcr = savestack(L, func); + if (!ttisfunction(tm)) + luaG_typeerror(L, func, "call"); + /* Open a hole inside the stack at `func' */ + for (p = L->top; p > func; p--) setobjs2s(L, p, p-1); + incr_top(L); + func = restorestack(L, funcr); /* previous call may change stack */ + setobj2s(L, func, tm); /* tag method is the new function to be called */ + return func; +} + + + +#define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L))) + + +/* +** returns true if function has been executed (C function) +*/ +int luaD_precall (lua_State *L, StkId func, int nresults) { + lua_CFunction f; + CallInfo *ci; + int n; /* number of arguments (Lua) or returns (C) */ + ptrdiff_t funcr = savestack(L, func); + switch (ttype(func)) { + case LUA_TLCF: /* light C function */ + f = fvalue(func); + goto Cfunc; + case LUA_TCCL: { /* C closure */ + f = clCvalue(func)->f; + Cfunc: + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + ci = next_ci(L); /* now 'enter' new function */ + ci->nresults = nresults; + ci->func = restorestack(L, funcr); + ci->top = L->top + LUA_MINSTACK; + lua_assert(ci->top <= L->stack_last); + ci->callstatus = 0; + luaC_checkGC(L); /* stack grow uses memory */ + if (L->hookmask & LUA_MASKCALL) + luaD_hook(L, LUA_HOOKCALL, -1); + lua_unlock(L); + n = (*f)(L); /* do the actual call */ + lua_lock(L); + api_checknelems(L, n); + luaD_poscall(L, L->top - n); + return 1; + } + case LUA_TLCL: { /* Lua function: prepare its call */ + StkId base; + Proto *p = clLvalue(func)->p; + n = cast_int(L->top - func) - 1; /* number of real arguments */ + luaD_checkstack(L, p->maxstacksize); + for (; n < p->numparams; n++) + setnilvalue(L->top++); /* complete missing arguments */ + if (!p->is_vararg) { + func = restorestack(L, funcr); + base = func + 1; + } + else { + base = adjust_varargs(L, p, n); + func = restorestack(L, funcr); /* previous call can change stack */ + } + ci = next_ci(L); /* now 'enter' new function */ + ci->nresults = nresults; + ci->func = func; + ci->u.l.base = base; + ci->top = base + p->maxstacksize; + lua_assert(ci->top <= L->stack_last); + ci->u.l.savedpc = p->code; /* starting point */ + ci->callstatus = CIST_LUA; + L->top = ci->top; + luaC_checkGC(L); /* stack grow uses memory */ + if (L->hookmask & LUA_MASKCALL) + callhook(L, ci); + return 0; + } + default: { /* not a function */ + func = tryfuncTM(L, func); /* retry with 'function' tag method */ + return luaD_precall(L, func, nresults); /* now it must be a function */ + } + } +} + + +int luaD_poscall (lua_State *L, StkId firstResult) { + StkId res; + int wanted, i; + CallInfo *ci = L->ci; + if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) { + if (L->hookmask & LUA_MASKRET) { + ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */ + luaD_hook(L, LUA_HOOKRET, -1); + firstResult = restorestack(L, fr); + } + L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for caller function */ + } + res = ci->func; /* res == final position of 1st result */ + wanted = ci->nresults; + L->ci = ci = ci->previous; /* back to caller */ + /* move results to correct place */ + for (i = wanted; i != 0 && firstResult < L->top; i--) + setobjs2s(L, res++, firstResult++); + while (i-- > 0) + setnilvalue(res++); + L->top = res; + return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ +} + + +/* +** Call a function (C or Lua). The function to be called is at *func. +** The arguments are on the stack, right after the function. +** When returns, all the results are on the stack, starting at the original +** function position. +*/ +void luaD_call (lua_State *L, StkId func, int nResults, int allowyield) { + if (++L->nCcalls >= LUAI_MAXCCALLS) { + if (L->nCcalls == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) + luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ + } + if (!allowyield) L->nny++; + if (!luaD_precall(L, func, nResults)) /* is a Lua function? */ + luaV_execute(L); /* call it */ + if (!allowyield) L->nny--; + L->nCcalls--; +} + + +static void finishCcall (lua_State *L) { + CallInfo *ci = L->ci; + int n; + lua_assert(ci->u.c.k != NULL); /* must have a continuation */ + lua_assert(L->nny == 0); + if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */ + ci->callstatus &= ~CIST_YPCALL; /* finish 'lua_pcall' */ + L->errfunc = ci->u.c.old_errfunc; + } + /* finish 'lua_callk'/'lua_pcall' */ + adjustresults(L, ci->nresults); + /* call continuation function */ + if (!(ci->callstatus & CIST_STAT)) /* no call status? */ + ci->u.c.status = LUA_YIELD; /* 'default' status */ + lua_assert(ci->u.c.status != LUA_OK); + ci->callstatus = (ci->callstatus & ~(CIST_YPCALL | CIST_STAT)) | CIST_YIELDED; + lua_unlock(L); + n = (*ci->u.c.k)(L); + lua_lock(L); + api_checknelems(L, n); + /* finish 'luaD_precall' */ + luaD_poscall(L, L->top - n); +} + + +static void unroll (lua_State *L, void *ud) { + UNUSED(ud); + for (;;) { + if (L->ci == &L->base_ci) /* stack is empty? */ + return; /* coroutine finished normally */ + if (!isLua(L->ci)) /* C function? */ + finishCcall(L); + else { /* Lua function */ + luaV_finishOp(L); /* finish interrupted instruction */ + luaV_execute(L); /* execute down to higher C 'boundary' */ + } + } +} + + +/* +** check whether thread has a suspended protected call +*/ +static CallInfo *findpcall (lua_State *L) { + CallInfo *ci; + for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */ + if (ci->callstatus & CIST_YPCALL) + return ci; + } + return NULL; /* no pending pcall */ +} + + +static int recover (lua_State *L, int status) { + StkId oldtop; + CallInfo *ci = findpcall(L); + if (ci == NULL) return 0; /* no recovery point */ + /* "finish" luaD_pcall */ + oldtop = restorestack(L, ci->extra); + luaF_close(L, oldtop); + seterrorobj(L, status, oldtop); + L->ci = ci; + L->allowhook = ci->u.c.old_allowhook; + L->nny = 0; /* should be zero to be yieldable */ + luaD_shrinkstack(L); + L->errfunc = ci->u.c.old_errfunc; + ci->callstatus |= CIST_STAT; /* call has error status */ + ci->u.c.status = status; /* (here it is) */ + return 1; /* continue running the coroutine */ +} + + +/* +** signal an error in the call to 'resume', not in the execution of the +** coroutine itself. (Such errors should not be handled by any coroutine +** error handler and should not kill the coroutine.) +*/ +static l_noret resume_error (lua_State *L, const char *msg, StkId firstArg) { + L->top = firstArg; /* remove args from the stack */ + setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */ + api_incr_top(L); + luaD_throw(L, -1); /* jump back to 'lua_resume' */ +} + + +/* +** do the work for 'lua_resume' in protected mode +*/ +static void resume_cb (lua_State *L, void *ud) { + int nCcalls = L->nCcalls; + StkId firstArg = cast(StkId, ud); + CallInfo *ci = L->ci; + if (nCcalls >= LUAI_MAXCCALLS) + resume_error(L, "C stack overflow", firstArg); + if (L->status == LUA_OK) { /* may be starting a coroutine */ + if (ci != &L->base_ci) /* not in base level? */ + resume_error(L, "cannot resume non-suspended coroutine", firstArg); + /* coroutine is in base level; start running it */ + if (!luaD_precall(L, firstArg - 1, LUA_MULTRET)) /* Lua function? */ + luaV_execute(L); /* call it */ + } + else if (L->status != LUA_YIELD) + resume_error(L, "cannot resume dead coroutine", firstArg); + else { /* resuming from previous yield */ + L->status = LUA_OK; + ci->func = restorestack(L, ci->extra); + if (isLua(ci)) /* yielded inside a hook? */ + luaV_execute(L); /* just continue running Lua code */ + else { /* 'common' yield */ + if (ci->u.c.k != NULL) { /* does it have a continuation? */ + int n; + ci->u.c.status = LUA_YIELD; /* 'default' status */ + ci->callstatus |= CIST_YIELDED; + lua_unlock(L); + n = (*ci->u.c.k)(L); /* call continuation */ + lua_lock(L); + api_checknelems(L, n); + firstArg = L->top - n; /* yield results come from continuation */ + } + luaD_poscall(L, firstArg); /* finish 'luaD_precall' */ + } + unroll(L, NULL); + } + lua_assert(nCcalls == L->nCcalls); +} + + +LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs) { + int status; + int oldnny = L->nny; /* save 'nny' */ + lua_lock(L); + luai_userstateresume(L, nargs); + L->nCcalls = (from) ? from->nCcalls + 1 : 1; + L->nny = 0; /* allow yields */ + api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); + status = luaD_rawrunprotected(L, resume_cb, L->top - nargs); + if (status == -1) /* error calling 'lua_resume'? */ + status = LUA_ERRRUN; + else { /* yield or regular error */ + while (status != LUA_OK && status != LUA_YIELD) { /* error? */ + if (recover(L, status)) /* recover point? */ + status = luaD_rawrunprotected(L, unroll, NULL); /* run continuation */ + else { /* unrecoverable error */ + L->status = cast_byte(status); /* mark thread as `dead' */ + seterrorobj(L, status, L->top); + L->ci->top = L->top; + break; + } + } + lua_assert(status == L->status); + } + L->nny = oldnny; /* restore 'nny' */ + L->nCcalls--; + lua_assert(L->nCcalls == ((from) ? from->nCcalls : 0)); + lua_unlock(L); + return status; +} + + +LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) { + CallInfo *ci = L->ci; + luai_userstateyield(L, nresults); + lua_lock(L); + api_checknelems(L, nresults); + if (L->nny > 0) { + if (L != G(L)->mainthread) + luaG_runerror(L, "attempt to yield across a C-call boundary"); + else + luaG_runerror(L, "attempt to yield from outside a coroutine"); + } + L->status = LUA_YIELD; + ci->extra = savestack(L, ci->func); /* save current 'func' */ + if (isLua(ci)) { /* inside a hook? */ + api_check(L, k == NULL, "hooks cannot continue after yielding"); + } + else { + if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ + ci->u.c.ctx = ctx; /* save context */ + ci->func = L->top - nresults - 1; /* protect stack below results */ + luaD_throw(L, LUA_YIELD); + } + lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ + lua_unlock(L); + return 0; /* return to 'luaD_hook' */ +} + + +int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t old_top, ptrdiff_t ef) { + int status; + CallInfo *old_ci = L->ci; + lu_byte old_allowhooks = L->allowhook; + unsigned short old_nny = L->nny; + ptrdiff_t old_errfunc = L->errfunc; + L->errfunc = ef; + status = luaD_rawrunprotected(L, func, u); + if (status != LUA_OK) { /* an error occurred? */ + StkId oldtop = restorestack(L, old_top); + luaF_close(L, oldtop); /* close possible pending closures */ + seterrorobj(L, status, oldtop); + L->ci = old_ci; + L->allowhook = old_allowhooks; + L->nny = old_nny; + luaD_shrinkstack(L); + } + L->errfunc = old_errfunc; + return status; +} + + + +/* +** Execute a protected parser. +*/ +struct SParser { /* data to `f_parser' */ + ZIO *z; + Mbuffer buff; /* dynamic structure used by the scanner */ + Dyndata dyd; /* dynamic structures used by the parser */ + const char *mode; + const char *name; +}; + + +static void checkmode (lua_State *L, const char *mode, const char *x) { + if (mode && strchr(mode, x[0]) == NULL) { + luaO_pushfstring(L, + "attempt to load a %s chunk (mode is " LUA_QS ")", x, mode); + luaD_throw(L, LUA_ERRSYNTAX); + } +} + + +static void f_parser (lua_State *L, void *ud) { + int i; + Closure *cl; + struct SParser *p = cast(struct SParser *, ud); + int c = zgetc(p->z); /* read first character */ + if (c == LUA_SIGNATURE[0]) { + checkmode(L, p->mode, "binary"); + cl = luaU_undump(L, p->z, &p->buff, p->name); + } + else { + checkmode(L, p->mode, "text"); + cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); + } + lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues); + for (i = 0; i < cl->l.nupvalues; i++) { /* initialize upvalues */ + UpVal *up = luaF_newupval(L); + cl->l.upvals[i] = up; + luaC_objbarrier(L, cl, up); + } +} + + +int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, + const char *mode) { + struct SParser p; + int status; + L->nny++; /* cannot yield during parsing */ + p.z = z; p.name = name; p.mode = mode; + p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0; + p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; + p.dyd.label.arr = NULL; p.dyd.label.size = 0; + luaZ_initbuffer(L, &p.buff); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); + luaZ_freebuffer(L, &p.buff); + luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); + luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); + luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size); + L->nny--; + return status; +} + + diff --git a/usr/src/uts/common/fs/zfs/lua/ldo.h b/usr/src/uts/common/fs/zfs/lua/ldo.h new file mode 100644 index 0000000000..d3d3082c9b --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ldo.h @@ -0,0 +1,46 @@ +/* +** $Id: ldo.h,v 2.20.1.1 2013/04/12 18:48:47 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#ifndef ldo_h +#define ldo_h + + +#include "lobject.h" +#include "lstate.h" +#include "lzio.h" + + +#define luaD_checkstack(L,n) if (L->stack_last - L->top <= (n)) \ + luaD_growstack(L, n); else condmovestack(L); + + +#define incr_top(L) {L->top++; luaD_checkstack(L,0);} + +#define savestack(L,p) ((char *)(p) - (char *)L->stack) +#define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) + + +/* type of protected functions, to be ran by `runprotected' */ +typedef void (*Pfunc) (lua_State *L, void *ud); + +LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, + const char *mode); +LUAI_FUNC void luaD_hook (lua_State *L, int event, int line); +LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults, + int allowyield); +LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t oldtop, ptrdiff_t ef); +LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult); +LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); +LUAI_FUNC void luaD_growstack (lua_State *L, int n); +LUAI_FUNC void luaD_shrinkstack (lua_State *L); + +LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); +LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); + +#endif + diff --git a/usr/src/uts/common/fs/zfs/lua/ldump.c b/usr/src/uts/common/fs/zfs/lua/ldump.c new file mode 100644 index 0000000000..64e5649332 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ldump.c @@ -0,0 +1,173 @@ +/* +** $Id: ldump.c,v 2.17.1.1 2013/04/12 18:48:47 roberto Exp $ +** save precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#include <sys/zfs_context.h> + +#define ldump_c +#define LUA_CORE + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lundump.h" + +typedef struct { + lua_State* L; + lua_Writer writer; + void* data; + int strip; + int status; +} DumpState; + +#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D) +#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D) + +static void DumpBlock(const void* b, size_t size, DumpState* D) +{ + if (D->status==0) + { + lua_unlock(D->L); + D->status=(*D->writer)(D->L,b,size,D->data); + lua_lock(D->L); + } +} + +static void DumpChar(int y, DumpState* D) +{ + char x=(char)y; + DumpVar(x,D); +} + +static void DumpInt(int x, DumpState* D) +{ + DumpVar(x,D); +} + +static void DumpNumber(lua_Number x, DumpState* D) +{ + DumpVar(x,D); +} + +static void DumpVector(const void* b, int n, size_t size, DumpState* D) +{ + DumpInt(n,D); + DumpMem(b,n,size,D); +} + +static void DumpString(const TString* s, DumpState* D) +{ + if (s==NULL) + { + size_t size=0; + DumpVar(size,D); + } + else + { + size_t size=s->tsv.len+1; /* include trailing '\0' */ + DumpVar(size,D); + DumpBlock(getstr(s),size*sizeof(char),D); + } +} + +#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D) + +static void DumpFunction(const Proto* f, DumpState* D); + +static void DumpConstants(const Proto* f, DumpState* D) +{ + int i,n=f->sizek; + DumpInt(n,D); + for (i=0; i<n; i++) + { + const TValue* o=&f->k[i]; + DumpChar(ttypenv(o),D); + switch (ttypenv(o)) + { + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + DumpChar(bvalue(o),D); + break; + case LUA_TNUMBER: + DumpNumber(nvalue(o),D); + break; + case LUA_TSTRING: + DumpString(rawtsvalue(o),D); + break; + default: lua_assert(0); + } + } + n=f->sizep; + DumpInt(n,D); + for (i=0; i<n; i++) DumpFunction(f->p[i],D); +} + +static void DumpUpvalues(const Proto* f, DumpState* D) +{ + int i,n=f->sizeupvalues; + DumpInt(n,D); + for (i=0; i<n; i++) + { + DumpChar(f->upvalues[i].instack,D); + DumpChar(f->upvalues[i].idx,D); + } +} + +static void DumpDebug(const Proto* f, DumpState* D) +{ + int i,n; + DumpString((D->strip) ? NULL : f->source,D); + n= (D->strip) ? 0 : f->sizelineinfo; + DumpVector(f->lineinfo,n,sizeof(int),D); + n= (D->strip) ? 0 : f->sizelocvars; + DumpInt(n,D); + for (i=0; i<n; i++) + { + DumpString(f->locvars[i].varname,D); + DumpInt(f->locvars[i].startpc,D); + DumpInt(f->locvars[i].endpc,D); + } + n= (D->strip) ? 0 : f->sizeupvalues; + DumpInt(n,D); + for (i=0; i<n; i++) DumpString(f->upvalues[i].name,D); +} + +static void DumpFunction(const Proto* f, DumpState* D) +{ + DumpInt(f->linedefined,D); + DumpInt(f->lastlinedefined,D); + DumpChar(f->numparams,D); + DumpChar(f->is_vararg,D); + DumpChar(f->maxstacksize,D); + DumpCode(f,D); + DumpConstants(f,D); + DumpUpvalues(f,D); + DumpDebug(f,D); +} + +static void DumpHeader(DumpState* D) +{ + lu_byte h[LUAC_HEADERSIZE]; + luaU_header(h); + DumpBlock(h,LUAC_HEADERSIZE,D); +} + +/* +** dump Lua function as precompiled chunk +*/ +int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) +{ + DumpState D; + D.L=L; + D.writer=w; + D.data=data; + D.strip=strip; + D.status=0; + DumpHeader(&D); + DumpFunction(f,&D); + return D.status; +} diff --git a/usr/src/uts/common/fs/zfs/lua/lfunc.c b/usr/src/uts/common/fs/zfs/lua/lfunc.c new file mode 100644 index 0000000000..684e44709a --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lfunc.c @@ -0,0 +1,161 @@ +/* +** $Id: lfunc.c,v 2.30.1.1 2013/04/12 18:48:47 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define lfunc_c +#define LUA_CORE + +#include "lua.h" + +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +Closure *luaF_newCclosure (lua_State *L, int n) { + Closure *c = &luaC_newobj(L, LUA_TCCL, sizeCclosure(n), NULL, 0)->cl; + c->c.nupvalues = cast_byte(n); + return c; +} + + +Closure *luaF_newLclosure (lua_State *L, int n) { + Closure *c = &luaC_newobj(L, LUA_TLCL, sizeLclosure(n), NULL, 0)->cl; + c->l.p = NULL; + c->l.nupvalues = cast_byte(n); + while (n--) c->l.upvals[n] = NULL; + return c; +} + + +UpVal *luaF_newupval (lua_State *L) { + UpVal *uv = &luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal), NULL, 0)->uv; + uv->v = &uv->u.value; + setnilvalue(uv->v); + return uv; +} + + +UpVal *luaF_findupval (lua_State *L, StkId level) { + global_State *g = G(L); + GCObject **pp = &L->openupval; + UpVal *p; + UpVal *uv; + while (*pp != NULL && (p = gco2uv(*pp))->v >= level) { + GCObject *o = obj2gco(p); + lua_assert(p->v != &p->u.value); + lua_assert(!isold(o) || isold(obj2gco(L))); + if (p->v == level) { /* found a corresponding upvalue? */ + if (isdead(g, o)) /* is it dead? */ + changewhite(o); /* resurrect it */ + return p; + } + pp = &p->next; + } + /* not found: create a new one */ + uv = &luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal), pp, 0)->uv; + uv->v = level; /* current value lives in the stack */ + uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ + uv->u.l.next = g->uvhead.u.l.next; + uv->u.l.next->u.l.prev = uv; + g->uvhead.u.l.next = uv; + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + return uv; +} + + +static void unlinkupval (UpVal *uv) { + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ + uv->u.l.prev->u.l.next = uv->u.l.next; +} + + +void luaF_freeupval (lua_State *L, UpVal *uv) { + if (uv->v != &uv->u.value) /* is it open? */ + unlinkupval(uv); /* remove from open list */ + luaM_free(L, uv); /* free upvalue */ +} + + +void luaF_close (lua_State *L, StkId level) { + UpVal *uv; + global_State *g = G(L); + while (L->openupval != NULL && (uv = gco2uv(L->openupval))->v >= level) { + GCObject *o = obj2gco(uv); + lua_assert(!isblack(o) && uv->v != &uv->u.value); + L->openupval = uv->next; /* remove from `open' list */ + if (isdead(g, o)) + luaF_freeupval(L, uv); /* free upvalue */ + else { + unlinkupval(uv); /* remove upvalue from 'uvhead' list */ + setobj(L, &uv->u.value, uv->v); /* move value to upvalue slot */ + uv->v = &uv->u.value; /* now current value lives here */ + gch(o)->next = g->allgc; /* link upvalue into 'allgc' list */ + g->allgc = o; + luaC_checkupvalcolor(g, uv); + } + } +} + + +Proto *luaF_newproto (lua_State *L) { + Proto *f = &luaC_newobj(L, LUA_TPROTO, sizeof(Proto), NULL, 0)->p; + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->cache = NULL; + f->sizecode = 0; + f->lineinfo = NULL; + f->sizelineinfo = 0; + f->upvalues = NULL; + f->sizeupvalues = 0; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->locvars = NULL; + f->sizelocvars = 0; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + + +void luaF_freeproto (lua_State *L, Proto *f) { + luaM_freearray(L, f->code, f->sizecode); + luaM_freearray(L, f->p, f->sizep); + luaM_freearray(L, f->k, f->sizek); + luaM_freearray(L, f->lineinfo, f->sizelineinfo); + luaM_freearray(L, f->locvars, f->sizelocvars); + luaM_freearray(L, f->upvalues, f->sizeupvalues); + luaM_free(L, f); +} + + +/* +** Look for n-th local variable at line `line' in function `func'. +** Returns NULL if not found. +*/ +const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { + int i; + for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) { + if (pc < f->locvars[i].endpc) { /* is variable active? */ + local_number--; + if (local_number == 0) + return getstr(f->locvars[i].varname); + } + } + return NULL; /* not found */ +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lfunc.h b/usr/src/uts/common/fs/zfs/lua/lfunc.h new file mode 100644 index 0000000000..ca0d3a3e0b --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lfunc.h @@ -0,0 +1,33 @@ +/* +** $Id: lfunc.h,v 2.8.1.1 2013/04/12 18:48:47 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#ifndef lfunc_h +#define lfunc_h + + +#include "lobject.h" + + +#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ + cast(int, sizeof(TValue)*((n)-1))) + +#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ + cast(int, sizeof(TValue *)*((n)-1))) + + +LUAI_FUNC Proto *luaF_newproto (lua_State *L); +LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems); +LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems); +LUAI_FUNC UpVal *luaF_newupval (lua_State *L); +LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_close (lua_State *L, StkId level); +LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); +LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv); +LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, + int pc); + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lgc.c b/usr/src/uts/common/fs/zfs/lua/lgc.c new file mode 100644 index 0000000000..9c98630574 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lgc.c @@ -0,0 +1,1220 @@ +/* +** $Id: lgc.c,v 2.140.1.3 2014/09/01 16:55:08 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#include <sys/zfs_context.h> + +#define lgc_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + + +/* +** cost of sweeping one element (the size of a small object divided +** by some adjust for the sweep speed) +*/ +#define GCSWEEPCOST ((sizeof(TString) + 4) / 4) + +/* maximum number of elements to sweep in each single step */ +#define GCSWEEPMAX (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4)) + +/* maximum number of finalizers to call in each GC step */ +#define GCFINALIZENUM 4 + + +/* +** macro to adjust 'stepmul': 'stepmul' is actually used like +** 'stepmul / STEPMULADJ' (value chosen by tests) +*/ +#define STEPMULADJ 200 + + +/* +** macro to adjust 'pause': 'pause' is actually used like +** 'pause / PAUSEADJ' (value chosen by tests) +*/ +#define PAUSEADJ 100 + + +/* +** 'makewhite' erases all color bits plus the old bit and then +** sets only the current white bit +*/ +#define maskcolors (~(bit2mask(BLACKBIT, OLDBIT) | WHITEBITS)) +#define makewhite(g,x) \ + (gch(x)->marked = cast_byte((gch(x)->marked & maskcolors) | luaC_white(g))) + +#define white2gray(x) resetbits(gch(x)->marked, WHITEBITS) +#define black2gray(x) resetbit(gch(x)->marked, BLACKBIT) + + +#define isfinalized(x) testbit(gch(x)->marked, FINALIZEDBIT) + +#define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) + + +#define checkconsistency(obj) \ + lua_longassert(!iscollectable(obj) || righttt(obj)) + + +#define markvalue(g,o) { checkconsistency(o); \ + if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } + +#define markobject(g,t) { if ((t) && iswhite(obj2gco(t))) \ + reallymarkobject(g, obj2gco(t)); } + +static void reallymarkobject (global_State *g, GCObject *o); + + +/* +** {====================================================== +** Generic functions +** ======================================================= +*/ + + +/* +** one after last element in a hash array +*/ +#define gnodelast(h) gnode(h, cast(size_t, sizenode(h))) + + +/* +** link table 'h' into list pointed by 'p' +*/ +#define linktable(h,p) ((h)->gclist = *(p), *(p) = obj2gco(h)) + + +/* +** if key is not marked, mark its entry as dead (therefore removing it +** from the table) +*/ +static void removeentry (Node *n) { + lua_assert(ttisnil(gval(n))); + if (valiswhite(gkey(n))) + setdeadvalue(gkey(n)); /* unused and unmarked key; remove it */ +} + + +/* +** tells whether a key or value can be cleared from a weak +** table. Non-collectable objects are never removed from weak +** tables. Strings behave as `values', so are never removed too. for +** other objects: if really collected, cannot keep them; for objects +** being finalized, keep them in keys, but not in values +*/ +static int iscleared (global_State *g, const TValue *o) { + if (!iscollectable(o)) return 0; + else if (ttisstring(o)) { + markobject(g, rawtsvalue(o)); /* strings are `values', so are never weak */ + return 0; + } + else return iswhite(gcvalue(o)); +} + + +/* +** barrier that moves collector forward, that is, mark the white object +** being pointed by a black object. +*/ +void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { + global_State *g = G(L); + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); + lua_assert(g->gcstate != GCSpause); + lua_assert(gch(o)->tt != LUA_TTABLE); + if (keepinvariantout(g)) /* must keep invariant? */ + reallymarkobject(g, v); /* restore invariant */ + else { /* sweep phase */ + lua_assert(issweepphase(g)); + makewhite(g, o); /* mark main obj. as white to avoid other barriers */ + } +} + + +/* +** barrier that moves collector backward, that is, mark the black object +** pointing to a white object as gray again. (Current implementation +** only works for tables; access to 'gclist' is not uniform across +** different types.) +*/ +void luaC_barrierback_ (lua_State *L, GCObject *o) { + global_State *g = G(L); + lua_assert(isblack(o) && !isdead(g, o) && gch(o)->tt == LUA_TTABLE); + black2gray(o); /* make object gray (again) */ + gco2t(o)->gclist = g->grayagain; + g->grayagain = o; +} + + +/* +** barrier for prototypes. When creating first closure (cache is +** NULL), use a forward barrier; this may be the only closure of the +** prototype (if it is a "regular" function, with a single instance) +** and the prototype may be big, so it is better to avoid traversing +** it again. Otherwise, use a backward barrier, to avoid marking all +** possible instances. +*/ +LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c) { + global_State *g = G(L); + lua_assert(isblack(obj2gco(p))); + if (p->cache == NULL) { /* first time? */ + luaC_objbarrier(L, p, c); + } + else { /* use a backward barrier */ + black2gray(obj2gco(p)); /* make prototype gray (again) */ + p->gclist = g->grayagain; + g->grayagain = obj2gco(p); + } +} + + +/* +** check color (and invariants) for an upvalue that was closed, +** i.e., moved into the 'allgc' list +*/ +void luaC_checkupvalcolor (global_State *g, UpVal *uv) { + GCObject *o = obj2gco(uv); + lua_assert(!isblack(o)); /* open upvalues are never black */ + if (isgray(o)) { + if (keepinvariant(g)) { + resetoldbit(o); /* see MOVE OLD rule */ + gray2black(o); /* it is being visited now */ + markvalue(g, uv->v); + } + else { + lua_assert(issweepphase(g)); + makewhite(g, o); + } + } +} + + +/* +** create a new collectable object (with given type and size) and link +** it to '*list'. 'offset' tells how many bytes to allocate before the +** object itself (used only by states). +*/ +GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list, + int offset) { + global_State *g = G(L); + char *raw = cast(char *, luaM_newobject(L, novariant(tt), sz)); + GCObject *o = obj2gco(raw + offset); + if (list == NULL) + list = &g->allgc; /* standard list for collectable objects */ + gch(o)->marked = luaC_white(g); + gch(o)->tt = tt; + gch(o)->next = *list; + *list = o; + return o; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Mark functions +** ======================================================= +*/ + + +/* +** mark an object. Userdata, strings, and closed upvalues are visited +** and turned black here. Other objects are marked gray and added +** to appropriate list to be visited (and turned black) later. (Open +** upvalues are already linked in 'headuv' list.) +*/ +static void reallymarkobject (global_State *g, GCObject *o) { + lu_mem size; + white2gray(o); + switch (gch(o)->tt) { + case LUA_TSHRSTR: + case LUA_TLNGSTR: { + size = sizestring(gco2ts(o)); + break; /* nothing else to mark; make it black */ + } + case LUA_TUSERDATA: { + Table *mt = gco2u(o)->metatable; + markobject(g, mt); + markobject(g, gco2u(o)->env); + size = sizeudata(gco2u(o)); + break; + } + case LUA_TUPVAL: { + UpVal *uv = gco2uv(o); + markvalue(g, uv->v); + if (uv->v != &uv->u.value) /* open? */ + return; /* open upvalues remain gray */ + size = sizeof(UpVal); + break; + } + case LUA_TLCL: { + gco2lcl(o)->gclist = g->gray; + g->gray = o; + return; + } + case LUA_TCCL: { + gco2ccl(o)->gclist = g->gray; + g->gray = o; + return; + } + case LUA_TTABLE: { + linktable(gco2t(o), &g->gray); + return; + } + case LUA_TTHREAD: { + gco2th(o)->gclist = g->gray; + g->gray = o; + return; + } + case LUA_TPROTO: { + gco2p(o)->gclist = g->gray; + g->gray = o; + return; + } + default: lua_assert(0); return; + } + gray2black(o); + g->GCmemtrav += size; +} + + +/* +** mark metamethods for basic types +*/ +static void markmt (global_State *g) { + int i; + for (i=0; i < LUA_NUMTAGS; i++) + markobject(g, g->mt[i]); +} + + +/* +** mark all objects in list of being-finalized +*/ +static void markbeingfnz (global_State *g) { + GCObject *o; + for (o = g->tobefnz; o != NULL; o = gch(o)->next) { + makewhite(g, o); + reallymarkobject(g, o); + } +} + + +/* +** mark all values stored in marked open upvalues. (See comment in +** 'lstate.h'.) +*/ +static void remarkupvals (global_State *g) { + UpVal *uv; + for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { + if (isgray(obj2gco(uv))) + markvalue(g, uv->v); + } +} + + +/* +** mark root set and reset all gray lists, to start a new +** incremental (or full) collection +*/ +static void restartcollection (global_State *g) { + g->gray = g->grayagain = NULL; + g->weak = g->allweak = g->ephemeron = NULL; + markobject(g, g->mainthread); + markvalue(g, &g->l_registry); + markmt(g); + markbeingfnz(g); /* mark any finalizing object left from previous cycle */ +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Traverse functions +** ======================================================= +*/ + +static void traverseweakvalue (global_State *g, Table *h) { + Node *n, *limit = gnodelast(h); + /* if there is array part, assume it may have white values (do not + traverse it just to check) */ + int hasclears = (h->sizearray > 0); + for (n = gnode(h, 0); n < limit; n++) { + checkdeadkey(n); + if (ttisnil(gval(n))) /* entry is empty? */ + removeentry(n); /* remove it */ + else { + lua_assert(!ttisnil(gkey(n))); + markvalue(g, gkey(n)); /* mark key */ + if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */ + hasclears = 1; /* table will have to be cleared */ + } + } + if (hasclears) + linktable(h, &g->weak); /* has to be cleared later */ + else /* no white values */ + linktable(h, &g->grayagain); /* no need to clean */ +} + + +static int traverseephemeron (global_State *g, Table *h) { + int marked = 0; /* true if an object is marked in this traversal */ + int hasclears = 0; /* true if table has white keys */ + int prop = 0; /* true if table has entry "white-key -> white-value" */ + Node *n, *limit = gnodelast(h); + int i; + /* traverse array part (numeric keys are 'strong') */ + for (i = 0; i < h->sizearray; i++) { + if (valiswhite(&h->array[i])) { + marked = 1; + reallymarkobject(g, gcvalue(&h->array[i])); + } + } + /* traverse hash part */ + for (n = gnode(h, 0); n < limit; n++) { + checkdeadkey(n); + if (ttisnil(gval(n))) /* entry is empty? */ + removeentry(n); /* remove it */ + else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */ + hasclears = 1; /* table must be cleared */ + if (valiswhite(gval(n))) /* value not marked yet? */ + prop = 1; /* must propagate again */ + } + else if (valiswhite(gval(n))) { /* value not marked yet? */ + marked = 1; + reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ + } + } + if (g->gcstate != GCSatomic || prop) + linktable(h, &g->ephemeron); /* have to propagate again */ + else if (hasclears) /* does table have white keys? */ + linktable(h, &g->allweak); /* may have to clean white keys */ + else /* no white keys */ + linktable(h, &g->grayagain); /* no need to clean */ + return marked; +} + + +static void traversestrongtable (global_State *g, Table *h) { + Node *n, *limit = gnodelast(h); + int i; + for (i = 0; i < h->sizearray; i++) /* traverse array part */ + markvalue(g, &h->array[i]); + for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ + checkdeadkey(n); + if (ttisnil(gval(n))) /* entry is empty? */ + removeentry(n); /* remove it */ + else { + lua_assert(!ttisnil(gkey(n))); + markvalue(g, gkey(n)); /* mark key */ + markvalue(g, gval(n)); /* mark value */ + } + } +} + + +static lu_mem traversetable (global_State *g, Table *h) { + const char *weakkey, *weakvalue; + const TValue *mode = gfasttm(g, h->metatable, TM_MODE); + markobject(g, h->metatable); + if (mode && ttisstring(mode) && /* is there a weak mode? */ + ((weakkey = strchr(svalue(mode), 'k')), + (weakvalue = strchr(svalue(mode), 'v')), + (weakkey || weakvalue))) { /* is really weak? */ + black2gray(obj2gco(h)); /* keep table gray */ + if (!weakkey) /* strong keys? */ + traverseweakvalue(g, h); + else if (!weakvalue) /* strong values? */ + traverseephemeron(g, h); + else /* all weak */ + linktable(h, &g->allweak); /* nothing to traverse now */ + } + else /* not weak */ + traversestrongtable(g, h); + return sizeof(Table) + sizeof(TValue) * h->sizearray + + sizeof(Node) * cast(size_t, sizenode(h)); +} + + +static int traverseproto (global_State *g, Proto *f) { + int i; + if (f->cache && iswhite(obj2gco(f->cache))) + f->cache = NULL; /* allow cache to be collected */ + markobject(g, f->source); + for (i = 0; i < f->sizek; i++) /* mark literals */ + markvalue(g, &f->k[i]); + for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ + markobject(g, f->upvalues[i].name); + for (i = 0; i < f->sizep; i++) /* mark nested protos */ + markobject(g, f->p[i]); + for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ + markobject(g, f->locvars[i].varname); + return sizeof(Proto) + sizeof(Instruction) * f->sizecode + + sizeof(Proto *) * f->sizep + + sizeof(TValue) * f->sizek + + sizeof(int) * f->sizelineinfo + + sizeof(LocVar) * f->sizelocvars + + sizeof(Upvaldesc) * f->sizeupvalues; +} + + +static lu_mem traverseCclosure (global_State *g, CClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ + markvalue(g, &cl->upvalue[i]); + return sizeCclosure(cl->nupvalues); +} + +static lu_mem traverseLclosure (global_State *g, LClosure *cl) { + int i; + markobject(g, cl->p); /* mark its prototype */ + for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ + markobject(g, cl->upvals[i]); + return sizeLclosure(cl->nupvalues); +} + + +static lu_mem traversestack (global_State *g, lua_State *th) { + int n = 0; + StkId o = th->stack; + if (o == NULL) + return 1; /* stack not completely built yet */ + for (; o < th->top; o++) /* mark live elements in the stack */ + markvalue(g, o); + if (g->gcstate == GCSatomic) { /* final traversal? */ + StkId lim = th->stack + th->stacksize; /* real end of stack */ + for (; o < lim; o++) /* clear not-marked stack slice */ + setnilvalue(o); + } + else { /* count call infos to compute size */ + CallInfo *ci; + for (ci = &th->base_ci; ci != th->ci; ci = ci->next) + n++; + } + return sizeof(lua_State) + sizeof(TValue) * th->stacksize + + sizeof(CallInfo) * n; +} + + +/* +** traverse one gray object, turning it to black (except for threads, +** which are always gray). +*/ +static void propagatemark (global_State *g) { + lu_mem size; + GCObject *o = g->gray; + lua_assert(isgray(o)); + gray2black(o); + switch (gch(o)->tt) { + case LUA_TTABLE: { + Table *h = gco2t(o); + g->gray = h->gclist; /* remove from 'gray' list */ + size = traversetable(g, h); + break; + } + case LUA_TLCL: { + LClosure *cl = gco2lcl(o); + g->gray = cl->gclist; /* remove from 'gray' list */ + size = traverseLclosure(g, cl); + break; + } + case LUA_TCCL: { + CClosure *cl = gco2ccl(o); + g->gray = cl->gclist; /* remove from 'gray' list */ + size = traverseCclosure(g, cl); + break; + } + case LUA_TTHREAD: { + lua_State *th = gco2th(o); + g->gray = th->gclist; /* remove from 'gray' list */ + th->gclist = g->grayagain; + g->grayagain = o; /* insert into 'grayagain' list */ + black2gray(o); + size = traversestack(g, th); + break; + } + case LUA_TPROTO: { + Proto *p = gco2p(o); + g->gray = p->gclist; /* remove from 'gray' list */ + size = traverseproto(g, p); + break; + } + default: lua_assert(0); return; + } + g->GCmemtrav += size; +} + + +static void propagateall (global_State *g) { + while (g->gray) propagatemark(g); +} + + +static void propagatelist (global_State *g, GCObject *l) { + lua_assert(g->gray == NULL); /* no grays left */ + g->gray = l; + propagateall(g); /* traverse all elements from 'l' */ +} + +/* +** retraverse all gray lists. Because tables may be reinserted in other +** lists when traversed, traverse the original lists to avoid traversing +** twice the same table (which is not wrong, but inefficient) +*/ +static void retraversegrays (global_State *g) { + GCObject *weak = g->weak; /* save original lists */ + GCObject *grayagain = g->grayagain; + GCObject *ephemeron = g->ephemeron; + g->weak = g->grayagain = g->ephemeron = NULL; + propagateall(g); /* traverse main gray list */ + propagatelist(g, grayagain); + propagatelist(g, weak); + propagatelist(g, ephemeron); +} + + +static void convergeephemerons (global_State *g) { + int changed; + do { + GCObject *w; + GCObject *next = g->ephemeron; /* get ephemeron list */ + g->ephemeron = NULL; /* tables will return to this list when traversed */ + changed = 0; + while ((w = next) != NULL) { + next = gco2t(w)->gclist; + if (traverseephemeron(g, gco2t(w))) { /* traverse marked some value? */ + propagateall(g); /* propagate changes */ + changed = 1; /* will have to revisit all ephemeron tables */ + } + } + } while (changed); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Sweep Functions +** ======================================================= +*/ + + +/* +** clear entries with unmarked keys from all weaktables in list 'l' up +** to element 'f' +*/ +static void clearkeys (global_State *g, GCObject *l, GCObject *f) { + for (; l != f; l = gco2t(l)->gclist) { + Table *h = gco2t(l); + Node *n, *limit = gnodelast(h); + for (n = gnode(h, 0); n < limit; n++) { + if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) { + setnilvalue(gval(n)); /* remove value ... */ + removeentry(n); /* and remove entry from table */ + } + } + } +} + + +/* +** clear entries with unmarked values from all weaktables in list 'l' up +** to element 'f' +*/ +static void clearvalues (global_State *g, GCObject *l, GCObject *f) { + for (; l != f; l = gco2t(l)->gclist) { + Table *h = gco2t(l); + Node *n, *limit = gnodelast(h); + int i; + for (i = 0; i < h->sizearray; i++) { + TValue *o = &h->array[i]; + if (iscleared(g, o)) /* value was collected? */ + setnilvalue(o); /* remove value */ + } + for (n = gnode(h, 0); n < limit; n++) { + if (!ttisnil(gval(n)) && iscleared(g, gval(n))) { + setnilvalue(gval(n)); /* remove value ... */ + removeentry(n); /* and remove entry from table */ + } + } + } +} + + +static void freeobj (lua_State *L, GCObject *o) { + switch (gch(o)->tt) { + case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; + case LUA_TLCL: { + luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues)); + break; + } + case LUA_TCCL: { + luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); + break; + } + case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; + case LUA_TTABLE: luaH_free(L, gco2t(o)); break; + case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break; + case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break; + case LUA_TSHRSTR: + G(L)->strt.nuse--; + /* go through */ + case LUA_TLNGSTR: { + luaM_freemem(L, o, sizestring(gco2ts(o))); + break; + } + default: lua_assert(0); + } +} + + +#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) +static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count); + + +/* +** sweep the (open) upvalues of a thread and resize its stack and +** list of call-info structures. +*/ +static void sweepthread (lua_State *L, lua_State *L1) { + if (L1->stack == NULL) return; /* stack not completely built yet */ + sweepwholelist(L, &L1->openupval); /* sweep open upvalues */ + luaE_freeCI(L1); /* free extra CallInfo slots */ + /* should not change the stack during an emergency gc cycle */ + if (G(L)->gckind != KGC_EMERGENCY) + luaD_shrinkstack(L1); +} + + +/* +** sweep at most 'count' elements from a list of GCObjects erasing dead +** objects, where a dead (not alive) object is one marked with the "old" +** (non current) white and not fixed. +** In non-generational mode, change all non-dead objects back to white, +** preparing for next collection cycle. +** In generational mode, keep black objects black, and also mark them as +** old; stop when hitting an old object, as all objects after that +** one will be old too. +** When object is a thread, sweep its list of open upvalues too. +*/ +static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { + global_State *g = G(L); + int ow = otherwhite(g); + int toclear, toset; /* bits to clear and to set in all live objects */ + int tostop; /* stop sweep when this is true */ + if (isgenerational(g)) { /* generational mode? */ + toclear = ~0; /* clear nothing */ + toset = bitmask(OLDBIT); /* set the old bit of all surviving objects */ + tostop = bitmask(OLDBIT); /* do not sweep old generation */ + } + else { /* normal mode */ + toclear = maskcolors; /* clear all color bits + old bit */ + toset = luaC_white(g); /* make object white */ + tostop = 0; /* do not stop */ + } + while (*p != NULL && count-- > 0) { + GCObject *curr = *p; + int marked = gch(curr)->marked; + if (isdeadm(ow, marked)) { /* is 'curr' dead? */ + *p = gch(curr)->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { + if (testbits(marked, tostop)) + return NULL; /* stop sweeping this list */ + if (gch(curr)->tt == LUA_TTHREAD) + sweepthread(L, gco2th(curr)); /* sweep thread's upvalues */ + /* update marks */ + gch(curr)->marked = cast_byte((marked & toclear) | toset); + p = &gch(curr)->next; /* go to next element */ + } + } + return (*p == NULL) ? NULL : p; +} + + +/* +** sweep a list until a live object (or end of list) +*/ +static GCObject **sweeptolive (lua_State *L, GCObject **p, int *n) { + GCObject ** old = p; + int i = 0; + do { + i++; + p = sweeplist(L, p, 1); + } while (p == old); + if (n) *n += i; + return p; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Finalization +** ======================================================= +*/ + +static void checkSizes (lua_State *L) { + global_State *g = G(L); + if (g->gckind != KGC_EMERGENCY) { /* do not change sizes in emergency */ + int hs = g->strt.size / 2; /* half the size of the string table */ + if (g->strt.nuse < cast(lu_int32, hs)) /* using less than that half? */ + luaS_resize(L, hs); /* halve its size */ + luaZ_freebuffer(L, &g->buff); /* free concatenation buffer */ + } +} + + +static GCObject *udata2finalize (global_State *g) { + GCObject *o = g->tobefnz; /* get first element */ + lua_assert(isfinalized(o)); + g->tobefnz = gch(o)->next; /* remove it from 'tobefnz' list */ + gch(o)->next = g->allgc; /* return it to 'allgc' list */ + g->allgc = o; + resetbit(gch(o)->marked, SEPARATED); /* mark that it is not in 'tobefnz' */ + lua_assert(!isold(o)); /* see MOVE OLD rule */ + if (!keepinvariantout(g)) /* not keeping invariant? */ + makewhite(g, o); /* "sweep" object */ + return o; +} + + +static void dothecall (lua_State *L, void *ud) { + UNUSED(ud); + luaD_call(L, L->top - 2, 0, 0); +} + + +static void GCTM (lua_State *L, int propagateerrors) { + global_State *g = G(L); + const TValue *tm; + TValue v; + setgcovalue(L, &v, udata2finalize(g)); + tm = luaT_gettmbyobj(L, &v, TM_GC); + if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */ + int status; + lu_byte oldah = L->allowhook; + int running = g->gcrunning; + L->allowhook = 0; /* stop debug hooks during GC metamethod */ + g->gcrunning = 0; /* avoid GC steps */ + setobj2s(L, L->top, tm); /* push finalizer... */ + setobj2s(L, L->top + 1, &v); /* ... and its argument */ + L->top += 2; /* and (next line) call the finalizer */ + status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); + L->allowhook = oldah; /* restore hooks */ + g->gcrunning = running; /* restore state */ + if (status != LUA_OK && propagateerrors) { /* error while running __gc? */ + if (status == LUA_ERRRUN) { /* is there an error object? */ + const char *msg = (ttisstring(L->top - 1)) + ? svalue(L->top - 1) + : "no message"; + luaO_pushfstring(L, "error in __gc metamethod (%s)", msg); + status = LUA_ERRGCMM; /* error in __gc metamethod */ + } + luaD_throw(L, status); /* re-throw error */ + } + } +} + + +/* +** move all unreachable objects (or 'all' objects) that need +** finalization from list 'finobj' to list 'tobefnz' (to be finalized) +*/ +static void separatetobefnz (lua_State *L, int all) { + global_State *g = G(L); + GCObject **p = &g->finobj; + GCObject *curr; + GCObject **lastnext = &g->tobefnz; + /* find last 'next' field in 'tobefnz' list (to add elements in its end) */ + while (*lastnext != NULL) + lastnext = &gch(*lastnext)->next; + while ((curr = *p) != NULL) { /* traverse all finalizable objects */ + lua_assert(!isfinalized(curr)); + lua_assert(testbit(gch(curr)->marked, SEPARATED)); + if (!(iswhite(curr) || all)) /* not being collected? */ + p = &gch(curr)->next; /* don't bother with it */ + else { + l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ + *p = gch(curr)->next; /* remove 'curr' from 'finobj' list */ + gch(curr)->next = *lastnext; /* link at the end of 'tobefnz' list */ + *lastnext = curr; + lastnext = &gch(curr)->next; + } + } +} + + +/* +** if object 'o' has a finalizer, remove it from 'allgc' list (must +** search the list to find it) and link it in 'finobj' list. +*/ +void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { + global_State *g = G(L); + if (testbit(gch(o)->marked, SEPARATED) || /* obj. is already separated... */ + isfinalized(o) || /* ... or is finalized... */ + gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ + return; /* nothing to be done */ + else { /* move 'o' to 'finobj' list */ + GCObject **p; + GCheader *ho = gch(o); + if (g->sweepgc == &ho->next) { /* avoid removing current sweep object */ + lua_assert(issweepphase(g)); + g->sweepgc = sweeptolive(L, g->sweepgc, NULL); + } + /* search for pointer pointing to 'o' */ + for (p = &g->allgc; *p != o; p = &gch(*p)->next) { /* empty */ } + *p = ho->next; /* remove 'o' from root list */ + ho->next = g->finobj; /* link it in list 'finobj' */ + g->finobj = o; + l_setbit(ho->marked, SEPARATED); /* mark it as such */ + if (!keepinvariantout(g)) /* not keeping invariant? */ + makewhite(g, o); /* "sweep" object */ + else + resetoldbit(o); /* see MOVE OLD rule */ + } +} + +/* }====================================================== */ + + +/* +** {====================================================== +** GC control +** ======================================================= +*/ + + +/* +** set a reasonable "time" to wait before starting a new GC cycle; +** cycle will start when memory use hits threshold +*/ +static void setpause (global_State *g, l_mem estimate) { + l_mem debt, threshold; + estimate = estimate / PAUSEADJ; /* adjust 'estimate' */ + threshold = (g->gcpause < MAX_LMEM / estimate) /* overflow? */ + ? estimate * g->gcpause /* no overflow */ + : MAX_LMEM; /* overflow; truncate to maximum */ + debt = -cast(l_mem, threshold - gettotalbytes(g)); + luaE_setdebt(g, debt); +} + + +#define sweepphases \ + (bitmask(GCSsweepstring) | bitmask(GCSsweepudata) | bitmask(GCSsweep)) + + +/* +** enter first sweep phase (strings) and prepare pointers for other +** sweep phases. The calls to 'sweeptolive' make pointers point to an +** object inside the list (instead of to the header), so that the real +** sweep do not need to skip objects created between "now" and the start +** of the real sweep. +** Returns how many objects it swept. +*/ +static int entersweep (lua_State *L) { + global_State *g = G(L); + int n = 0; + g->gcstate = GCSsweepstring; + lua_assert(g->sweepgc == NULL && g->sweepfin == NULL); + /* prepare to sweep strings, finalizable objects, and regular objects */ + g->sweepstrgc = 0; + g->sweepfin = sweeptolive(L, &g->finobj, &n); + g->sweepgc = sweeptolive(L, &g->allgc, &n); + return n; +} + + +/* +** change GC mode +*/ +void luaC_changemode (lua_State *L, int mode) { + global_State *g = G(L); + if (mode == g->gckind) return; /* nothing to change */ + if (mode == KGC_GEN) { /* change to generational mode */ + /* make sure gray lists are consistent */ + luaC_runtilstate(L, bitmask(GCSpropagate)); + g->GCestimate = gettotalbytes(g); + g->gckind = KGC_GEN; + } + else { /* change to incremental mode */ + /* sweep all objects to turn them back to white + (as white has not changed, nothing extra will be collected) */ + g->gckind = KGC_NORMAL; + entersweep(L); + luaC_runtilstate(L, ~sweepphases); + } +} + + +/* +** call all pending finalizers +*/ +static void callallpendingfinalizers (lua_State *L, int propagateerrors) { + global_State *g = G(L); + while (g->tobefnz) { + resetoldbit(g->tobefnz); + GCTM(L, propagateerrors); + } +} + + +void luaC_freeallobjects (lua_State *L) { + global_State *g = G(L); + int i; + separatetobefnz(L, 1); /* separate all objects with finalizers */ + lua_assert(g->finobj == NULL); + callallpendingfinalizers(L, 0); + g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */ + g->gckind = KGC_NORMAL; + sweepwholelist(L, &g->finobj); /* finalizers can create objs. in 'finobj' */ + sweepwholelist(L, &g->allgc); + for (i = 0; i < g->strt.size; i++) /* free all string lists */ + sweepwholelist(L, &g->strt.hash[i]); + lua_assert(g->strt.nuse == 0); +} + + +static l_mem atomic (lua_State *L) { + global_State *g = G(L); + l_mem work = -cast(l_mem, g->GCmemtrav); /* start counting work */ + GCObject *origweak, *origall; + lua_assert(!iswhite(obj2gco(g->mainthread))); + markobject(g, L); /* mark running thread */ + /* registry and global metatables may be changed by API */ + markvalue(g, &g->l_registry); + markmt(g); /* mark basic metatables */ + /* remark occasional upvalues of (maybe) dead threads */ + remarkupvals(g); + propagateall(g); /* propagate changes */ + work += g->GCmemtrav; /* stop counting (do not (re)count grays) */ + /* traverse objects caught by write barrier and by 'remarkupvals' */ + retraversegrays(g); + work -= g->GCmemtrav; /* restart counting */ + convergeephemerons(g); + /* at this point, all strongly accessible objects are marked. */ + /* clear values from weak tables, before checking finalizers */ + clearvalues(g, g->weak, NULL); + clearvalues(g, g->allweak, NULL); + origweak = g->weak; origall = g->allweak; + work += g->GCmemtrav; /* stop counting (objects being finalized) */ + separatetobefnz(L, 0); /* separate objects to be finalized */ + markbeingfnz(g); /* mark objects that will be finalized */ + propagateall(g); /* remark, to propagate `preserveness' */ + work -= g->GCmemtrav; /* restart counting */ + convergeephemerons(g); + /* at this point, all resurrected objects are marked. */ + /* remove dead objects from weak tables */ + clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */ + clearkeys(g, g->allweak, NULL); /* clear keys from all allweak tables */ + /* clear values from resurrected weak tables */ + clearvalues(g, g->weak, origweak); + clearvalues(g, g->allweak, origall); + g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ + work += g->GCmemtrav; /* complete counting */ + return work; /* estimate of memory marked by 'atomic' */ +} + + +static lu_mem singlestep (lua_State *L) { + global_State *g = G(L); + switch (g->gcstate) { + case GCSpause: { + /* start to count memory traversed */ + g->GCmemtrav = g->strt.size * sizeof(GCObject*); + lua_assert(!isgenerational(g)); + restartcollection(g); + g->gcstate = GCSpropagate; + return g->GCmemtrav; + } + case GCSpropagate: { + if (g->gray) { + lu_mem oldtrav = g->GCmemtrav; + propagatemark(g); + return g->GCmemtrav - oldtrav; /* memory traversed in this step */ + } + else { /* no more `gray' objects */ + lu_mem work; + int sw; + g->gcstate = GCSatomic; /* finish mark phase */ + g->GCestimate = g->GCmemtrav; /* save what was counted */; + work = atomic(L); /* add what was traversed by 'atomic' */ + g->GCestimate += work; /* estimate of total memory traversed */ + sw = entersweep(L); + return work + sw * GCSWEEPCOST; + } + } + case GCSsweepstring: { + int i; + for (i = 0; i < GCSWEEPMAX && g->sweepstrgc + i < g->strt.size; i++) + sweepwholelist(L, &g->strt.hash[g->sweepstrgc + i]); + g->sweepstrgc += i; + if (g->sweepstrgc >= g->strt.size) /* no more strings to sweep? */ + g->gcstate = GCSsweepudata; + return i * GCSWEEPCOST; + } + case GCSsweepudata: { + if (g->sweepfin) { + g->sweepfin = sweeplist(L, g->sweepfin, GCSWEEPMAX); + return GCSWEEPMAX*GCSWEEPCOST; + } + else { + g->gcstate = GCSsweep; + return 0; + } + } + case GCSsweep: { + if (g->sweepgc) { + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); + return GCSWEEPMAX*GCSWEEPCOST; + } + else { + /* sweep main thread */ + GCObject *mt = obj2gco(g->mainthread); + sweeplist(L, &mt, 1); + checkSizes(L); + g->gcstate = GCSpause; /* finish collection */ + return GCSWEEPCOST; + } + } + default: lua_assert(0); return 0; + } +} + + +/* +** advances the garbage collector until it reaches a state allowed +** by 'statemask' +*/ +void luaC_runtilstate (lua_State *L, int statesmask) { + global_State *g = G(L); + while (!testbit(statesmask, g->gcstate)) + singlestep(L); +} + + +static void generationalcollection (lua_State *L) { + global_State *g = G(L); + lua_assert(g->gcstate == GCSpropagate); + if (g->GCestimate == 0) { /* signal for another major collection? */ + luaC_fullgc(L, 0); /* perform a full regular collection */ + g->GCestimate = gettotalbytes(g); /* update control */ + } + else { + lu_mem estimate = g->GCestimate; + luaC_runtilstate(L, bitmask(GCSpause)); /* run complete (minor) cycle */ + g->gcstate = GCSpropagate; /* skip restart */ + if (gettotalbytes(g) > (estimate / 100) * g->gcmajorinc) + g->GCestimate = 0; /* signal for a major collection */ + else + g->GCestimate = estimate; /* keep estimate from last major coll. */ + + } + setpause(g, gettotalbytes(g)); + lua_assert(g->gcstate == GCSpropagate); +} + + +static void incstep (lua_State *L) { + global_State *g = G(L); + l_mem debt = g->GCdebt; + int stepmul = g->gcstepmul; + if (stepmul < 40) stepmul = 40; /* avoid ridiculous low values (and 0) */ + /* convert debt from Kb to 'work units' (avoid zero debt and overflows) */ + debt = (debt / STEPMULADJ) + 1; + debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM; + do { /* always perform at least one single step */ + lu_mem work = singlestep(L); /* do some work */ + debt -= work; + } while (debt > -GCSTEPSIZE && g->gcstate != GCSpause); + if (g->gcstate == GCSpause) + setpause(g, g->GCestimate); /* pause until next cycle */ + else { + debt = (debt / stepmul) * STEPMULADJ; /* convert 'work units' to Kb */ + luaE_setdebt(g, debt); + } +} + + +/* +** performs a basic GC step +*/ +void luaC_forcestep (lua_State *L) { + global_State *g = G(L); + int i; + if (isgenerational(g)) generationalcollection(L); + else incstep(L); + /* run a few finalizers (or all of them at the end of a collect cycle) */ + for (i = 0; g->tobefnz && (i < GCFINALIZENUM || g->gcstate == GCSpause); i++) + GCTM(L, 1); /* call one finalizer */ +} + + +/* +** performs a basic GC step only if collector is running +*/ +void luaC_step (lua_State *L) { + global_State *g = G(L); + if (g->gcrunning) luaC_forcestep(L); + else luaE_setdebt(g, -GCSTEPSIZE); /* avoid being called too often */ +} + + + +/* +** performs a full GC cycle; if "isemergency", does not call +** finalizers (which could change stack positions) +*/ +void luaC_fullgc (lua_State *L, int isemergency) { + global_State *g = G(L); + int origkind = g->gckind; + lua_assert(origkind != KGC_EMERGENCY); + if (isemergency) /* do not run finalizers during emergency GC */ + g->gckind = KGC_EMERGENCY; + else { + g->gckind = KGC_NORMAL; + callallpendingfinalizers(L, 1); + } + if (keepinvariant(g)) { /* may there be some black objects? */ + /* must sweep all objects to turn them back to white + (as white has not changed, nothing will be collected) */ + entersweep(L); + } + /* finish any pending sweep phase to start a new cycle */ + luaC_runtilstate(L, bitmask(GCSpause)); + luaC_runtilstate(L, ~bitmask(GCSpause)); /* start new collection */ + luaC_runtilstate(L, bitmask(GCSpause)); /* run entire collection */ + if (origkind == KGC_GEN) { /* generational mode? */ + /* generational mode must be kept in propagate phase */ + luaC_runtilstate(L, bitmask(GCSpropagate)); + } + g->gckind = origkind; + setpause(g, gettotalbytes(g)); + if (!isemergency) /* do not run finalizers during emergency GC */ + callallpendingfinalizers(L, 1); +} + +/* }====================================================== */ + + diff --git a/usr/src/uts/common/fs/zfs/lua/lgc.h b/usr/src/uts/common/fs/zfs/lua/lgc.h new file mode 100644 index 0000000000..84bb1cdf99 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lgc.h @@ -0,0 +1,157 @@ +/* +** $Id: lgc.h,v 2.58.1.1 2013/04/12 18:48:47 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#ifndef lgc_h +#define lgc_h + + +#include "lobject.h" +#include "lstate.h" + +/* +** Collectable objects may have one of three colors: white, which +** means the object is not marked; gray, which means the +** object is marked, but its references may be not marked; and +** black, which means that the object and all its references are marked. +** The main invariant of the garbage collector, while marking objects, +** is that a black object can never point to a white one. Moreover, +** any gray object must be in a "gray list" (gray, grayagain, weak, +** allweak, ephemeron) so that it can be visited again before finishing +** the collection cycle. These lists have no meaning when the invariant +** is not being enforced (e.g., sweep phase). +*/ + + + +/* how much to allocate before next GC step */ +#if !defined(GCSTEPSIZE) +/* ~100 small strings */ +#define GCSTEPSIZE (cast_int(100 * sizeof(TString))) +#endif + + +/* +** Possible states of the Garbage Collector +*/ +#define GCSpropagate 0 +#define GCSatomic 1 +#define GCSsweepstring 2 +#define GCSsweepudata 3 +#define GCSsweep 4 +#define GCSpause 5 + + +#define issweepphase(g) \ + (GCSsweepstring <= (g)->gcstate && (g)->gcstate <= GCSsweep) + +#define isgenerational(g) ((g)->gckind == KGC_GEN) + +/* +** macros to tell when main invariant (white objects cannot point to black +** ones) must be kept. During a non-generational collection, the sweep +** phase may break the invariant, as objects turned white may point to +** still-black objects. The invariant is restored when sweep ends and +** all objects are white again. During a generational collection, the +** invariant must be kept all times. +*/ + +#define keepinvariant(g) (isgenerational(g) || g->gcstate <= GCSatomic) + + +/* +** Outside the collector, the state in generational mode is kept in +** 'propagate', so 'keepinvariant' is always true. +*/ +#define keepinvariantout(g) \ + check_exp(g->gcstate == GCSpropagate || !isgenerational(g), \ + g->gcstate <= GCSatomic) + + +/* +** some useful bit tricks +*/ +#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) + + +/* Layout for bit use in `marked' field: */ +#define WHITE0BIT 0 /* object is white (type 0) */ +#define WHITE1BIT 1 /* object is white (type 1) */ +#define BLACKBIT 2 /* object is black */ +#define FINALIZEDBIT 3 /* object has been separated for finalization */ +#define SEPARATED 4 /* object is in 'finobj' list or in 'tobefnz' */ +#define FIXEDBIT 5 /* object is fixed (should not be collected) */ +#define OLDBIT 6 /* object is old (only in generational mode) */ +/* bit 7 is currently used by tests (luaL_checkmemory) */ + +#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) + + +#define iswhite(x) testbits((x)->gch.marked, WHITEBITS) +#define isblack(x) testbit((x)->gch.marked, BLACKBIT) +#define isgray(x) /* neither white nor black */ \ + (!testbits((x)->gch.marked, WHITEBITS | bitmask(BLACKBIT))) + +#define isold(x) testbit((x)->gch.marked, OLDBIT) + +/* MOVE OLD rule: whenever an object is moved to the beginning of + a GC list, its old bit must be cleared */ +#define resetoldbit(o) resetbit((o)->gch.marked, OLDBIT) + +#define otherwhite(g) (g->currentwhite ^ WHITEBITS) +#define isdeadm(ow,m) (!(((m) ^ WHITEBITS) & (ow))) +#define isdead(g,v) isdeadm(otherwhite(g), (v)->gch.marked) + +#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) +#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) + +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) + +#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) + + +#define luaC_condGC(L,c) \ + {if (G(L)->GCdebt > 0) {c;}; condchangemem(L);} +#define luaC_checkGC(L) luaC_condGC(L, luaC_step(L);) + + +#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ + luaC_barrier_(L,obj2gco(p),gcvalue(v)); } + +#define luaC_barrierback(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ + luaC_barrierback_(L,p); } + +#define luaC_objbarrier(L,p,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ + luaC_barrier_(L,obj2gco(p),obj2gco(o)); } + +#define luaC_objbarrierback(L,p,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) luaC_barrierback_(L,p); } + +#define luaC_barrierproto(L,p,c) \ + { if (isblack(obj2gco(p))) luaC_barrierproto_(L,p,c); } + +LUAI_FUNC void luaC_freeallobjects (lua_State *L); +LUAI_FUNC void luaC_step (lua_State *L); +LUAI_FUNC void luaC_forcestep (lua_State *L); +LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); +LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); +LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, + GCObject **list, int offset); +LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); +LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); +LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c); +LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); +LUAI_FUNC void luaC_checkupvalcolor (global_State *g, UpVal *uv); +LUAI_FUNC void luaC_changemode (lua_State *L, int mode); + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/llex.c b/usr/src/uts/common/fs/zfs/lua/llex.c new file mode 100644 index 0000000000..c84844aec5 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/llex.c @@ -0,0 +1,528 @@ +/* +** $Id: llex.c,v 2.63.1.3 2015/02/09 17:56:34 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#include <sys/zfs_context.h> + +#define llex_c +#define LUA_CORE + +#include "lua.h" + +#include "lctype.h" +#include "ldo.h" +#include "llex.h" +#include "lobject.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lzio.h" + + + +#define next(ls) (ls->current = zgetc(ls->z)) + + + +#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') + + +/* ORDER RESERVED */ +static const char *const luaX_tokens [] = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "goto", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "..", "...", "==", ">=", "<=", "~=", "::", "<eof>", + "<number>", "<name>", "<string>" +}; + + +#define save_and_next(ls) (save(ls, ls->current), next(ls)) + + +static l_noret lexerror (LexState *ls, const char *msg, int token); + + +static void save (LexState *ls, int c) { + Mbuffer *b = ls->buff; + if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { + size_t newsize; + if (luaZ_sizebuffer(b) >= MAX_SIZET/2) + lexerror(ls, "lexical element too long", 0); + newsize = luaZ_sizebuffer(b) * 2; + luaZ_resizebuffer(ls->L, b, newsize); + } + b->buffer[luaZ_bufflen(b)++] = cast(char, c); +} + + +void luaX_init (lua_State *L) { + int i; + for (i=0; i<NUM_RESERVED; i++) { + TString *ts = luaS_new(L, luaX_tokens[i]); + luaS_fix(ts); /* reserved words are never collected */ + ts->tsv.extra = cast_byte(i+1); /* reserved word */ + } +} + + +const char *luaX_token2str (LexState *ls, int token) { + if (token < FIRST_RESERVED) { /* single-byte symbols? */ + lua_assert(token == cast(unsigned char, token)); + return (lisprint(token)) ? luaO_pushfstring(ls->L, LUA_QL("%c"), token) : + luaO_pushfstring(ls->L, "char(%d)", token); + } + else { + const char *s = luaX_tokens[token - FIRST_RESERVED]; + if (token < TK_EOS) /* fixed format (symbols and reserved words)? */ + return luaO_pushfstring(ls->L, LUA_QS, s); + else /* names, strings, and numerals */ + return s; + } +} + + +static const char *txtToken (LexState *ls, int token) { + switch (token) { + case TK_NAME: + case TK_STRING: + case TK_NUMBER: + save(ls, '\0'); + return luaO_pushfstring(ls->L, LUA_QS, luaZ_buffer(ls->buff)); + default: + return luaX_token2str(ls, token); + } +} + + +static l_noret lexerror (LexState *ls, const char *msg, int token) { + char buff[LUA_IDSIZE]; + luaO_chunkid(buff, getstr(ls->source), LUA_IDSIZE); + msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg); + if (token) + luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token)); + luaD_throw(ls->L, LUA_ERRSYNTAX); +} + + +l_noret luaX_syntaxerror (LexState *ls, const char *msg) { + lexerror(ls, msg, ls->t.token); +} + + +/* +** creates a new string and anchors it in function's table so that +** it will not be collected until the end of the function's compilation +** (by that time it should be anchored in function's prototype) +*/ +TString *luaX_newstring (LexState *ls, const char *str, size_t l) { + lua_State *L = ls->L; + TValue *o; /* entry for `str' */ + TString *ts = luaS_newlstr(L, str, l); /* create new string */ + setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */ + o = luaH_set(L, ls->fs->h, L->top - 1); + if (ttisnil(o)) { /* not in use yet? (see 'addK') */ + /* boolean value does not need GC barrier; + table has no metatable, so it does not need to invalidate cache */ + setbvalue(o, 1); /* t[string] = true */ + luaC_checkGC(L); + } + else { /* string already present */ + ts = rawtsvalue(keyfromval(o)); /* re-use value previously stored */ + } + L->top--; /* remove string from stack */ + return ts; +} + + +/* +** increment line number and skips newline sequence (any of +** \n, \r, \n\r, or \r\n) +*/ +static void inclinenumber (LexState *ls) { + int old = ls->current; + lua_assert(currIsNewline(ls)); + next(ls); /* skip `\n' or `\r' */ + if (currIsNewline(ls) && ls->current != old) + next(ls); /* skip `\n\r' or `\r\n' */ + if (++ls->linenumber >= MAX_INT) + lexerror(ls, "chunk has too many lines", 0); +} + + +void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, + int firstchar) { + ls->decpoint = '.'; + ls->L = L; + ls->current = firstchar; + ls->lookahead.token = TK_EOS; /* no look-ahead token */ + ls->z = z; + ls->fs = NULL; + ls->linenumber = 1; + ls->lastline = 1; + ls->source = source; + ls->envn = luaS_new(L, LUA_ENV); /* create env name */ + luaS_fix(ls->envn); /* never collect this name */ + luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ +} + + + +/* +** ======================================================= +** LEXICAL ANALYZER +** ======================================================= +*/ + + + +static int check_next (LexState *ls, const char *set) { + if (ls->current == '\0' || !strchr(set, ls->current)) + return 0; + save_and_next(ls); + return 1; +} + + +/* +** change all characters 'from' in buffer to 'to' +*/ +static void buffreplace (LexState *ls, char from, char to) { + size_t n = luaZ_bufflen(ls->buff); + char *p = luaZ_buffer(ls->buff); + while (n--) + if (p[n] == from) p[n] = to; +} + + +#if !defined(getlocaledecpoint) +#define getlocaledecpoint() (localeconv()->decimal_point[0]) +#endif + + +#define buff2d(b,e) luaO_str2d(luaZ_buffer(b), luaZ_bufflen(b) - 1, e) + +/* +** in case of format error, try to change decimal point separator to +** the one defined in the current locale and check again +*/ +static void trydecpoint (LexState *ls, SemInfo *seminfo) { + char old = ls->decpoint; + ls->decpoint = getlocaledecpoint(); + buffreplace(ls, old, ls->decpoint); /* try new decimal separator */ + if (!buff2d(ls->buff, &seminfo->r)) { + /* format error with correct decimal point: no more options */ + buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ + lexerror(ls, "malformed number", TK_NUMBER); + } +} + + +/* LUA_NUMBER */ +/* +** this function is quite liberal in what it accepts, as 'luaO_str2d' +** will reject ill-formed numerals. +*/ +static void read_numeral (LexState *ls, SemInfo *seminfo) { + const char *expo = "Ee"; + int first = ls->current; + lua_assert(lisdigit(ls->current)); + save_and_next(ls); + if (first == '0' && check_next(ls, "Xx")) /* hexadecimal? */ + expo = "Pp"; + for (;;) { + if (check_next(ls, expo)) /* exponent part? */ + check_next(ls, "+-"); /* optional exponent sign */ + if (lisxdigit(ls->current) || ls->current == '.') + save_and_next(ls); + else break; + } + save(ls, '\0'); + buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ + if (!buff2d(ls->buff, &seminfo->r)) /* format error? */ + trydecpoint(ls, seminfo); /* try to update decimal point separator */ +} + + +/* +** skip a sequence '[=*[' or ']=*]' and return its number of '='s or +** -1 if sequence is malformed +*/ +static int skip_sep (LexState *ls) { + int count = 0; + int s = ls->current; + lua_assert(s == '[' || s == ']'); + save_and_next(ls); + while (ls->current == '=') { + save_and_next(ls); + count++; + } + return (ls->current == s) ? count : (-count) - 1; +} + + +static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { + save_and_next(ls); /* skip 2nd `[' */ + if (currIsNewline(ls)) /* string starts with a newline? */ + inclinenumber(ls); /* skip it */ + for (;;) { + switch (ls->current) { + case EOZ: + lexerror(ls, (seminfo) ? "unfinished long string" : + "unfinished long comment", TK_EOS); + break; /* to avoid warnings */ + case ']': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd `]' */ + goto endloop; + } + break; + } + case '\n': case '\r': { + save(ls, '\n'); + inclinenumber(ls); + if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ + break; + } + default: { + if (seminfo) save_and_next(ls); + else next(ls); + } + } + } endloop: + if (seminfo) + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), + luaZ_bufflen(ls->buff) - 2*(2 + sep)); +} + + +static void escerror (LexState *ls, int *c, int n, const char *msg) { + int i; + luaZ_resetbuffer(ls->buff); /* prepare error message */ + save(ls, '\\'); + for (i = 0; i < n && c[i] != EOZ; i++) + save(ls, c[i]); + lexerror(ls, msg, TK_STRING); +} + + +static int readhexaesc (LexState *ls) { + int c[3], i; /* keep input for error message */ + int r = 0; /* result accumulator */ + c[0] = 'x'; /* for error message */ + for (i = 1; i < 3; i++) { /* read two hexadecimal digits */ + c[i] = next(ls); + if (!lisxdigit(c[i])) + escerror(ls, c, i + 1, "hexadecimal digit expected"); + r = (r << 4) + luaO_hexavalue(c[i]); + } + return r; +} + + +static int readdecesc (LexState *ls) { + int c[3], i; + int r = 0; /* result accumulator */ + for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */ + c[i] = ls->current; + r = 10*r + c[i] - '0'; + next(ls); + } + if (r > UCHAR_MAX) + escerror(ls, c, i, "decimal escape too large"); + return r; +} + + +static void read_string (LexState *ls, int del, SemInfo *seminfo) { + save_and_next(ls); /* keep delimiter (for error messages) */ + while (ls->current != del) { + switch (ls->current) { + case EOZ: + lexerror(ls, "unfinished string", TK_EOS); + break; /* to avoid warnings */ + case '\n': + case '\r': + lexerror(ls, "unfinished string", TK_STRING); + break; /* to avoid warnings */ + case '\\': { /* escape sequences */ + int c; /* final character to be saved */ + next(ls); /* do not save the `\' */ + switch (ls->current) { + case 'a': c = '\a'; goto read_save; + case 'b': c = '\b'; goto read_save; + case 'f': c = '\f'; goto read_save; + case 'n': c = '\n'; goto read_save; + case 'r': c = '\r'; goto read_save; + case 't': c = '\t'; goto read_save; + case 'v': c = '\v'; goto read_save; + case 'x': c = readhexaesc(ls); goto read_save; + case '\n': case '\r': + inclinenumber(ls); c = '\n'; goto only_save; + case '\\': case '\"': case '\'': + c = ls->current; goto read_save; + case EOZ: goto no_save; /* will raise an error next loop */ + case 'z': { /* zap following span of spaces */ + next(ls); /* skip the 'z' */ + while (lisspace(ls->current)) { + if (currIsNewline(ls)) inclinenumber(ls); + else next(ls); + } + goto no_save; + } + default: { + if (!lisdigit(ls->current)) + escerror(ls, &ls->current, 1, "invalid escape sequence"); + /* digital escape \ddd */ + c = readdecesc(ls); + goto only_save; + } + } + read_save: next(ls); /* read next character */ + only_save: save(ls, c); /* save 'c' */ + no_save: break; + } + default: + save_and_next(ls); + } + } + save_and_next(ls); /* skip delimiter */ + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, + luaZ_bufflen(ls->buff) - 2); +} + + +static int llex (LexState *ls, SemInfo *seminfo) { + luaZ_resetbuffer(ls->buff); + for (;;) { + switch (ls->current) { + case '\n': case '\r': { /* line breaks */ + inclinenumber(ls); + break; + } + case ' ': case '\f': case '\t': case '\v': { /* spaces */ + next(ls); + break; + } + case '-': { /* '-' or '--' (comment) */ + next(ls); + if (ls->current != '-') return '-'; + /* else is a comment */ + next(ls); + if (ls->current == '[') { /* long comment? */ + int sep = skip_sep(ls); + luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ + if (sep >= 0) { + read_long_string(ls, NULL, sep); /* skip long comment */ + luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */ + break; + } + } + /* else short comment */ + while (!currIsNewline(ls) && ls->current != EOZ) + next(ls); /* skip until end of line (or end of file) */ + break; + } + case '[': { /* long string or simply '[' */ + int sep = skip_sep(ls); + if (sep >= 0) { + read_long_string(ls, seminfo, sep); + return TK_STRING; + } + else if (sep == -1) return '['; + else lexerror(ls, "invalid long string delimiter", TK_STRING); + } + case '=': { + next(ls); + if (ls->current != '=') return '='; + else { next(ls); return TK_EQ; } + } + case '<': { + next(ls); + if (ls->current != '=') return '<'; + else { next(ls); return TK_LE; } + } + case '>': { + next(ls); + if (ls->current != '=') return '>'; + else { next(ls); return TK_GE; } + } + case '~': { + next(ls); + if (ls->current != '=') return '~'; + else { next(ls); return TK_NE; } + } + case ':': { + next(ls); + if (ls->current != ':') return ':'; + else { next(ls); return TK_DBCOLON; } + } + case '"': case '\'': { /* short literal strings */ + read_string(ls, ls->current, seminfo); + return TK_STRING; + } + case '.': { /* '.', '..', '...', or number */ + save_and_next(ls); + if (check_next(ls, ".")) { + if (check_next(ls, ".")) + return TK_DOTS; /* '...' */ + else return TK_CONCAT; /* '..' */ + } + else if (!lisdigit(ls->current)) return '.'; + /* else go through */ + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + read_numeral(ls, seminfo); + return TK_NUMBER; + } + case EOZ: { + return TK_EOS; + } + default: { + if (lislalpha(ls->current)) { /* identifier or reserved word? */ + TString *ts; + do { + save_and_next(ls); + } while (lislalnum(ls->current)); + ts = luaX_newstring(ls, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff)); + seminfo->ts = ts; + if (isreserved(ts)) /* reserved word? */ + return ts->tsv.extra - 1 + FIRST_RESERVED; + else { + return TK_NAME; + } + } + else { /* single-char tokens (+ - / ...) */ + int c = ls->current; + next(ls); + return c; + } + } + } + } +} + + +void luaX_next (LexState *ls) { + ls->lastline = ls->linenumber; + if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + ls->t = ls->lookahead; /* use this one */ + ls->lookahead.token = TK_EOS; /* and discharge it */ + } + else + ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ +} + + +int luaX_lookahead (LexState *ls) { + lua_assert(ls->lookahead.token == TK_EOS); + ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); + return ls->lookahead.token; +} + diff --git a/usr/src/uts/common/fs/zfs/lua/llex.h b/usr/src/uts/common/fs/zfs/lua/llex.h new file mode 100644 index 0000000000..a4acdd3021 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/llex.h @@ -0,0 +1,78 @@ +/* +** $Id: llex.h,v 1.72.1.1 2013/04/12 18:48:47 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#ifndef llex_h +#define llex_h + +#include "lobject.h" +#include "lzio.h" + + +#define FIRST_RESERVED 257 + + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER RESERVED" +*/ +enum RESERVED { + /* terminal symbols denoted by reserved words */ + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, + TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, + TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + /* other terminal symbols */ + TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_DBCOLON, TK_EOS, + TK_NUMBER, TK_NAME, TK_STRING +}; + +/* number of reserved words */ +#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) + + +typedef union { + lua_Number r; + TString *ts; +} SemInfo; /* semantics information */ + + +typedef struct Token { + int token; + SemInfo seminfo; +} Token; + + +/* state of the lexer plus state of the parser when shared by all + functions */ +typedef struct LexState { + int current; /* current character (charint) */ + int linenumber; /* input line counter */ + int lastline; /* line of last token `consumed' */ + Token t; /* current token */ + Token lookahead; /* look ahead token */ + struct FuncState *fs; /* current function (parser) */ + struct lua_State *L; + ZIO *z; /* input stream */ + Mbuffer *buff; /* buffer for tokens */ + struct Dyndata *dyd; /* dynamic structures used by the parser */ + TString *source; /* current source name */ + TString *envn; /* environment variable name */ + char decpoint; /* locale decimal point */ +} LexState; + + +LUAI_FUNC void luaX_init (lua_State *L); +LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, + TString *source, int firstchar); +LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); +LUAI_FUNC void luaX_next (LexState *ls); +LUAI_FUNC int luaX_lookahead (LexState *ls); +LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s); +LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/llimits.h b/usr/src/uts/common/fs/zfs/lua/llimits.h new file mode 100644 index 0000000000..4277c1fd03 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/llimits.h @@ -0,0 +1,308 @@ +/* +** $Id: llimits.h,v 1.103.1.1 2013/04/12 18:48:47 roberto Exp $ +** Limits, basic types, and some other `installation-dependent' definitions +** See Copyright Notice in lua.h +*/ + +#ifndef llimits_h +#define llimits_h + + +#include <sys/zfs_context.h> + +#include "lua.h" + + +typedef unsigned LUA_INT32 lu_int32; + +typedef LUAI_UMEM lu_mem; + +typedef LUAI_MEM l_mem; + + + +/* chars used as small naturals (so that `char' is reserved for characters) */ +typedef unsigned char lu_byte; + + +#define MAX_SIZET ((size_t)(~(size_t)0)-2) + +#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) + +#define MAX_LMEM ((l_mem) ((MAX_LUMEM >> 1) - 2)) + + +#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ + +/* +** conversion of pointer to integer +** this is for hashing only; there is no problem if the integer +** cannot hold the whole pointer value +*/ +#define IntPoint(p) ((unsigned int)(lu_mem)(p)) + + + +/* type to ensure maximum alignment */ +#if !defined(LUAI_USER_ALIGNMENT_T) +#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } +#endif + +typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; + + +/* result of a `usual argument conversion' over lua_Number */ +typedef LUAI_UACNUMBER l_uacNumber; + + +/* internal assertions for in-house debugging */ +#if defined(lua_assert) +#define check_exp(c,e) (lua_assert(c), (e)) +/* to avoid problems with conditions too long */ +#define lua_longassert(c) { if (!(c)) lua_assert(0); } +#else +#define lua_assert(c) ((void)0) +#define check_exp(c,e) (e) +#define lua_longassert(c) ((void)0) +#endif + +/* +** assertion for checking API calls +*/ +#if !defined(luai_apicheck) + +#if defined(LUA_USE_APICHECK) +#include <assert.h> +#define luai_apicheck(L,e) assert(e) +#else +#define luai_apicheck(L,e) lua_assert(e) +#endif + +#endif + +#define api_check(l,e,msg) luai_apicheck(l,(e) && msg) + + +#if !defined(UNUSED) +#define UNUSED(x) ((void)(x)) /* to avoid warnings */ +#endif + + +#define cast(t, exp) ((t)(exp)) + +#define cast_byte(i) cast(lu_byte, (i)) +#define cast_num(i) cast(lua_Number, (i)) +#define cast_int(i) cast(int, (i)) +#define cast_uchar(i) cast(unsigned char, (i)) + + +/* +** non-return type +*/ +#if defined(__GNUC__) +#define l_noret void __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define l_noret void __declspec(noreturn) +#else +#define l_noret void +#endif + + + +/* +** maximum depth for nested C calls and syntactical nested non-terminals +** in a program. (Value must fit in an unsigned short int.) +** +** Note: On amd64 platform, the limit has been measured to be 45. We set +** the maximum lower to give a margin for changing the amount of stack +** used by various functions involved in parsing and executing code. +*/ +#if !defined(LUAI_MAXCCALLS) +#define LUAI_MAXCCALLS 20 +#endif + +/* +** maximum number of upvalues in a closure (both C and Lua). (Value +** must fit in an unsigned char.) +*/ +#define MAXUPVAL UCHAR_MAX + + +/* +** type for virtual-machine instructions +** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) +*/ +typedef lu_int32 Instruction; + + + +/* maximum stack for a Lua function */ +#define MAXSTACK 250 + + + +/* minimum size for the string table (must be power of 2) */ +#if !defined(MINSTRTABSIZE) +#define MINSTRTABSIZE 32 +#endif + + +/* minimum size for string buffer */ +#if !defined(LUA_MINBUFFER) +#define LUA_MINBUFFER 32 +#endif + + +#if !defined(lua_lock) +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + +#if !defined(luai_threadyield) +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + + +/* +** these macros allow user-specific actions on threads when you defined +** LUAI_EXTRASPACE and need to do something extra when a thread is +** created/deleted/resumed/yielded. +*/ +#if !defined(luai_userstateopen) +#define luai_userstateopen(L) ((void)L) +#endif + +#if !defined(luai_userstateclose) +#define luai_userstateclose(L) ((void)L) +#endif + +#if !defined(luai_userstatethread) +#define luai_userstatethread(L,L1) ((void)L) +#endif + +#if !defined(luai_userstatefree) +#define luai_userstatefree(L,L1) ((void)L) +#endif + +#if !defined(luai_userstateresume) +#define luai_userstateresume(L,n) ((void)L) +#endif + +#if !defined(luai_userstateyield) +#define luai_userstateyield(L,n) ((void)L) +#endif + +/* +** lua_number2int is a macro to convert lua_Number to int. +** lua_number2integer is a macro to convert lua_Number to lua_Integer. +** lua_number2unsigned is a macro to convert a lua_Number to a lua_Unsigned. +** lua_unsigned2number is a macro to convert a lua_Unsigned to a lua_Number. +** luai_hashnum is a macro to hash a lua_Number value into an integer. +** The hash must be deterministic and give reasonable values for +** both small and large values (outside the range of integers). +*/ + +#if defined(MS_ASMTRICK) || defined(LUA_MSASMTRICK) /* { */ +/* trick with Microsoft assembler for X86 */ + +#define lua_number2int(i,n) __asm {__asm fld n __asm fistp i} +#define lua_number2integer(i,n) lua_number2int(i, n) +#define lua_number2unsigned(i,n) \ + {__int64 l; __asm {__asm fld n __asm fistp l} i = (unsigned int)l;} + + +#elif defined(LUA_IEEE754TRICK) /* }{ */ +/* the next trick should work on any machine using IEEE754 with + a 32-bit int type */ + +union luai_Cast { double l_d; LUA_INT32 l_p[2]; }; + +#if !defined(LUA_IEEEENDIAN) /* { */ +#define LUAI_EXTRAIEEE \ + static const union luai_Cast ieeeendian = {-(33.0 + 6755399441055744.0)}; +#define LUA_IEEEENDIANLOC (ieeeendian.l_p[1] == 33) +#else +#define LUA_IEEEENDIANLOC LUA_IEEEENDIAN +#define LUAI_EXTRAIEEE /* empty */ +#endif /* } */ + +#define lua_number2int32(i,n,t) \ + { LUAI_EXTRAIEEE \ + volatile union luai_Cast u; u.l_d = (n) + 6755399441055744.0; \ + (i) = (t)u.l_p[LUA_IEEEENDIANLOC]; } + +#define luai_hashnum(i,n) \ + { volatile union luai_Cast u; u.l_d = (n) + 1.0; /* avoid -0 */ \ + (i) = u.l_p[0]; (i) += u.l_p[1]; } /* add double bits for his hash */ + +#define lua_number2int(i,n) lua_number2int32(i, n, int) +#define lua_number2unsigned(i,n) lua_number2int32(i, n, lua_Unsigned) + +/* the trick can be expanded to lua_Integer when it is a 32-bit value */ +#if defined(LUA_IEEELL) +#define lua_number2integer(i,n) lua_number2int32(i, n, lua_Integer) +#endif + +#endif /* } */ + + +/* the following definitions always work, but may be slow */ + +#if !defined(lua_number2int) +#define lua_number2int(i,n) ((i)=(int)(n)) +#endif + +#if !defined(lua_number2integer) +#define lua_number2integer(i,n) ((i)=(lua_Integer)(n)) +#endif + +#if !defined(lua_number2unsigned) /* { */ +/* the following definition assures proper modulo behavior */ +#if defined(LUA_NUMBER_DOUBLE) || defined(LUA_NUMBER_FLOAT) +#include <math.h> +#define SUPUNSIGNED ((lua_Number)(~(lua_Unsigned)0) + 1) +#define lua_number2unsigned(i,n) \ + ((i)=(lua_Unsigned)((n) - floor((n)/SUPUNSIGNED)*SUPUNSIGNED)) +#else +#define lua_number2unsigned(i,n) ((i)=(lua_Unsigned)(n)) +#endif +#endif /* } */ + + +#if !defined(lua_unsigned2number) +/* on several machines, coercion from unsigned to double is slow, + so it may be worth to avoid */ +#define lua_unsigned2number(u) \ + (((u) <= (lua_Unsigned)INT_MAX) ? (lua_Number)(int)(u) : (lua_Number)(u)) +#endif + + + +#if defined(ltable_c) && !defined(luai_hashnum) + +extern int lcompat_hashnum(int64_t); + +#define luai_hashnum(i,n) (i = lcompat_hashnum(n)) + +#endif + + + +/* +** macro to control inclusion of some hard tests on stack reallocation +*/ +#if !defined(HARDSTACKTESTS) +#define condmovestack(L) ((void)0) +#else +/* realloc stack keeping its size */ +#define condmovestack(L) luaD_reallocstack((L), (L)->stacksize) +#endif + +#if !defined(HARDMEMTESTS) +#define condchangemem(L) condmovestack(L) +#else +#define condchangemem(L) \ + ((void)(!(G(L)->gcrunning) || (luaC_fullgc(L, 0), 1))) +#endif + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lmem.c b/usr/src/uts/common/fs/zfs/lua/lmem.c new file mode 100644 index 0000000000..0d070fbde8 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lmem.c @@ -0,0 +1,99 @@ +/* +** $Id: lmem.c,v 1.84.1.1 2013/04/12 18:48:47 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define lmem_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +/* +** About the realloc function: +** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); +** (`osize' is the old size, `nsize' is the new size) +** +** * frealloc(ud, NULL, x, s) creates a new block of size `s' (no +** matter 'x'). +** +** * frealloc(ud, p, x, 0) frees the block `p' +** (in this specific case, frealloc must return NULL); +** particularly, frealloc(ud, NULL, 0, 0) does nothing +** (which is equivalent to free(NULL) in ANSI C) +** +** frealloc returns NULL if it cannot create or reallocate the area +** (any reallocation to an equal or smaller size cannot fail!) +*/ + + + +#define MINSIZEARRAY 4 + + +void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems, + int limit, const char *what) { + void *newblock; + int newsize; + if (*size >= limit/2) { /* cannot double it? */ + if (*size >= limit) /* cannot grow even a little? */ + luaG_runerror(L, "too many %s (limit is %d)", what, limit); + newsize = limit; /* still have at least one free place */ + } + else { + newsize = (*size)*2; + if (newsize < MINSIZEARRAY) + newsize = MINSIZEARRAY; /* minimum size */ + } + newblock = luaM_reallocv(L, block, *size, newsize, size_elems); + *size = newsize; /* update only when everything else is OK */ + return newblock; +} + + +l_noret luaM_toobig (lua_State *L) { + luaG_runerror(L, "memory allocation error: block too big"); +} + + + +/* +** generic allocation routine. +*/ +void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { + void *newblock; + global_State *g = G(L); + size_t realosize = (block) ? osize : 0; + lua_assert((realosize == 0) == (block == NULL)); +#if defined(HARDMEMTESTS) + if (nsize > realosize && g->gcrunning) + luaC_fullgc(L, 1); /* force a GC whenever possible */ +#endif + newblock = (*g->frealloc)(g->ud, block, osize, nsize); + if (newblock == NULL && nsize > 0) { + api_check(L, nsize > realosize, + "realloc cannot fail when shrinking a block"); + if (g->gcrunning) { + luaC_fullgc(L, 1); /* try to free some memory... */ + newblock = (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ + } + if (newblock == NULL) + luaD_throw(L, LUA_ERRMEM); + } + lua_assert((nsize == 0) == (newblock == NULL)); + g->GCdebt = (g->GCdebt + nsize) - realosize; + return newblock; +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lmem.h b/usr/src/uts/common/fs/zfs/lua/lmem.h new file mode 100644 index 0000000000..c75a3d5098 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lmem.h @@ -0,0 +1,57 @@ +/* +** $Id: lmem.h,v 1.40.1.1 2013/04/12 18:48:47 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#ifndef lmem_h +#define lmem_h + + +#include <sys/zfs_context.h> + +#include "llimits.h" +#include "lua.h" + + +/* +** This macro avoids the runtime division MAX_SIZET/(e), as 'e' is +** always constant. +** The macro is somewhat complex to avoid warnings: +** +1 avoids warnings of "comparison has constant result"; +** cast to 'void' avoids warnings of "value unused". +*/ +#define luaM_reallocv(L,b,on,n,e) \ + (cast(void, \ + (cast(size_t, (n)+1) > MAX_SIZET/(e)) ? (luaM_toobig(L), 0) : 0), \ + luaM_realloc_(L, (b), (on)*(e), (n)*(e))) + +#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) +#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) +#define luaM_freearray(L, b, n) luaM_reallocv(L, (b), n, 0, sizeof((b)[0])) + +#define luaM_malloc(L,s) luaM_realloc_(L, NULL, 0, (s)) +#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) +#define luaM_newvector(L,n,t) \ + cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) + +#define luaM_newobject(L,tag,s) luaM_realloc_(L, NULL, tag, (s)) + +#define luaM_growvector(L,v,nelems,size,t,limit,e) \ + if ((nelems)+1 > (size)) \ + ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) + +#define luaM_reallocvector(L, v,oldn,n,t) \ + ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) + +LUAI_FUNC l_noret luaM_toobig (lua_State *L); + +/* not to be called directly */ +LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size, + size_t size_elem, int limit, + const char *what); + +#endif + diff --git a/usr/src/uts/common/fs/zfs/lua/lobject.c b/usr/src/uts/common/fs/zfs/lua/lobject.c new file mode 100644 index 0000000000..339c84d21d --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lobject.c @@ -0,0 +1,283 @@ +/* +** $Id: lobject.c,v 2.58.1.1 2013/04/12 18:48:47 roberto Exp $ +** Some generic functions over Lua objects +** See Copyright Notice in lua.h +*/ + +#include <sys/zfs_context.h> + +#define lobject_c +#define LUA_CORE + +#include "lua.h" + +#include "lctype.h" +#include "ldebug.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "lvm.h" + + + +LUAI_DDEF const TValue luaO_nilobject_ = {NILCONSTANT}; + + +/* +** converts an integer to a "floating point byte", represented as +** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if +** eeeee != 0 and (xxx) otherwise. +*/ +int luaO_int2fb (unsigned int x) { + int e = 0; /* exponent */ + if (x < 8) return x; + while (x >= 0x10) { + x = (x+1) >> 1; + e++; + } + return ((e+1) << 3) | (cast_int(x) - 8); +} + + +/* converts back */ +int luaO_fb2int (int x) { + int e = (x >> 3) & 0x1f; + if (e == 0) return x; + else return ((x & 7) + 8) << (e - 1); +} + + +int luaO_ceillog2 (unsigned int x) { + static const lu_byte log_2[256] = { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + int l = 0; + x--; + while (x >= 256) { l += 8; x >>= 8; } + return l + log_2[x]; +} + + +lua_Number luaO_arith (int op, lua_Number v1, lua_Number v2) { + switch (op) { + case LUA_OPADD: return luai_numadd(NULL, v1, v2); + case LUA_OPSUB: return luai_numsub(NULL, v1, v2); + case LUA_OPMUL: return luai_nummul(NULL, v1, v2); + case LUA_OPDIV: return luai_numdiv(NULL, v1, v2); + case LUA_OPMOD: return luai_nummod(NULL, v1, v2); + case LUA_OPPOW: return luai_numpow(NULL, v1, v2); + case LUA_OPUNM: return luai_numunm(NULL, v1); + default: lua_assert(0); return 0; + } +} + + +int luaO_hexavalue (int c) { + if (lisdigit(c)) return c - '0'; + else return ltolower(c) - 'a' + 10; +} + + +#if !defined(lua_strx2number) + + + +static int isneg (const char **s) { + if (**s == '-') { (*s)++; return 1; } + else if (**s == '+') (*s)++; + return 0; +} + + +static lua_Number readhexa (const char **s, lua_Number r, int *count) { + for (; lisxdigit(cast_uchar(**s)); (*s)++) { /* read integer part */ + r = (r * cast_num(16.0)) + cast_num(luaO_hexavalue(cast_uchar(**s))); + (*count)++; + } + return r; +} + + +/* +** convert an hexadecimal numeric string to a number, following +** C99 specification for 'strtod' +*/ +static lua_Number lua_strx2number (const char *s, char **endptr) { + lua_Number r = 0.0; + int e = 0, i = 0; + int neg = 0; /* 1 if number is negative */ + *endptr = cast(char *, s); /* nothing is valid yet */ + while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ + neg = isneg(&s); /* check signal */ + if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ + return 0.0; /* invalid format (no '0x') */ + s += 2; /* skip '0x' */ + r = readhexa(&s, r, &i); /* read integer part */ + if (*s == '.') { + s++; /* skip dot */ + r = readhexa(&s, r, &e); /* read fractional part */ + } + if (i == 0 && e == 0) + return 0.0; /* invalid format (no digit) */ + e *= -4; /* each fractional digit divides value by 2^-4 */ + *endptr = cast(char *, s); /* valid up to here */ + if (*s == 'p' || *s == 'P') { /* exponent part? */ + int exp1 = 0; + int neg1; + s++; /* skip 'p' */ + neg1 = isneg(&s); /* signal */ + if (!lisdigit(cast_uchar(*s))) + goto ret; /* must have at least one digit */ + while (lisdigit(cast_uchar(*s))) /* read exponent */ + exp1 = exp1 * 10 + *(s++) - '0'; + if (neg1) exp1 = -exp1; + e += exp1; + } + *endptr = cast(char *, s); /* valid up to here */ + ret: + if (neg) r = -r; + return (r * (1 << e)); +} + +#endif + + +int luaO_str2d (const char *s, size_t len, lua_Number *result) { + char *endptr; + if (strpbrk(s, "nN")) /* reject 'inf' and 'nan' */ + return 0; + else if (strpbrk(s, "xX")) /* hexa? */ + *result = lua_strx2number(s, &endptr); + else + *result = lua_str2number(s, &endptr); + if (endptr == s) return 0; /* nothing recognized */ + while (lisspace(cast_uchar(*endptr))) endptr++; + return (endptr == s + len); /* OK if no trailing characters */ +} + + + +static void pushstr (lua_State *L, const char *str, size_t l) { + setsvalue2s(L, L->top++, luaS_newlstr(L, str, l)); +} + + +/* this function handles only `%d', `%c', %f, %p, and `%s' formats */ +const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { + int n = 0; + for (;;) { + const char *e = strchr(fmt, '%'); + if (e == NULL) break; + luaD_checkstack(L, 2); /* fmt + item */ + pushstr(L, fmt, e - fmt); + switch (*(e+1)) { + case 's': { + const char *s = va_arg(argp, char *); + if (s == NULL) s = "(null)"; + pushstr(L, s, strlen(s)); + break; + } + case 'c': { + char buff; + buff = cast(char, va_arg(argp, int)); + pushstr(L, &buff, 1); + break; + } + case 'd': { + setnvalue(L->top++, cast_num(va_arg(argp, int))); + break; + } + case 'f': { + setnvalue(L->top++, cast_num(va_arg(argp, l_uacNumber))); + break; + } + case 'p': { + char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */ + int l = lcompat_sprintf(buff, "%p", va_arg(argp, void *)); + pushstr(L, buff, l); + break; + } + case '%': { + pushstr(L, "%", 1); + break; + } + default: { + luaG_runerror(L, + "invalid option " LUA_QL("%%%c") " to " LUA_QL("lua_pushfstring"), + *(e + 1)); + } + } + n += 2; + fmt = e+2; + } + luaD_checkstack(L, 1); + pushstr(L, fmt, strlen(fmt)); + if (n > 0) luaV_concat(L, n + 1); + return svalue(L->top - 1); +} + + +const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + return msg; +} + + +/* number of chars of a literal string without the ending \0 */ +#define LL(x) (sizeof(x)/sizeof(char) - 1) + +#define RETS "..." +#define PRE "[string \"" +#define POS "\"]" + +#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) + +void luaO_chunkid (char *out, const char *source, size_t bufflen) { + size_t l = strlen(source); + if (*source == '=') { /* 'literal' source */ + if (l <= bufflen) /* small enough? */ + memcpy(out, source + 1, l * sizeof(char)); + else { /* truncate it */ + addstr(out, source + 1, bufflen - 1); + *out = '\0'; + } + } + else if (*source == '@') { /* file name */ + if (l <= bufflen) /* small enough? */ + memcpy(out, source + 1, l * sizeof(char)); + else { /* add '...' before rest of name */ + addstr(out, RETS, LL(RETS)); + bufflen -= LL(RETS); + memcpy(out, source + 1 + l - bufflen, bufflen * sizeof(char)); + } + } + else { /* string; format as [string "source"] */ + const char *nl = strchr(source, '\n'); /* find first new line (if any) */ + addstr(out, PRE, LL(PRE)); /* add prefix */ + bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */ + if (l < bufflen && nl == NULL) { /* small one-line source? */ + addstr(out, source, l); /* keep it */ + } + else { + if (nl != NULL) l = nl - source; /* stop at first newline */ + if (l > bufflen) l = bufflen; + addstr(out, source, l); + addstr(out, RETS, LL(RETS)); + } + memcpy(out, POS, (LL(POS) + 1) * sizeof(char)); + } +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lobject.h b/usr/src/uts/common/fs/zfs/lua/lobject.h new file mode 100644 index 0000000000..9c9f235428 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lobject.h @@ -0,0 +1,606 @@ +/* +** $Id: lobject.h,v 2.71.1.2 2014/05/07 14:14:58 roberto Exp $ +** Type definitions for Lua objects +** See Copyright Notice in lua.h +*/ + + +#ifndef lobject_h +#define lobject_h + + +#include <sys/zfs_context.h> + +#include "llimits.h" +#include "lua.h" + + +/* +** Extra tags for non-values +*/ +#define LUA_TPROTO LUA_NUMTAGS +#define LUA_TUPVAL (LUA_NUMTAGS+1) +#define LUA_TDEADKEY (LUA_NUMTAGS+2) + +/* +** number of all possible tags (including LUA_TNONE but excluding DEADKEY) +*/ +#define LUA_TOTALTAGS (LUA_TUPVAL+2) + + +/* +** tags for Tagged Values have the following use of bits: +** bits 0-3: actual tag (a LUA_T* value) +** bits 4-5: variant bits +** bit 6: whether value is collectable +*/ + +#define VARBITS (3 << 4) + + +/* +** LUA_TFUNCTION variants: +** 0 - Lua function +** 1 - light C function +** 2 - regular C function (closure) +*/ + +/* Variant tags for functions */ +#define LUA_TLCL (LUA_TFUNCTION | (0 << 4)) /* Lua closure */ +#define LUA_TLCF (LUA_TFUNCTION | (1 << 4)) /* light C function */ +#define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) /* C closure */ + + +/* Variant tags for strings */ +#define LUA_TSHRSTR (LUA_TSTRING | (0 << 4)) /* short strings */ +#define LUA_TLNGSTR (LUA_TSTRING | (1 << 4)) /* long strings */ + + +/* Bit mark for collectable types */ +#define BIT_ISCOLLECTABLE (1 << 6) + +/* mark a tag as collectable */ +#define ctb(t) ((t) | BIT_ISCOLLECTABLE) + + +/* +** Union of all collectable objects +*/ +typedef union GCObject GCObject; + + +/* +** Common Header for all collectable objects (in macro form, to be +** included in other objects) +*/ +#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked + + +/* +** Common header in struct form +*/ +typedef struct GCheader { + CommonHeader; +} GCheader; + + + +/* +** Union of all Lua values +*/ +typedef union Value Value; + + +#define numfield lua_Number n; /* numbers */ + + + +/* +** Tagged Values. This is the basic representation of values in Lua, +** an actual value plus a tag with its type. +*/ + +#define TValuefields Value value_; int tt_ + +typedef struct lua_TValue TValue; + + +/* macro defining a nil value */ +#define NILCONSTANT {NULL}, LUA_TNIL + + +#define val_(o) ((o)->value_) +#define num_(o) (val_(o).n) + + +/* raw type tag of a TValue */ +#define rttype(o) ((o)->tt_) + +/* tag with no variants (bits 0-3) */ +#define novariant(x) ((x) & 0x0F) + +/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */ +#define ttype(o) (rttype(o) & 0x3F) + +/* type tag of a TValue with no variants (bits 0-3) */ +#define ttypenv(o) (novariant(rttype(o))) + + +/* Macros to test type */ +#define checktag(o,t) (rttype(o) == (t)) +#define checktype(o,t) (ttypenv(o) == (t)) +#define ttisnumber(o) checktag((o), LUA_TNUMBER) +#define ttisnil(o) checktag((o), LUA_TNIL) +#define ttisboolean(o) checktag((o), LUA_TBOOLEAN) +#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA) +#define ttisstring(o) checktype((o), LUA_TSTRING) +#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR)) +#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR)) +#define ttistable(o) checktag((o), ctb(LUA_TTABLE)) +#define ttisfunction(o) checktype(o, LUA_TFUNCTION) +#define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION) +#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL)) +#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL)) +#define ttislcf(o) checktag((o), LUA_TLCF) +#define ttisuserdata(o) checktag((o), ctb(LUA_TUSERDATA)) +#define ttisthread(o) checktag((o), ctb(LUA_TTHREAD)) +#define ttisdeadkey(o) checktag((o), LUA_TDEADKEY) + +#define ttisequal(o1,o2) (rttype(o1) == rttype(o2)) + +/* Macros to access values */ +#define nvalue(o) check_exp(ttisnumber(o), num_(o)) +#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc) +#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p) +#define rawtsvalue(o) check_exp(ttisstring(o), &val_(o).gc->ts) +#define tsvalue(o) (&rawtsvalue(o)->tsv) +#define rawuvalue(o) check_exp(ttisuserdata(o), &val_(o).gc->u) +#define uvalue(o) (&rawuvalue(o)->uv) +#define clvalue(o) check_exp(ttisclosure(o), &val_(o).gc->cl) +#define clLvalue(o) check_exp(ttisLclosure(o), &val_(o).gc->cl.l) +#define clCvalue(o) check_exp(ttisCclosure(o), &val_(o).gc->cl.c) +#define fvalue(o) check_exp(ttislcf(o), val_(o).f) +#define hvalue(o) check_exp(ttistable(o), &val_(o).gc->h) +#define bvalue(o) check_exp(ttisboolean(o), val_(o).b) +#define thvalue(o) check_exp(ttisthread(o), &val_(o).gc->th) +/* a dead value may get the 'gc' field, but cannot access its contents */ +#define deadvalue(o) check_exp(ttisdeadkey(o), cast(void *, val_(o).gc)) + +#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) + + +#define iscollectable(o) (rttype(o) & BIT_ISCOLLECTABLE) + + +/* Macros for internal tests */ +#define righttt(obj) (ttype(obj) == gcvalue(obj)->gch.tt) + +#define checkliveness(g,obj) \ + lua_longassert(!iscollectable(obj) || \ + (righttt(obj) && !isdead(g,gcvalue(obj)))) + + +/* Macros to set values */ +#define settt_(o,t) ((o)->tt_=(t)) + +#define setnvalue(obj,x) \ + { TValue *io=(obj); num_(io)=(x); settt_(io, LUA_TNUMBER); } + +#define setnilvalue(obj) settt_(obj, LUA_TNIL) + +#define setfvalue(obj,x) \ + { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_TLCF); } + +#define setpvalue(obj,x) \ + { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); } + +#define setbvalue(obj,x) \ + { TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); } + +#define setgcovalue(L,obj,x) \ + { TValue *io=(obj); GCObject *i_g=(x); \ + val_(io).gc=i_g; settt_(io, ctb(gch(i_g)->tt)); } + +#define setsvalue(L,obj,x) \ + { TValue *io=(obj); \ + TString *x_ = (x); \ + val_(io).gc=cast(GCObject *, x_); settt_(io, ctb(x_->tsv.tt)); \ + checkliveness(G(L),io); } + +#define setuvalue(L,obj,x) \ + { TValue *io=(obj); \ + val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TUSERDATA)); \ + checkliveness(G(L),io); } + +#define setthvalue(L,obj,x) \ + { TValue *io=(obj); \ + val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TTHREAD)); \ + checkliveness(G(L),io); } + +#define setclLvalue(L,obj,x) \ + { TValue *io=(obj); \ + val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TLCL)); \ + checkliveness(G(L),io); } + +#define setclCvalue(L,obj,x) \ + { TValue *io=(obj); \ + val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TCCL)); \ + checkliveness(G(L),io); } + +#define sethvalue(L,obj,x) \ + { TValue *io=(obj); \ + val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TTABLE)); \ + checkliveness(G(L),io); } + +#define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY) + + + +#define setobj(L,obj1,obj2) \ + { const TValue *io2=(obj2); TValue *io1=(obj1); \ + io1->value_ = io2->value_; io1->tt_ = io2->tt_; \ + checkliveness(G(L),io1); } + + +/* +** different types of assignments, according to destination +*/ + +/* from stack to (same) stack */ +#define setobjs2s setobj +/* to stack (not from same stack) */ +#define setobj2s setobj +#define setsvalue2s setsvalue +#define sethvalue2s sethvalue +#define setptvalue2s setptvalue +/* from table to same table */ +#define setobjt2t setobj +/* to table */ +#define setobj2t setobj +/* to new object */ +#define setobj2n setobj +#define setsvalue2n setsvalue + + +/* check whether a number is valid (useful only for NaN trick) */ +#define luai_checknum(L,o,c) { /* empty */ } + + +/* +** {====================================================== +** NaN Trick +** ======================================================= +*/ +#if defined(LUA_NANTRICK) + +/* +** numbers are represented in the 'd_' field. All other values have the +** value (NNMARK | tag) in 'tt__'. A number with such pattern would be +** a "signaled NaN", which is never generated by regular operations by +** the CPU (nor by 'strtod') +*/ + +/* allows for external implementation for part of the trick */ +#if !defined(NNMARK) /* { */ + + +#if !defined(LUA_IEEEENDIAN) +#error option 'LUA_NANTRICK' needs 'LUA_IEEEENDIAN' +#endif + + +#define NNMARK 0x7FF7A500 +#define NNMASK 0x7FFFFF00 + +#undef TValuefields +#undef NILCONSTANT + +#if (LUA_IEEEENDIAN == 0) /* { */ + +/* little endian */ +#define TValuefields \ + union { struct { Value v__; int tt__; } i; double d__; } u +#define NILCONSTANT {{{NULL}, tag2tt(LUA_TNIL)}} +/* field-access macros */ +#define v_(o) ((o)->u.i.v__) +#define d_(o) ((o)->u.d__) +#define tt_(o) ((o)->u.i.tt__) + +#else /* }{ */ + +/* big endian */ +#define TValuefields \ + union { struct { int tt__; Value v__; } i; double d__; } u +#define NILCONSTANT {{tag2tt(LUA_TNIL), {NULL}}} +/* field-access macros */ +#define v_(o) ((o)->u.i.v__) +#define d_(o) ((o)->u.d__) +#define tt_(o) ((o)->u.i.tt__) + +#endif /* } */ + +#endif /* } */ + + +/* correspondence with standard representation */ +#undef val_ +#define val_(o) v_(o) +#undef num_ +#define num_(o) d_(o) + + +#undef numfield +#define numfield /* no such field; numbers are the entire struct */ + +/* basic check to distinguish numbers from non-numbers */ +#undef ttisnumber +#define ttisnumber(o) ((tt_(o) & NNMASK) != NNMARK) + +#define tag2tt(t) (NNMARK | (t)) + +#undef rttype +#define rttype(o) (ttisnumber(o) ? LUA_TNUMBER : tt_(o) & 0xff) + +#undef settt_ +#define settt_(o,t) (tt_(o) = tag2tt(t)) + +#undef setnvalue +#define setnvalue(obj,x) \ + { TValue *io_=(obj); num_(io_)=(x); lua_assert(ttisnumber(io_)); } + +#undef setobj +#define setobj(L,obj1,obj2) \ + { const TValue *o2_=(obj2); TValue *o1_=(obj1); \ + o1_->u = o2_->u; \ + checkliveness(G(L),o1_); } + + +/* +** these redefinitions are not mandatory, but these forms are more efficient +*/ + +#undef checktag +#undef checktype +#define checktag(o,t) (tt_(o) == tag2tt(t)) +#define checktype(o,t) (ctb(tt_(o) | VARBITS) == ctb(tag2tt(t) | VARBITS)) + +#undef ttisequal +#define ttisequal(o1,o2) \ + (ttisnumber(o1) ? ttisnumber(o2) : (tt_(o1) == tt_(o2))) + + +#undef luai_checknum +#define luai_checknum(L,o,c) { if (!ttisnumber(o)) c; } + +#endif +/* }====================================================== */ + + + +/* +** {====================================================== +** types and prototypes +** ======================================================= +*/ + + +union Value { + GCObject *gc; /* collectable objects */ + void *p; /* light userdata */ + int b; /* booleans */ + lua_CFunction f; /* light C functions */ + numfield /* numbers */ +}; + + +struct lua_TValue { + TValuefields; +}; + + +typedef TValue *StkId; /* index to stack elements */ + + + + +/* +** Header for string value; string bytes follow the end of this structure +*/ +typedef union TString { + L_Umaxalign dummy; /* ensures maximum alignment for strings */ + struct { + CommonHeader; + lu_byte extra; /* reserved words for short strings; "has hash" for longs */ + unsigned int hash; + size_t len; /* number of characters in string */ + } tsv; +} TString; + + +/* get the actual string (array of bytes) from a TString */ +#define getstr(ts) cast(const char *, (ts) + 1) + +/* get the actual string (array of bytes) from a Lua value */ +#define svalue(o) getstr(rawtsvalue(o)) + + +/* +** Header for userdata; memory area follows the end of this structure +*/ +typedef union Udata { + L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ + struct { + CommonHeader; + struct Table *metatable; + struct Table *env; + size_t len; /* number of bytes */ + } uv; +} Udata; + + + +/* +** Description of an upvalue for function prototypes +*/ +typedef struct Upvaldesc { + TString *name; /* upvalue name (for debug information) */ + lu_byte instack; /* whether it is in stack */ + lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ +} Upvaldesc; + + +/* +** Description of a local variable for function prototypes +** (used for debug information) +*/ +typedef struct LocVar { + TString *varname; + int startpc; /* first point where variable is active */ + int endpc; /* first point where variable is dead */ +} LocVar; + + +/* +** Function Prototypes +*/ +typedef struct Proto { + CommonHeader; + TValue *k; /* constants used by the function */ + Instruction *code; + struct Proto **p; /* functions defined inside the function */ + int *lineinfo; /* map from opcodes to source lines (debug information) */ + LocVar *locvars; /* information about local variables (debug information) */ + Upvaldesc *upvalues; /* upvalue information */ + union Closure *cache; /* last created closure with this prototype */ + TString *source; /* used for debug information */ + int sizeupvalues; /* size of 'upvalues' */ + int sizek; /* size of `k' */ + int sizecode; + int sizelineinfo; + int sizep; /* size of `p' */ + int sizelocvars; + int linedefined; + int lastlinedefined; + GCObject *gclist; + lu_byte numparams; /* number of fixed parameters */ + lu_byte is_vararg; + lu_byte maxstacksize; /* maximum stack used by this function */ +} Proto; + + + +/* +** Lua Upvalues +*/ +typedef struct UpVal { + CommonHeader; + TValue *v; /* points to stack or to its own value */ + union { + TValue value; /* the value (when closed) */ + struct { /* double linked list (when open) */ + struct UpVal *prev; + struct UpVal *next; + } l; + } u; +} UpVal; + + +/* +** Closures +*/ + +#define ClosureHeader \ + CommonHeader; lu_byte nupvalues; GCObject *gclist + +typedef struct CClosure { + ClosureHeader; + lua_CFunction f; + TValue upvalue[1]; /* list of upvalues */ +} CClosure; + + +typedef struct LClosure { + ClosureHeader; + struct Proto *p; + UpVal *upvals[1]; /* list of upvalues */ +} LClosure; + + +typedef union Closure { + CClosure c; + LClosure l; +} Closure; + + +#define isLfunction(o) ttisLclosure(o) + +#define getproto(o) (clLvalue(o)->p) + + +/* +** Tables +*/ + +typedef union TKey { + struct { + TValuefields; + struct Node *next; /* for chaining */ + } nk; + TValue tvk; +} TKey; + + +typedef struct Node { + TValue i_val; + TKey i_key; +} Node; + + +typedef struct Table { + CommonHeader; + lu_byte flags; /* 1<<p means tagmethod(p) is not present */ + lu_byte lsizenode; /* log2 of size of `node' array */ + int sizearray; /* size of `array' array */ + TValue *array; /* array part */ + Node *node; + Node *lastfree; /* any free position is before this position */ + struct Table *metatable; + GCObject *gclist; +} Table; + + + +/* +** `module' operation for hashing (size is always a power of 2) +*/ +#define lmod(s,size) \ + (check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1))))) + + +#define twoto(x) (1<<(x)) +#define sizenode(t) (twoto((t)->lsizenode)) + + +/* +** (address of) a fixed nil value +*/ +#define luaO_nilobject (&luaO_nilobject_) + + +LUAI_DDEC const TValue luaO_nilobject_; + + +LUAI_FUNC int luaO_int2fb (unsigned int x); +LUAI_FUNC int luaO_fb2int (int x); +LUAI_FUNC int luaO_ceillog2 (unsigned int x); +LUAI_FUNC lua_Number luaO_arith (int op, lua_Number v1, lua_Number v2); +LUAI_FUNC int luaO_str2d (const char *s, size_t len, lua_Number *result); +LUAI_FUNC int luaO_hexavalue (int c); +LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, + va_list argp); +LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len); + + +#endif + diff --git a/usr/src/uts/common/fs/zfs/lua/lopcodes.c b/usr/src/uts/common/fs/zfs/lua/lopcodes.c new file mode 100644 index 0000000000..4190dc7624 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lopcodes.c @@ -0,0 +1,107 @@ +/* +** $Id: lopcodes.c,v 1.49.1.1 2013/04/12 18:48:47 roberto Exp $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + + +#define lopcodes_c +#define LUA_CORE + + +#include "lopcodes.h" + + +/* ORDER OP */ + +LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = { + "MOVE", + "LOADK", + "LOADKX", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETTABUP", + "GETTABLE", + "SETTABUP", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORCALL", + "TFORLOOP", + "SETLIST", + "CLOSURE", + "VARARG", + "EXTRAARG", + NULL +}; + + +#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) + +LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { +/* T A B C mode opcode */ + opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ + ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ + ,opmode(0, 1, OpArgN, OpArgN, iABx) /* OP_LOADKX */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_LOADNIL */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ + ,opmode(0, 1, OpArgU, OpArgK, iABC) /* OP_GETTABUP */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ + ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ + ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ + ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ + ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ + ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TEST */ + ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ + ,opmode(0, 0, OpArgN, OpArgU, iABC) /* OP_TFORCALL */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_TFORLOOP */ + ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ + ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ + ,opmode(0, 0, OpArgU, OpArgU, iAx) /* OP_EXTRAARG */ +}; + diff --git a/usr/src/uts/common/fs/zfs/lua/lopcodes.h b/usr/src/uts/common/fs/zfs/lua/lopcodes.h new file mode 100644 index 0000000000..8e2f80a131 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lopcodes.h @@ -0,0 +1,288 @@ +/* +** $Id: lopcodes.h,v 1.142.1.2 2014/10/20 18:32:09 roberto Exp $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lopcodes_h +#define lopcodes_h + +#include "llimits.h" + + +/*=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. + Instructions can have the following fields: + `A' : 8 bits + `B' : 9 bits + `C' : 9 bits + 'Ax' : 26 bits ('A', 'B', and 'C' together) + `Bx' : 18 bits (`B' and `C' together) + `sBx' : signed Bx + + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. +===========================================================================*/ + + +enum OpMode {iABC, iABx, iAsBx, iAx}; /* basic instruction format */ + + +/* +** size and position of opcode arguments. +*/ +#define SIZE_C 9 +#define SIZE_B 9 +#define SIZE_Bx (SIZE_C + SIZE_B) +#define SIZE_A 8 +#define SIZE_Ax (SIZE_C + SIZE_B + SIZE_A) + +#define SIZE_OP 6 + +#define POS_OP 0 +#define POS_A (POS_OP + SIZE_OP) +#define POS_C (POS_A + SIZE_A) +#define POS_B (POS_C + SIZE_C) +#define POS_Bx POS_C +#define POS_Ax POS_A + + +/* +** limits for opcode arguments. +** we use (signed) int to manipulate most arguments, +** so they must fit in LUAI_BITSINT-1 bits (-1 for sign) +*/ +#if SIZE_Bx < LUAI_BITSINT-1 +#define MAXARG_Bx ((1<<SIZE_Bx)-1) +#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */ +#else +#define MAXARG_Bx MAX_INT +#define MAXARG_sBx MAX_INT +#endif + +#if SIZE_Ax < LUAI_BITSINT-1 +#define MAXARG_Ax ((1<<SIZE_Ax)-1) +#else +#define MAXARG_Ax MAX_INT +#endif + + +#define MAXARG_A ((1<<SIZE_A)-1) +#define MAXARG_B ((1<<SIZE_B)-1) +#define MAXARG_C ((1<<SIZE_C)-1) + + +/* creates a mask with `n' 1 bits at position `p' */ +#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p)) + +/* creates a mask with `n' 0 bits at position `p' */ +#define MASK0(n,p) (~MASK1(n,p)) + +/* +** the following macros help to manipulate instructions +*/ + +#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ + ((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP)))) + +#define getarg(i,pos,size) (cast(int, ((i)>>pos) & MASK1(size,0))) +#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ + ((cast(Instruction, v)<<pos)&MASK1(size,pos)))) + +#define GETARG_A(i) getarg(i, POS_A, SIZE_A) +#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A) + +#define GETARG_B(i) getarg(i, POS_B, SIZE_B) +#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B) + +#define GETARG_C(i) getarg(i, POS_C, SIZE_C) +#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C) + +#define GETARG_Bx(i) getarg(i, POS_Bx, SIZE_Bx) +#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx) + +#define GETARG_Ax(i) getarg(i, POS_Ax, SIZE_Ax) +#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax) + +#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx) +#define SETARG_sBx(i,b) SETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx)) + + +#define CREATE_ABC(o,a,b,c) ((cast(Instruction, o)<<POS_OP) \ + | (cast(Instruction, a)<<POS_A) \ + | (cast(Instruction, b)<<POS_B) \ + | (cast(Instruction, c)<<POS_C)) + +#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \ + | (cast(Instruction, a)<<POS_A) \ + | (cast(Instruction, bc)<<POS_Bx)) + +#define CREATE_Ax(o,a) ((cast(Instruction, o)<<POS_OP) \ + | (cast(Instruction, a)<<POS_Ax)) + + +/* +** Macros to operate RK indices +*/ + +/* this bit 1 means constant (0 means register) */ +#define BITRK (1 << (SIZE_B - 1)) + +/* test whether value is a constant */ +#define ISK(x) ((x) & BITRK) + +/* gets the index of the constant */ +#define INDEXK(r) ((int)(r) & ~BITRK) + +#define MAXINDEXRK (BITRK - 1) + +/* code a constant index as a RK value */ +#define RKASK(x) ((x) | BITRK) + + +/* +** invalid register that fits in 8 bits +*/ +#define NO_REG MAXARG_A + + +/* +** R(x) - register +** Kst(x) - constant (in constant table) +** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x) +*/ + + +/* +** grep "ORDER OP" if you change these enums +*/ + +typedef enum { +/*---------------------------------------------------------------------- +name args description +------------------------------------------------------------------------*/ +OP_MOVE,/* A B R(A) := R(B) */ +OP_LOADK,/* A Bx R(A) := Kst(Bx) */ +OP_LOADKX,/* A R(A) := Kst(extra arg) */ +OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */ +OP_LOADNIL,/* A B R(A), R(A+1), ..., R(A+B) := nil */ +OP_GETUPVAL,/* A B R(A) := UpValue[B] */ + +OP_GETTABUP,/* A B C R(A) := UpValue[B][RK(C)] */ +OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */ + +OP_SETTABUP,/* A B C UpValue[A][RK(B)] := RK(C) */ +OP_SETUPVAL,/* A B UpValue[B] := R(A) */ +OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */ + +OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */ + +OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */ + +OP_ADD,/* A B C R(A) := RK(B) + RK(C) */ +OP_SUB,/* A B C R(A) := RK(B) - RK(C) */ +OP_MUL,/* A B C R(A) := RK(B) * RK(C) */ +OP_DIV,/* A B C R(A) := RK(B) / RK(C) */ +OP_MOD,/* A B C R(A) := RK(B) % RK(C) */ +OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */ +OP_UNM,/* A B R(A) := -R(B) */ +OP_NOT,/* A B R(A) := not R(B) */ +OP_LEN,/* A B R(A) := length of R(B) */ + +OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */ + +OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ +OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ +OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ +OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ + +OP_TEST,/* A C if not (R(A) <=> C) then pc++ */ +OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + +OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ + +OP_FORLOOP,/* A sBx R(A)+=R(A+2); + if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/ +OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */ + +OP_TFORCALL,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ +OP_TFORLOOP,/* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/ + +OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */ + +OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */ + +OP_VARARG,/* A B R(A), R(A+1), ..., R(A+B-2) = vararg */ + +OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ +} OpCode; + + +#define NUM_OPCODES (cast(int, OP_EXTRAARG) + 1) + + + +/*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top. If (C == 0), then `top' is + set to last_result+1, so next open instruction (OP_CALL, OP_RETURN, + OP_SETLIST) may use `top'. + + (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to `top'. + + (*) In OP_SETLIST, if (B == 0) then B = `top'; if (C == 0) then next + 'instruction' is EXTRAARG(real C). + + (*) In OP_LOADKX, the next 'instruction' is always EXTRAARG. + + (*) For comparisons, A specifies what condition the test should accept + (true or false). + + (*) All `skips' (pc++) assume that next instruction is a jump. + +===========================================================================*/ + + +/* +** masks for instruction properties. The format is: +** bits 0-1: op mode +** bits 2-3: C arg mode +** bits 4-5: B arg mode +** bit 6: instruction set register A +** bit 7: operator is a test (next instruction must be a jump) +*/ + +enum OpArgMask { + OpArgN, /* argument is not used */ + OpArgU, /* argument is used */ + OpArgR, /* argument is a register or a jump offset */ + OpArgK /* argument is a constant or register/constant */ +}; + +LUAI_DDEC const lu_byte luaP_opmodes[NUM_OPCODES]; + +#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3)) +#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3)) +#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3)) +#define testAMode(m) (luaP_opmodes[m] & (1 << 6)) +#define testTMode(m) (luaP_opmodes[m] & (1 << 7)) + + +LUAI_DDEC const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */ + + +/* number of list items to accumulate before a SETLIST instruction */ +#define LFIELDS_PER_FLUSH 50 + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lparser.c b/usr/src/uts/common/fs/zfs/lua/lparser.c new file mode 100644 index 0000000000..73f1af64f8 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lparser.c @@ -0,0 +1,1637 @@ +/* +** $Id: lparser.c,v 2.130.1.1 2013/04/12 18:48:47 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#include <sys/zfs_context.h> + +#define lparser_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" + + + +/* maximum number of local variables per function (must be smaller + than 250, due to the bytecode format) */ +#define MAXVARS 200 + + +#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) + + + +/* +** nodes for block list (list of active blocks) +*/ +typedef struct BlockCnt { + struct BlockCnt *previous; /* chain */ + short firstlabel; /* index of first label in this block */ + short firstgoto; /* index of first pending goto in this block */ + lu_byte nactvar; /* # active locals outside the block */ + lu_byte upval; /* true if some variable in the block is an upvalue */ + lu_byte isloop; /* true if `block' is a loop */ +} BlockCnt; + + + +/* +** prototypes for recursive non-terminal functions +*/ +static void statement (LexState *ls); +static void expr (LexState *ls, expdesc *v); + + +static void anchor_token (LexState *ls) { + /* last token from outer function must be EOS */ + lua_assert(ls->fs != NULL || ls->t.token == TK_EOS); + if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { + TString *ts = ls->t.seminfo.ts; + luaX_newstring(ls, getstr(ts), ts->tsv.len); + } +} + + +/* semantic error */ +static l_noret semerror (LexState *ls, const char *msg) { + ls->t.token = 0; /* remove 'near to' from final message */ + luaX_syntaxerror(ls, msg); +} + + +static l_noret error_expected (LexState *ls, int token) { + luaX_syntaxerror(ls, + luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token))); +} + + +static l_noret errorlimit (FuncState *fs, int limit, const char *what) { + lua_State *L = fs->ls->L; + const char *msg; + int line = fs->f->linedefined; + const char *where = (line == 0) + ? "main function" + : luaO_pushfstring(L, "function at line %d", line); + msg = luaO_pushfstring(L, "too many %s (limit is %d) in %s", + what, limit, where); + luaX_syntaxerror(fs->ls, msg); +} + + +static void checklimit (FuncState *fs, int v, int l, const char *what) { + if (v > l) errorlimit(fs, l, what); +} + + +static int testnext (LexState *ls, int c) { + if (ls->t.token == c) { + luaX_next(ls); + return 1; + } + else return 0; +} + + +static void check (LexState *ls, int c) { + if (ls->t.token != c) + error_expected(ls, c); +} + + +static void checknext (LexState *ls, int c) { + check(ls, c); + luaX_next(ls); +} + + +#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } + + + +static void check_match (LexState *ls, int what, int who, int where) { + if (!testnext(ls, what)) { + if (where == ls->linenumber) + error_expected(ls, what); + else { + luaX_syntaxerror(ls, luaO_pushfstring(ls->L, + "%s expected (to close %s at line %d)", + luaX_token2str(ls, what), luaX_token2str(ls, who), where)); + } + } +} + + +static TString *str_checkname (LexState *ls) { + TString *ts; + check(ls, TK_NAME); + ts = ls->t.seminfo.ts; + luaX_next(ls); + return ts; +} + + +static void init_exp (expdesc *e, expkind k, int i) { + e->f = e->t = NO_JUMP; + e->k = k; + e->u.info = i; +} + + +static void codestring (LexState *ls, expdesc *e, TString *s) { + init_exp(e, VK, luaK_stringK(ls->fs, s)); +} + + +static void checkname (LexState *ls, expdesc *e) { + codestring(ls, e, str_checkname(ls)); +} + + +static int registerlocalvar (LexState *ls, TString *varname) { + FuncState *fs = ls->fs; + Proto *f = fs->f; + int oldsize = f->sizelocvars; + luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, + LocVar, SHRT_MAX, "local variables"); + while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; + f->locvars[fs->nlocvars].varname = varname; + luaC_objbarrier(ls->L, f, varname); + return fs->nlocvars++; +} + + +static void new_localvar (LexState *ls, TString *name) { + FuncState *fs = ls->fs; + Dyndata *dyd = ls->dyd; + int reg = registerlocalvar(ls, name); + checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, + MAXVARS, "local variables"); + luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1, + dyd->actvar.size, Vardesc, MAX_INT, "local variables"); + dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg); +} + + +static void new_localvarliteral_ (LexState *ls, const char *name, size_t sz) { + new_localvar(ls, luaX_newstring(ls, name, sz)); +} + +#define new_localvarliteral(ls,v) \ + new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1) + + +static LocVar *getlocvar (FuncState *fs, int i) { + int idx = fs->ls->dyd->actvar.arr[fs->firstlocal + i].idx; + lua_assert(idx < fs->nlocvars); + return &fs->f->locvars[idx]; +} + + +static void adjustlocalvars (LexState *ls, int nvars) { + FuncState *fs = ls->fs; + fs->nactvar = cast_byte(fs->nactvar + nvars); + for (; nvars; nvars--) { + getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc; + } +} + + +static void removevars (FuncState *fs, int tolevel) { + fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); + while (fs->nactvar > tolevel) + getlocvar(fs, --fs->nactvar)->endpc = fs->pc; +} + + +static int searchupvalue (FuncState *fs, TString *name) { + int i; + Upvaldesc *up = fs->f->upvalues; + for (i = 0; i < fs->nups; i++) { + if (luaS_eqstr(up[i].name, name)) return i; + } + return -1; /* not found */ +} + + +static int newupvalue (FuncState *fs, TString *name, expdesc *v) { + Proto *f = fs->f; + int oldsize = f->sizeupvalues; + checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); + luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues, + Upvaldesc, MAXUPVAL, "upvalues"); + while (oldsize < f->sizeupvalues) f->upvalues[oldsize++].name = NULL; + f->upvalues[fs->nups].instack = (v->k == VLOCAL); + f->upvalues[fs->nups].idx = cast_byte(v->u.info); + f->upvalues[fs->nups].name = name; + luaC_objbarrier(fs->ls->L, f, name); + return fs->nups++; +} + + +static int searchvar (FuncState *fs, TString *n) { + int i; + for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { + if (luaS_eqstr(n, getlocvar(fs, i)->varname)) + return i; + } + return -1; /* not found */ +} + + +/* + Mark block where variable at given level was defined + (to emit close instructions later). +*/ +static void markupval (FuncState *fs, int level) { + BlockCnt *bl = fs->bl; + while (bl->nactvar > level) bl = bl->previous; + bl->upval = 1; +} + + +/* + Find variable with given name 'n'. If it is an upvalue, add this + upvalue into all intermediate functions. +*/ +static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { + if (fs == NULL) /* no more levels? */ + return VVOID; /* default is global */ + else { + int v = searchvar(fs, n); /* look up locals at current level */ + if (v >= 0) { /* found? */ + init_exp(var, VLOCAL, v); /* variable is local */ + if (!base) + markupval(fs, v); /* local will be used as an upval */ + return VLOCAL; + } + else { /* not found as local at current level; try upvalues */ + int idx = searchupvalue(fs, n); /* try existing upvalues */ + if (idx < 0) { /* not found? */ + if (singlevaraux(fs->prev, n, var, 0) == VVOID) /* try upper levels */ + return VVOID; /* not found; is a global */ + /* else was LOCAL or UPVAL */ + idx = newupvalue(fs, n, var); /* will be a new upvalue */ + } + init_exp(var, VUPVAL, idx); + return VUPVAL; + } + } +} + + +static void singlevar (LexState *ls, expdesc *var) { + TString *varname = str_checkname(ls); + FuncState *fs = ls->fs; + if (singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */ + expdesc key; + singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ + lua_assert(var->k == VLOCAL || var->k == VUPVAL); + codestring(ls, &key, varname); /* key is variable name */ + luaK_indexed(fs, var, &key); /* env[varname] */ + } +} + + +static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { + FuncState *fs = ls->fs; + int extra = nvars - nexps; + if (hasmultret(e->k)) { + extra++; /* includes call itself */ + if (extra < 0) extra = 0; + luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ + if (extra > 1) luaK_reserveregs(fs, extra-1); + } + else { + if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */ + if (extra > 0) { + int reg = fs->freereg; + luaK_reserveregs(fs, extra); + luaK_nil(fs, reg, extra); + } + } +} + + +static void enterlevel (LexState *ls) { + lua_State *L = ls->L; + ++L->nCcalls; + checklimit(ls->fs, L->nCcalls, LUAI_MAXCCALLS, "C levels"); +} + + +#define leavelevel(ls) ((ls)->L->nCcalls--) + + +static void closegoto (LexState *ls, int g, Labeldesc *label) { + int i; + FuncState *fs = ls->fs; + Labellist *gl = &ls->dyd->gt; + Labeldesc *gt = &gl->arr[g]; + lua_assert(luaS_eqstr(gt->name, label->name)); + if (gt->nactvar < label->nactvar) { + TString *vname = getlocvar(fs, gt->nactvar)->varname; + const char *msg = luaO_pushfstring(ls->L, + "<goto %s> at line %d jumps into the scope of local " LUA_QS, + getstr(gt->name), gt->line, getstr(vname)); + semerror(ls, msg); + } + luaK_patchlist(fs, gt->pc, label->pc); + /* remove goto from pending list */ + for (i = g; i < gl->n - 1; i++) + gl->arr[i] = gl->arr[i + 1]; + gl->n--; +} + + +/* +** try to close a goto with existing labels; this solves backward jumps +*/ +static int findlabel (LexState *ls, int g) { + int i; + BlockCnt *bl = ls->fs->bl; + Dyndata *dyd = ls->dyd; + Labeldesc *gt = &dyd->gt.arr[g]; + /* check labels in current block for a match */ + for (i = bl->firstlabel; i < dyd->label.n; i++) { + Labeldesc *lb = &dyd->label.arr[i]; + if (luaS_eqstr(lb->name, gt->name)) { /* correct label? */ + if (gt->nactvar > lb->nactvar && + (bl->upval || dyd->label.n > bl->firstlabel)) + luaK_patchclose(ls->fs, gt->pc, lb->nactvar); + closegoto(ls, g, lb); /* close it */ + return 1; + } + } + return 0; /* label not found; cannot close goto */ +} + + +static int newlabelentry (LexState *ls, Labellist *l, TString *name, + int line, int pc) { + int n = l->n; + luaM_growvector(ls->L, l->arr, n, l->size, + Labeldesc, SHRT_MAX, "labels/gotos"); + l->arr[n].name = name; + l->arr[n].line = line; + l->arr[n].nactvar = ls->fs->nactvar; + l->arr[n].pc = pc; + l->n++; + return n; +} + + +/* +** check whether new label 'lb' matches any pending gotos in current +** block; solves forward jumps +*/ +static void findgotos (LexState *ls, Labeldesc *lb) { + Labellist *gl = &ls->dyd->gt; + int i = ls->fs->bl->firstgoto; + while (i < gl->n) { + if (luaS_eqstr(gl->arr[i].name, lb->name)) + closegoto(ls, i, lb); + else + i++; + } +} + + +/* +** "export" pending gotos to outer level, to check them against +** outer labels; if the block being exited has upvalues, and +** the goto exits the scope of any variable (which can be the +** upvalue), close those variables being exited. +*/ +static void movegotosout (FuncState *fs, BlockCnt *bl) { + int i = bl->firstgoto; + Labellist *gl = &fs->ls->dyd->gt; + /* correct pending gotos to current block and try to close it + with visible labels */ + while (i < gl->n) { + Labeldesc *gt = &gl->arr[i]; + if (gt->nactvar > bl->nactvar) { + if (bl->upval) + luaK_patchclose(fs, gt->pc, bl->nactvar); + gt->nactvar = bl->nactvar; + } + if (!findlabel(fs->ls, i)) + i++; /* move to next one */ + } +} + + +static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { + bl->isloop = isloop; + bl->nactvar = fs->nactvar; + bl->firstlabel = fs->ls->dyd->label.n; + bl->firstgoto = fs->ls->dyd->gt.n; + bl->upval = 0; + bl->previous = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == fs->nactvar); +} + + +/* +** create a label named "break" to resolve break statements +*/ +static void breaklabel (LexState *ls) { + TString *n = luaS_new(ls->L, "break"); + int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc); + findgotos(ls, &ls->dyd->label.arr[l]); +} + +/* +** generates an error for an undefined 'goto'; choose appropriate +** message when label name is a reserved word (which can only be 'break') +*/ +static l_noret undefgoto (LexState *ls, Labeldesc *gt) { + const char *msg = isreserved(gt->name) + ? "<%s> at line %d not inside a loop" + : "no visible label " LUA_QS " for <goto> at line %d"; + msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); + semerror(ls, msg); +} + + +static void leaveblock (FuncState *fs) { + BlockCnt *bl = fs->bl; + LexState *ls = fs->ls; + if (bl->previous && bl->upval) { + /* create a 'jump to here' to close upvalues */ + int j = luaK_jump(fs); + luaK_patchclose(fs, j, bl->nactvar); + luaK_patchtohere(fs, j); + } + if (bl->isloop) + breaklabel(ls); /* close pending breaks */ + fs->bl = bl->previous; + removevars(fs, bl->nactvar); + lua_assert(bl->nactvar == fs->nactvar); + fs->freereg = fs->nactvar; /* free registers */ + ls->dyd->label.n = bl->firstlabel; /* remove local labels */ + if (bl->previous) /* inner block? */ + movegotosout(fs, bl); /* update pending gotos to outer block */ + else if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ + undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ +} + + +/* +** adds a new prototype into list of prototypes +*/ +static Proto *addprototype (LexState *ls) { + Proto *clp; + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; /* prototype of current function */ + if (fs->np >= f->sizep) { + int oldsize = f->sizep; + luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions"); + while (oldsize < f->sizep) f->p[oldsize++] = NULL; + } + f->p[fs->np++] = clp = luaF_newproto(L); + luaC_objbarrier(L, f, clp); + return clp; +} + + +/* +** codes instruction to create new closure in parent function. +** The OP_CLOSURE instruction must use the last available register, +** so that, if it invokes the GC, the GC knows which registers +** are in use at that time. +*/ +static void codeclosure (LexState *ls, expdesc *v) { + FuncState *fs = ls->fs->prev; + init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); + luaK_exp2nextreg(fs, v); /* fix it at the last register */ +} + + +static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { + lua_State *L = ls->L; + Proto *f; + fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + ls->fs = fs; + fs->pc = 0; + fs->lasttarget = 0; + fs->jpc = NO_JUMP; + fs->freereg = 0; + fs->nk = 0; + fs->np = 0; + fs->nups = 0; + fs->nlocvars = 0; + fs->nactvar = 0; + fs->firstlocal = ls->dyd->actvar.n; + fs->bl = NULL; + f = fs->f; + f->source = ls->source; + f->maxstacksize = 2; /* registers 0/1 are always valid */ + fs->h = luaH_new(L); + /* anchor table of constants (to avoid being collected) */ + sethvalue2s(L, L->top, fs->h); + incr_top(L); + enterblock(fs, bl, 0); +} + + +static void close_func (LexState *ls) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; + luaK_ret(fs, 0, 0); /* final return */ + leaveblock(fs); + luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); + f->sizecode = fs->pc; + luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); + f->sizelineinfo = fs->pc; + luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); + f->sizek = fs->nk; + luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); + f->sizep = fs->np; + luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar); + f->sizelocvars = fs->nlocvars; + luaM_reallocvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); + f->sizeupvalues = fs->nups; + lua_assert(fs->bl == NULL); + ls->fs = fs->prev; + /* last token read was anchored in defunct function; must re-anchor it */ + anchor_token(ls); + L->top--; /* pop table of constants */ + luaC_checkGC(L); +} + + + +/*============================================================*/ +/* GRAMMAR RULES */ +/*============================================================*/ + + +/* +** check whether current token is in the follow set of a block. +** 'until' closes syntactical blocks, but do not close scope, +** so it handled in separate. +*/ +static int block_follow (LexState *ls, int withuntil) { + switch (ls->t.token) { + case TK_ELSE: case TK_ELSEIF: + case TK_END: case TK_EOS: + return 1; + case TK_UNTIL: return withuntil; + default: return 0; + } +} + + +static void statlist (LexState *ls) { + /* statlist -> { stat [`;'] } */ + while (!block_follow(ls, 1)) { + if (ls->t.token == TK_RETURN) { + statement(ls); + return; /* 'return' must be last statement */ + } + statement(ls); + } +} + + +static void fieldsel (LexState *ls, expdesc *v) { + /* fieldsel -> ['.' | ':'] NAME */ + FuncState *fs = ls->fs; + expdesc key; + luaK_exp2anyregup(fs, v); + luaX_next(ls); /* skip the dot or colon */ + checkname(ls, &key); + luaK_indexed(fs, v, &key); +} + + +static void yindex (LexState *ls, expdesc *v) { + /* index -> '[' expr ']' */ + luaX_next(ls); /* skip the '[' */ + expr(ls, v); + luaK_exp2val(ls->fs, v); + checknext(ls, ']'); +} + + +/* +** {====================================================================== +** Rules for Constructors +** ======================================================================= +*/ + + +struct ConsControl { + expdesc v; /* last list item read */ + expdesc *t; /* table descriptor */ + int nh; /* total number of `record' elements */ + int na; /* total number of array elements */ + int tostore; /* number of array elements pending to be stored */ +}; + + +static void recfield (LexState *ls, struct ConsControl *cc) { + /* recfield -> (NAME | `['exp1`]') = exp1 */ + FuncState *fs = ls->fs; + int reg = ls->fs->freereg; + expdesc key, val; + int rkkey; + if (ls->t.token == TK_NAME) { + checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + checkname(ls, &key); + } + else /* ls->t.token == '[' */ + yindex(ls, &key); + cc->nh++; + checknext(ls, '='); + rkkey = luaK_exp2RK(fs, &key); + expr(ls, &val); + luaK_codeABC(fs, OP_SETTABLE, cc->t->u.info, rkkey, luaK_exp2RK(fs, &val)); + fs->freereg = reg; /* free registers */ +} + + +static void closelistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->v.k == VVOID) return; /* there is no list item */ + luaK_exp2nextreg(fs, &cc->v); + cc->v.k = VVOID; + if (cc->tostore == LFIELDS_PER_FLUSH) { + luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ + cc->tostore = 0; /* no more items pending */ + } +} + + +static void lastlistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->tostore == 0) return; + if (hasmultret(cc->v.k)) { + luaK_setmultret(fs, &cc->v); + luaK_setlist(fs, cc->t->u.info, cc->na, LUA_MULTRET); + cc->na--; /* do not count last expression (unknown number of elements) */ + } + else { + if (cc->v.k != VVOID) + luaK_exp2nextreg(fs, &cc->v); + luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); + } +} + + +static void listfield (LexState *ls, struct ConsControl *cc) { + /* listfield -> exp */ + expr(ls, &cc->v); + checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); + cc->na++; + cc->tostore++; +} + + +static void field (LexState *ls, struct ConsControl *cc) { + /* field -> listfield | recfield */ + switch(ls->t.token) { + case TK_NAME: { /* may be 'listfield' or 'recfield' */ + if (luaX_lookahead(ls) != '=') /* expression? */ + listfield(ls, cc); + else + recfield(ls, cc); + break; + } + case '[': { + recfield(ls, cc); + break; + } + default: { + listfield(ls, cc); + break; + } + } +} + + +static void constructor (LexState *ls, expdesc *t) { + /* constructor -> '{' [ field { sep field } [sep] ] '}' + sep -> ',' | ';' */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); + struct ConsControl cc; + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + init_exp(t, VRELOCABLE, pc); + init_exp(&cc.v, VVOID, 0); /* no value (yet) */ + luaK_exp2nextreg(ls->fs, t); /* fix it at stack top */ + checknext(ls, '{'); + do { + lua_assert(cc.v.k == VVOID || cc.tostore > 0); + if (ls->t.token == '}') break; + closelistfield(fs, &cc); + field(ls, &cc); + } while (testnext(ls, ',') || testnext(ls, ';')); + check_match(ls, '}', '{', line); + lastlistfield(fs, &cc); + SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ + SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ +} + +/* }====================================================================== */ + + + +static void parlist (LexState *ls) { + /* parlist -> [ param { `,' param } ] */ + FuncState *fs = ls->fs; + Proto *f = fs->f; + int nparams = 0; + f->is_vararg = 0; + if (ls->t.token != ')') { /* is `parlist' not empty? */ + do { + switch (ls->t.token) { + case TK_NAME: { /* param -> NAME */ + new_localvar(ls, str_checkname(ls)); + nparams++; + break; + } + case TK_DOTS: { /* param -> `...' */ + luaX_next(ls); + f->is_vararg = 1; + break; + } + default: luaX_syntaxerror(ls, "<name> or " LUA_QL("...") " expected"); + } + } while (!f->is_vararg && testnext(ls, ',')); + } + adjustlocalvars(ls, nparams); + f->numparams = cast_byte(fs->nactvar); + luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ +} + + +static void body (LexState *ls, expdesc *e, int ismethod, int line) { + /* body -> `(' parlist `)' block END */ + FuncState new_fs; + BlockCnt bl; + new_fs.f = addprototype(ls); + new_fs.f->linedefined = line; + open_func(ls, &new_fs, &bl); + checknext(ls, '('); + if (ismethod) { + new_localvarliteral(ls, "self"); /* create 'self' parameter */ + adjustlocalvars(ls, 1); + } + parlist(ls); + checknext(ls, ')'); + statlist(ls); + new_fs.f->lastlinedefined = ls->linenumber; + check_match(ls, TK_END, TK_FUNCTION, line); + codeclosure(ls, e); + close_func(ls); +} + + +static int explist (LexState *ls, expdesc *v) { + /* explist -> expr { `,' expr } */ + int n = 1; /* at least one expression */ + expr(ls, v); + while (testnext(ls, ',')) { + luaK_exp2nextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + + +static void funcargs (LexState *ls, expdesc *f, int line) { + FuncState *fs = ls->fs; + expdesc args; + int base, nparams; + switch (ls->t.token) { + case '(': { /* funcargs -> `(' [ explist ] `)' */ + luaX_next(ls); + if (ls->t.token == ')') /* arg list is empty? */ + args.k = VVOID; + else { + explist(ls, &args); + luaK_setmultret(fs, &args); + } + check_match(ls, ')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + constructor(ls, &args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + codestring(ls, &args, ls->t.seminfo.ts); + luaX_next(ls); /* must use `seminfo' before `next' */ + break; + } + default: { + luaX_syntaxerror(ls, "function arguments expected"); + } + } + lua_assert(f->k == VNONRELOC); + base = f->u.info; /* base register for call */ + if (hasmultret(args.k)) + nparams = LUA_MULTRET; /* open call */ + else { + if (args.k != VVOID) + luaK_exp2nextreg(fs, &args); /* close last argument */ + nparams = fs->freereg - (base+1); + } + init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); + luaK_fixline(fs, line); + fs->freereg = base+1; /* call remove function and arguments and leaves + (unless changed) one result */ +} + + + + +/* +** {====================================================================== +** Expression parsing +** ======================================================================= +*/ + + +static void primaryexp (LexState *ls, expdesc *v) { + /* primaryexp -> NAME | '(' expr ')' */ + switch (ls->t.token) { + case '(': { + int line = ls->linenumber; + luaX_next(ls); + expr(ls, v); + check_match(ls, ')', '(', line); + luaK_dischargevars(ls->fs, v); + return; + } + case TK_NAME: { + singlevar(ls, v); + return; + } + default: { + luaX_syntaxerror(ls, "unexpected symbol"); + } + } +} + + +static void suffixedexp (LexState *ls, expdesc *v) { + /* suffixedexp -> + primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + primaryexp(ls, v); + for (;;) { + switch (ls->t.token) { + case '.': { /* fieldsel */ + fieldsel(ls, v); + break; + } + case '[': { /* `[' exp1 `]' */ + expdesc key; + luaK_exp2anyregup(fs, v); + yindex(ls, &key); + luaK_indexed(fs, v, &key); + break; + } + case ':': { /* `:' NAME funcargs */ + expdesc key; + luaX_next(ls); + checkname(ls, &key); + luaK_self(fs, v, &key); + funcargs(ls, v, line); + break; + } + case '(': case TK_STRING: case '{': { /* funcargs */ + luaK_exp2nextreg(fs, v); + funcargs(ls, v, line); + break; + } + default: return; + } + } +} + + +static void simpleexp (LexState *ls, expdesc *v) { + /* simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... | + constructor | FUNCTION body | suffixedexp */ + switch (ls->t.token) { + case TK_NUMBER: { + init_exp(v, VKNUM, 0); + v->u.nval = ls->t.seminfo.r; + break; + } + case TK_STRING: { + codestring(ls, v, ls->t.seminfo.ts); + break; + } + case TK_NIL: { + init_exp(v, VNIL, 0); + break; + } + case TK_TRUE: { + init_exp(v, VTRUE, 0); + break; + } + case TK_FALSE: { + init_exp(v, VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState *fs = ls->fs; + check_condition(ls, fs->f->is_vararg, + "cannot use " LUA_QL("...") " outside a vararg function"); + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); + break; + } + case '{': { /* constructor */ + constructor(ls, v); + return; + } + case TK_FUNCTION: { + luaX_next(ls); + body(ls, v, 0, ls->linenumber); + return; + } + default: { + suffixedexp(ls, v); + return; + } + } + luaX_next(ls); +} + + +static UnOpr getunopr (int op) { + switch (op) { + case TK_NOT: return OPR_NOT; + case '-': return OPR_MINUS; + case '#': return OPR_LEN; + default: return OPR_NOUNOPR; + } +} + + +static BinOpr getbinopr (int op) { + switch (op) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '/': return OPR_DIV; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case TK_CONCAT: return OPR_CONCAT; + case TK_NE: return OPR_NE; + case TK_EQ: return OPR_EQ; + case '<': return OPR_LT; + case TK_LE: return OPR_LE; + case '>': return OPR_GT; + case TK_GE: return OPR_GE; + case TK_AND: return OPR_AND; + case TK_OR: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + + +static const struct { + lu_byte left; /* left priority for each binary operator */ + lu_byte right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `*' `/' `%' */ + {10, 9}, {5, 4}, /* ^, .. (right associative) */ + {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ + {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ + {2, 2}, {1, 1} /* and, or */ +}; + +#define UNARY_PRIORITY 8 /* priority for unary operators */ + + +/* +** subexpr -> (simpleexp | unop subexpr) { binop subexpr } +** where `binop' is any binary operator with a priority higher than `limit' +*/ +static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { + BinOpr op; + UnOpr uop; + enterlevel(ls); + uop = getunopr(ls->t.token); + if (uop != OPR_NOUNOPR) { + int line = ls->linenumber; + luaX_next(ls); + subexpr(ls, v, UNARY_PRIORITY); + luaK_prefix(ls->fs, uop, v, line); + } + else simpleexp(ls, v); + /* expand while operators have priorities higher than `limit' */ + op = getbinopr(ls->t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2; + BinOpr nextop; + int line = ls->linenumber; + luaX_next(ls); + luaK_infix(ls->fs, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + luaK_posfix(ls->fs, op, v, &v2, line); + op = nextop; + } + leavelevel(ls); + return op; /* return first untreated operator */ +} + + +static void expr (LexState *ls, expdesc *v) { + subexpr(ls, v, 0); +} + +/* }==================================================================== */ + + + +/* +** {====================================================================== +** Rules for Statements +** ======================================================================= +*/ + + +static void block (LexState *ls) { + /* block -> statlist */ + FuncState *fs = ls->fs; + BlockCnt bl; + enterblock(fs, &bl, 0); + statlist(ls); + leaveblock(fs); +} + + +/* +** structure to chain all variables in the left-hand side of an +** assignment +*/ +struct LHS_assign { + struct LHS_assign *prev; + expdesc v; /* variable (global, local, upvalue, or indexed) */ +}; + + +/* +** check whether, in an assignment to an upvalue/local variable, the +** upvalue/local variable is begin used in a previous assignment to a +** table. If so, save original upvalue/local value in a safe place and +** use this safe copy in the previous assignment. +*/ +static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { + FuncState *fs = ls->fs; + int extra = fs->freereg; /* eventual position to save local variable */ + int conflict = 0; + for (; lh; lh = lh->prev) { /* check all previous assignments */ + if (lh->v.k == VINDEXED) { /* assigning to a table? */ + /* table is the upvalue/local being assigned now? */ + if (lh->v.u.ind.vt == v->k && lh->v.u.ind.t == v->u.info) { + conflict = 1; + lh->v.u.ind.vt = VLOCAL; + lh->v.u.ind.t = extra; /* previous assignment will use safe copy */ + } + /* index is the local being assigned? (index cannot be upvalue) */ + if (v->k == VLOCAL && lh->v.u.ind.idx == v->u.info) { + conflict = 1; + lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ + } + } + } + if (conflict) { + /* copy upvalue/local value to a temporary (in position 'extra') */ + OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; + luaK_codeABC(fs, op, extra, v->u.info, 0); + luaK_reserveregs(fs, 1); + } +} + + +static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { + expdesc e; + check_condition(ls, vkisvar(lh->v.k), "syntax error"); + if (testnext(ls, ',')) { /* assignment -> ',' suffixedexp assignment */ + struct LHS_assign nv; + nv.prev = lh; + suffixedexp(ls, &nv.v); + if (nv.v.k != VINDEXED) + check_conflict(ls, lh, &nv.v); + checklimit(ls->fs, nvars + ls->L->nCcalls, LUAI_MAXCCALLS, + "C levels"); + assignment(ls, &nv, nvars+1); + } + else { /* assignment -> `=' explist */ + int nexps; + checknext(ls, '='); + nexps = explist(ls, &e); + if (nexps != nvars) { + adjust_assign(ls, nvars, nexps, &e); + if (nexps > nvars) + ls->fs->freereg -= nexps - nvars; /* remove extra values */ + } + else { + luaK_setoneret(ls->fs, &e); /* close last expression */ + luaK_storevar(ls->fs, &lh->v, &e); + return; /* avoid default */ + } + } + init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ + luaK_storevar(ls->fs, &lh->v, &e); +} + + +static int cond (LexState *ls) { + /* cond -> exp */ + expdesc v; + expr(ls, &v); /* read condition */ + if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */ + luaK_goiftrue(ls->fs, &v); + return v.f; +} + + +static void gotostat (LexState *ls, int pc) { + int line = ls->linenumber; + TString *label; + int g; + if (testnext(ls, TK_GOTO)) + label = str_checkname(ls); + else { + luaX_next(ls); /* skip break */ + label = luaS_new(ls->L, "break"); + } + g = newlabelentry(ls, &ls->dyd->gt, label, line, pc); + findlabel(ls, g); /* close it if label already defined */ +} + + +/* check for repeated labels on the same block */ +static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) { + int i; + for (i = fs->bl->firstlabel; i < ll->n; i++) { + if (luaS_eqstr(label, ll->arr[i].name)) { + const char *msg = luaO_pushfstring(fs->ls->L, + "label " LUA_QS " already defined on line %d", + getstr(label), ll->arr[i].line); + semerror(fs->ls, msg); + } + } +} + + +/* skip no-op statements */ +static void skipnoopstat (LexState *ls) { + while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) + statement(ls); +} + + +static void labelstat (LexState *ls, TString *label, int line) { + /* label -> '::' NAME '::' */ + FuncState *fs = ls->fs; + Labellist *ll = &ls->dyd->label; + int l; /* index of new label being created */ + checkrepeated(fs, ll, label); /* check for repeated labels */ + checknext(ls, TK_DBCOLON); /* skip double colon */ + /* create new entry for this label */ + l = newlabelentry(ls, ll, label, line, fs->pc); + skipnoopstat(ls); /* skip other no-op statements */ + if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */ + /* assume that locals are already out of scope */ + ll->arr[l].nactvar = fs->bl->nactvar; + } + findgotos(ls, &ll->arr[l]); +} + + +static void whilestat (LexState *ls, int line) { + /* whilestat -> WHILE cond DO block END */ + FuncState *fs = ls->fs; + int whileinit; + int condexit; + BlockCnt bl; + luaX_next(ls); /* skip WHILE */ + whileinit = luaK_getlabel(fs); + condexit = cond(ls); + enterblock(fs, &bl, 1); + checknext(ls, TK_DO); + block(ls); + luaK_jumpto(fs, whileinit); + check_match(ls, TK_END, TK_WHILE, line); + leaveblock(fs); + luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ +} + + +static void repeatstat (LexState *ls, int line) { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState *fs = ls->fs; + int repeat_init = luaK_getlabel(fs); + BlockCnt bl1, bl2; + enterblock(fs, &bl1, 1); /* loop block */ + enterblock(fs, &bl2, 0); /* scope block */ + luaX_next(ls); /* skip REPEAT */ + statlist(ls); + check_match(ls, TK_UNTIL, TK_REPEAT, line); + condexit = cond(ls); /* read condition (inside scope block) */ + if (bl2.upval) /* upvalues? */ + luaK_patchclose(fs, condexit, bl2.nactvar); + leaveblock(fs); /* finish scope */ + luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ + leaveblock(fs); /* finish loop */ +} + + +static int exp1 (LexState *ls) { + expdesc e; + int reg; + expr(ls, &e); + luaK_exp2nextreg(ls->fs, &e); + lua_assert(e.k == VNONRELOC); + reg = e.u.info; + return reg; +} + + +static void forbody (LexState *ls, int base, int line, int nvars, int isnum) { + /* forbody -> DO block */ + BlockCnt bl; + FuncState *fs = ls->fs; + int prep, endfor; + adjustlocalvars(ls, 3); /* control variables */ + checknext(ls, TK_DO); + prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs); + enterblock(fs, &bl, 0); /* scope for declared variables */ + adjustlocalvars(ls, nvars); + luaK_reserveregs(fs, nvars); + block(ls); + leaveblock(fs); /* end of scope for declared variables */ + luaK_patchtohere(fs, prep); + if (isnum) /* numeric for? */ + endfor = luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP); + else { /* generic for */ + luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars); + luaK_fixline(fs, line); + endfor = luaK_codeAsBx(fs, OP_TFORLOOP, base + 2, NO_JUMP); + } + luaK_patchlist(fs, endfor, prep + 1); + luaK_fixline(fs, line); +} + + +static void fornum (LexState *ls, TString *varname, int line) { + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + FuncState *fs = ls->fs; + int base = fs->freereg; + new_localvarliteral(ls, "(for index)"); + new_localvarliteral(ls, "(for limit)"); + new_localvarliteral(ls, "(for step)"); + new_localvar(ls, varname); + checknext(ls, '='); + exp1(ls); /* initial value */ + checknext(ls, ','); + exp1(ls); /* limit */ + if (testnext(ls, ',')) + exp1(ls); /* optional step */ + else { /* default step = 1 */ + luaK_codek(fs, fs->freereg, luaK_numberK(fs, 1)); + luaK_reserveregs(fs, 1); + } + forbody(ls, base, line, 1, 1); +} + + +static void forlist (LexState *ls, TString *indexname) { + /* forlist -> NAME {,NAME} IN explist forbody */ + FuncState *fs = ls->fs; + expdesc e; + int nvars = 4; /* gen, state, control, plus at least one declared var */ + int line; + int base = fs->freereg; + /* create control variables */ + new_localvarliteral(ls, "(for generator)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for control)"); + /* create declared variables */ + new_localvar(ls, indexname); + while (testnext(ls, ',')) { + new_localvar(ls, str_checkname(ls)); + nvars++; + } + checknext(ls, TK_IN); + line = ls->linenumber; + adjust_assign(ls, 3, explist(ls, &e), &e); + luaK_checkstack(fs, 3); /* extra space to call generator */ + forbody(ls, base, line, nvars - 3, 0); +} + + +static void forstat (LexState *ls, int line) { + /* forstat -> FOR (fornum | forlist) END */ + FuncState *fs = ls->fs; + TString *varname; + BlockCnt bl; + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + luaX_next(ls); /* skip `for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->t.token) { + case '=': fornum(ls, varname, line); break; + case ',': case TK_IN: forlist(ls, varname); break; + default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected"); + } + check_match(ls, TK_END, TK_FOR, line); + leaveblock(fs); /* loop scope (`break' jumps to this point) */ +} + + +static void test_then_block (LexState *ls, int *escapelist) { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + BlockCnt bl; + FuncState *fs = ls->fs; + expdesc v; + int jf; /* instruction to skip 'then' code (if condition is false) */ + luaX_next(ls); /* skip IF or ELSEIF */ + expr(ls, &v); /* read condition */ + checknext(ls, TK_THEN); + if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { + luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ + enterblock(fs, &bl, 0); /* must enter block before 'goto' */ + gotostat(ls, v.t); /* handle goto/break */ + skipnoopstat(ls); /* skip other no-op statements */ + if (block_follow(ls, 0)) { /* 'goto' is the entire block? */ + leaveblock(fs); + return; /* and that is it */ + } + else /* must skip over 'then' part if condition is false */ + jf = luaK_jump(fs); + } + else { /* regular case (not goto/break) */ + luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ + enterblock(fs, &bl, 0); + jf = v.f; + } + statlist(ls); /* `then' part */ + leaveblock(fs); + if (ls->t.token == TK_ELSE || + ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ + luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */ + luaK_patchtohere(fs, jf); +} + + +static void ifstat (LexState *ls, int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + int escapelist = NO_JUMP; /* exit list for finished parts */ + test_then_block(ls, &escapelist); /* IF cond THEN block */ + while (ls->t.token == TK_ELSEIF) + test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ + if (testnext(ls, TK_ELSE)) + block(ls); /* `else' part */ + check_match(ls, TK_END, TK_IF, line); + luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */ +} + + +static void localfunc (LexState *ls) { + expdesc b; + FuncState *fs = ls->fs; + new_localvar(ls, str_checkname(ls)); /* new local variable */ + adjustlocalvars(ls, 1); /* enter its scope */ + body(ls, &b, 0, ls->linenumber); /* function created in next register */ + /* debug information will only see the variable after this point! */ + getlocvar(fs, b.u.info)->startpc = fs->pc; +} + + +static void localstat (LexState *ls) { + /* stat -> LOCAL NAME {`,' NAME} [`=' explist] */ + int nvars = 0; + int nexps; + expdesc e; + do { + new_localvar(ls, str_checkname(ls)); + nvars++; + } while (testnext(ls, ',')); + if (testnext(ls, '=')) + nexps = explist(ls, &e); + else { + e.k = VVOID; + nexps = 0; + } + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); +} + + +static int funcname (LexState *ls, expdesc *v) { + /* funcname -> NAME {fieldsel} [`:' NAME] */ + int ismethod = 0; + singlevar(ls, v); + while (ls->t.token == '.') + fieldsel(ls, v); + if (ls->t.token == ':') { + ismethod = 1; + fieldsel(ls, v); + } + return ismethod; +} + + +static void funcstat (LexState *ls, int line) { + /* funcstat -> FUNCTION funcname body */ + int ismethod; + expdesc v, b; + luaX_next(ls); /* skip FUNCTION */ + ismethod = funcname(ls, &v); + body(ls, &b, ismethod, line); + luaK_storevar(ls->fs, &v, &b); + luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ +} + + +static void exprstat (LexState *ls) { + /* stat -> func | assignment */ + FuncState *fs = ls->fs; + struct LHS_assign v; + suffixedexp(ls, &v.v); + if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */ + v.prev = NULL; + assignment(ls, &v, 1); + } + else { /* stat -> func */ + check_condition(ls, v.v.k == VCALL, "syntax error"); + SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ + } +} + + +static void retstat (LexState *ls) { + /* stat -> RETURN [explist] [';'] */ + FuncState *fs = ls->fs; + expdesc e; + int first, nret; /* registers with returned values */ + if (block_follow(ls, 1) || ls->t.token == ';') + first = nret = 0; /* return no values */ + else { + nret = explist(ls, &e); /* optional return values */ + if (hasmultret(e.k)) { + luaK_setmultret(fs, &e); + if (e.k == VCALL && nret == 1) { /* tail call? */ + SET_OPCODE(getcode(fs,&e), OP_TAILCALL); + lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar); + } + first = fs->nactvar; + nret = LUA_MULTRET; /* return all values */ + } + else { + if (nret == 1) /* only one single value? */ + first = luaK_exp2anyreg(fs, &e); + else { + luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */ + first = fs->nactvar; /* return all `active' values */ + lua_assert(nret == fs->freereg - first); + } + } + } + luaK_ret(fs, first, nret); + testnext(ls, ';'); /* skip optional semicolon */ +} + + +static void statement (LexState *ls) { + int line = ls->linenumber; /* may be needed for error messages */ + enterlevel(ls); + switch (ls->t.token) { + case ';': { /* stat -> ';' (empty statement) */ + luaX_next(ls); /* skip ';' */ + break; + } + case TK_IF: { /* stat -> ifstat */ + ifstat(ls, line); + break; + } + case TK_WHILE: { /* stat -> whilestat */ + whilestat(ls, line); + break; + } + case TK_DO: { /* stat -> DO block END */ + luaX_next(ls); /* skip DO */ + block(ls); + check_match(ls, TK_END, TK_DO, line); + break; + } + case TK_FOR: { /* stat -> forstat */ + forstat(ls, line); + break; + } + case TK_REPEAT: { /* stat -> repeatstat */ + repeatstat(ls, line); + break; + } + case TK_FUNCTION: { /* stat -> funcstat */ + funcstat(ls, line); + break; + } + case TK_LOCAL: { /* stat -> localstat */ + luaX_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_FUNCTION)) /* local function? */ + localfunc(ls); + else + localstat(ls); + break; + } + case TK_DBCOLON: { /* stat -> label */ + luaX_next(ls); /* skip double colon */ + labelstat(ls, str_checkname(ls), line); + break; + } + case TK_RETURN: { /* stat -> retstat */ + luaX_next(ls); /* skip RETURN */ + retstat(ls); + break; + } + case TK_BREAK: /* stat -> breakstat */ + case TK_GOTO: { /* stat -> 'goto' NAME */ + gotostat(ls, luaK_jump(ls->fs)); + break; + } + default: { /* stat -> func | assignment */ + exprstat(ls); + break; + } + } + lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && + ls->fs->freereg >= ls->fs->nactvar); + ls->fs->freereg = ls->fs->nactvar; /* free registers */ + leavelevel(ls); +} + +/* }====================================================================== */ + + +/* +** compiles the main function, which is a regular vararg function with an +** upvalue named LUA_ENV +*/ +static void mainfunc (LexState *ls, FuncState *fs) { + BlockCnt bl; + expdesc v; + open_func(ls, fs, &bl); + fs->f->is_vararg = 1; /* main function is always vararg */ + init_exp(&v, VLOCAL, 0); /* create and... */ + newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ + luaX_next(ls); /* read first token */ + statlist(ls); /* parse main body */ + check(ls, TK_EOS); + close_func(ls); +} + + +Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar) { + LexState lexstate; + FuncState funcstate; + Closure *cl = luaF_newLclosure(L, 1); /* create main closure */ + /* anchor closure (to avoid being collected) */ + setclLvalue(L, L->top, cl); + incr_top(L); + funcstate.f = cl->l.p = luaF_newproto(L); + funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ + lexstate.buff = buff; + lexstate.dyd = dyd; + dyd->actvar.n = dyd->gt.n = dyd->label.n = 0; + luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar); + mainfunc(&lexstate, &funcstate); + lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); + /* all scopes should be correctly finished */ + lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); + return cl; /* it's on the stack too */ +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lparser.h b/usr/src/uts/common/fs/zfs/lua/lparser.h new file mode 100644 index 0000000000..0346e3c41a --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lparser.h @@ -0,0 +1,119 @@ +/* +** $Id: lparser.h,v 1.70.1.1 2013/04/12 18:48:47 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#ifndef lparser_h +#define lparser_h + +#include "llimits.h" +#include "lobject.h" +#include "lzio.h" + + +/* +** Expression descriptor +*/ + +typedef enum { + VVOID, /* no value */ + VNIL, + VTRUE, + VFALSE, + VK, /* info = index of constant in `k' */ + VKNUM, /* nval = numerical value */ + VNONRELOC, /* info = result register */ + VLOCAL, /* info = local register */ + VUPVAL, /* info = index of upvalue in 'upvalues' */ + VINDEXED, /* t = table register/upvalue; idx = index R/K */ + VJMP, /* info = instruction pc */ + VRELOCABLE, /* info = instruction pc */ + VCALL, /* info = instruction pc */ + VVARARG /* info = instruction pc */ +} expkind; + + +#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXED) +#define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL) + +typedef struct expdesc { + expkind k; + union { + struct { /* for indexed variables (VINDEXED) */ + short idx; /* index (R/K) */ + lu_byte t; /* table (register or upvalue) */ + lu_byte vt; /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */ + } ind; + int info; /* for generic use */ + lua_Number nval; /* for VKNUM */ + } u; + int t; /* patch list of `exit when true' */ + int f; /* patch list of `exit when false' */ +} expdesc; + + +/* description of active local variable */ +typedef struct Vardesc { + short idx; /* variable index in stack */ +} Vardesc; + + +/* description of pending goto statements and label statements */ +typedef struct Labeldesc { + TString *name; /* label identifier */ + int pc; /* position in code */ + int line; /* line where it appeared */ + lu_byte nactvar; /* local level where it appears in current block */ +} Labeldesc; + + +/* list of labels or gotos */ +typedef struct Labellist { + Labeldesc *arr; /* array */ + int n; /* number of entries in use */ + int size; /* array size */ +} Labellist; + + +/* dynamic structures used by the parser */ +typedef struct Dyndata { + struct { /* list of active local variables */ + Vardesc *arr; + int n; + int size; + } actvar; + Labellist gt; /* list of pending gotos */ + Labellist label; /* list of active labels */ +} Dyndata; + + +/* control of blocks */ +struct BlockCnt; /* defined in lparser.c */ + + +/* state needed to generate code for a given function */ +typedef struct FuncState { + Proto *f; /* current function header */ + Table *h; /* table to find (and reuse) elements in `k' */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct BlockCnt *bl; /* chain of current blocks */ + int pc; /* next position to code (equivalent to `ncode') */ + int lasttarget; /* 'label' of last 'jump label' */ + int jpc; /* list of pending jumps to `pc' */ + int nk; /* number of elements in `k' */ + int np; /* number of elements in `p' */ + int firstlocal; /* index of first local var (in Dyndata array) */ + short nlocvars; /* number of elements in 'f->locvars' */ + lu_byte nactvar; /* number of active local variables */ + lu_byte nups; /* number of upvalues */ + lu_byte freereg; /* first free register */ +} FuncState; + + +LUAI_FUNC Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar); + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lstate.c b/usr/src/uts/common/fs/zfs/lua/lstate.c new file mode 100644 index 0000000000..b98ce5c2b5 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lstate.c @@ -0,0 +1,321 @@ +/* +** $Id: lstate.c,v 2.99.1.2 2013/11/08 17:45:31 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define lstate_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +#if !defined(LUAI_GCPAUSE) +#define LUAI_GCPAUSE 200 /* 200% */ +#endif + +#if !defined(LUAI_GCMAJOR) +#define LUAI_GCMAJOR 200 /* 200% */ +#endif + +#if !defined(LUAI_GCMUL) +#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ +#endif + + +#define MEMERRMSG "not enough memory" + + +/* +** a macro to help the creation of a unique random seed when a state is +** created; the seed is used to randomize hashes. +*/ +#if !defined(luai_makeseed) +#define luai_makeseed() cast(unsigned int, gethrtime()) +#endif + + + +/* +** thread state + extra space +*/ +typedef struct LX { +#if defined(LUAI_EXTRASPACE) + char buff[LUAI_EXTRASPACE]; +#endif + lua_State l; +} LX; + + +/* +** Main thread combines a thread state and the global state +*/ +typedef struct LG { + LX l; + global_State g; +} LG; + + + +#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) + + +/* +** Compute an initial seed as random as possible. In ANSI, rely on +** Address Space Layout Randomization (if present) to increase +** randomness.. +*/ +#define addbuff(b,p,e) \ + { size_t t = cast(size_t, e); \ + memcpy(buff + p, &t, sizeof(t)); p += sizeof(t); } + +static unsigned int makeseed (lua_State *L) { + char buff[4 * sizeof(size_t)]; + unsigned int h = luai_makeseed(); + int p = 0; + addbuff(buff, p, L); /* heap variable */ + addbuff(buff, p, &h); /* local variable */ + addbuff(buff, p, luaO_nilobject); /* global variable */ + addbuff(buff, p, &lua_newstate); /* public function */ + lua_assert(p == sizeof(buff)); + return luaS_hash(buff, p, h); +} + + +/* +** set GCdebt to a new value keeping the value (totalbytes + GCdebt) +** invariant +*/ +void luaE_setdebt (global_State *g, l_mem debt) { + g->totalbytes -= (debt - g->GCdebt); + g->GCdebt = debt; +} + + +CallInfo *luaE_extendCI (lua_State *L) { + CallInfo *ci = luaM_new(L, CallInfo); + lua_assert(L->ci->next == NULL); + L->ci->next = ci; + ci->previous = L->ci; + ci->next = NULL; + return ci; +} + + +void luaE_freeCI (lua_State *L) { + CallInfo *ci = L->ci; + CallInfo *next = ci->next; + ci->next = NULL; + while ((ci = next) != NULL) { + next = ci->next; + luaM_free(L, ci); + } +} + + +static void stack_init (lua_State *L1, lua_State *L) { + int i; CallInfo *ci; + /* initialize stack array */ + L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, TValue); + L1->stacksize = BASIC_STACK_SIZE; + for (i = 0; i < BASIC_STACK_SIZE; i++) + setnilvalue(L1->stack + i); /* erase new stack */ + L1->top = L1->stack; + L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK; + /* initialize first ci */ + ci = &L1->base_ci; + ci->next = ci->previous = NULL; + ci->callstatus = 0; + ci->func = L1->top; + setnilvalue(L1->top++); /* 'function' entry for this 'ci' */ + ci->top = L1->top + LUA_MINSTACK; + L1->ci = ci; +} + + +static void freestack (lua_State *L) { + if (L->stack == NULL) + return; /* stack not completely built yet */ + L->ci = &L->base_ci; /* free the entire 'ci' list */ + luaE_freeCI(L); + luaM_freearray(L, L->stack, L->stacksize); /* free stack array */ +} + + +/* +** Create registry table and its predefined values +*/ +static void init_registry (lua_State *L, global_State *g) { + TValue mt; + /* create registry */ + Table *registry = luaH_new(L); + sethvalue(L, &g->l_registry, registry); + luaH_resize(L, registry, LUA_RIDX_LAST, 0); + /* registry[LUA_RIDX_MAINTHREAD] = L */ + setthvalue(L, &mt, L); + luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &mt); + /* registry[LUA_RIDX_GLOBALS] = table of globals */ + sethvalue(L, &mt, luaH_new(L)); + luaH_setint(L, registry, LUA_RIDX_GLOBALS, &mt); +} + + +/* +** open parts of the state that may cause memory-allocation errors +*/ +static void f_luaopen (lua_State *L, void *ud) { + global_State *g = G(L); + UNUSED(ud); + stack_init(L, L); /* init stack */ + init_registry(L, g); + luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ + luaT_init(L); + luaX_init(L); + /* pre-create memory-error message */ + g->memerrmsg = luaS_newliteral(L, MEMERRMSG); + luaS_fix(g->memerrmsg); /* it should never be collected */ + g->gcrunning = 1; /* allow gc */ + g->version = lua_version(NULL); + luai_userstateopen(L); +} + + +/* +** preinitialize a state with consistent values without allocating +** any memory (to avoid errors) +*/ +static void preinit_state (lua_State *L, global_State *g) { + G(L) = g; + L->stack = NULL; + L->ci = NULL; + L->stacksize = 0; + L->errorJmp = NULL; + L->nCcalls = 0; + L->hook = NULL; + L->hookmask = 0; + L->basehookcount = 0; + L->allowhook = 1; + resethookcount(L); + L->openupval = NULL; + L->nny = 1; + L->status = LUA_OK; + L->errfunc = 0; +} + + +static void close_state (lua_State *L) { + global_State *g = G(L); + luaF_close(L, L->stack); /* close all upvalues for this thread */ + luaC_freeallobjects(L); /* collect all objects */ + if (g->version) /* closing a fully built state? */ + luai_userstateclose(L); + luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); + luaZ_freebuffer(L, &g->buff); + freestack(L); + lua_assert(gettotalbytes(g) == sizeof(LG)); + (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ +} + + +LUA_API lua_State *lua_newthread (lua_State *L) { + lua_State *L1; + lua_lock(L); + luaC_checkGC(L); + L1 = &luaC_newobj(L, LUA_TTHREAD, sizeof(LX), NULL, offsetof(LX, l))->th; + setthvalue(L, L->top, L1); + api_incr_top(L); + preinit_state(L1, G(L)); + L1->hookmask = L->hookmask; + L1->basehookcount = L->basehookcount; + L1->hook = L->hook; + resethookcount(L1); + luai_userstatethread(L, L1); + stack_init(L1, L); /* init stack */ + lua_unlock(L); + return L1; +} + + +void luaE_freethread (lua_State *L, lua_State *L1) { + LX *l = fromstate(L1); + luaF_close(L1, L1->stack); /* close all upvalues for this thread */ + lua_assert(L1->openupval == NULL); + luai_userstatefree(L, L1); + freestack(L1); + luaM_free(L, l); +} + + +LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { + int i; + lua_State *L; + global_State *g; + LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG))); + if (l == NULL) return NULL; + L = &l->l.l; + g = &l->g; + L->next = NULL; + L->tt = LUA_TTHREAD; + g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); + L->marked = luaC_white(g); + g->gckind = KGC_NORMAL; + preinit_state(L, g); + g->frealloc = f; + g->ud = ud; + g->mainthread = L; + g->seed = makeseed(L); + g->uvhead.u.l.prev = &g->uvhead; + g->uvhead.u.l.next = &g->uvhead; + g->gcrunning = 0; /* no GC while building state */ + g->GCestimate = 0; + g->strt.size = 0; + g->strt.nuse = 0; + g->strt.hash = NULL; + setnilvalue(&g->l_registry); + luaZ_initbuffer(L, &g->buff); + g->panic = NULL; + g->version = NULL; + g->gcstate = GCSpause; + g->allgc = NULL; + g->finobj = NULL; + g->tobefnz = NULL; + g->sweepgc = g->sweepfin = NULL; + g->gray = g->grayagain = NULL; + g->weak = g->ephemeron = g->allweak = NULL; + g->totalbytes = sizeof(LG); + g->GCdebt = 0; + g->gcpause = LUAI_GCPAUSE; + g->gcmajorinc = LUAI_GCMAJOR; + g->gcstepmul = LUAI_GCMUL; + for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; + if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { + /* memory allocation error: free partial state */ + close_state(L); + L = NULL; + } + return L; +} + + +LUA_API void lua_close (lua_State *L) { + L = G(L)->mainthread; /* only the main thread can be closed */ + lua_lock(L); + close_state(L); +} + + diff --git a/usr/src/uts/common/fs/zfs/lua/lstate.h b/usr/src/uts/common/fs/zfs/lua/lstate.h new file mode 100644 index 0000000000..daffd9aacf --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lstate.h @@ -0,0 +1,228 @@ +/* +** $Id: lstate.h,v 2.82.1.1 2013/04/12 18:48:47 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + +#ifndef lstate_h +#define lstate_h + +#include "lua.h" + +#include "lobject.h" +#include "ltm.h" +#include "lzio.h" + + +/* + +** Some notes about garbage-collected objects: All objects in Lua must +** be kept somehow accessible until being freed. +** +** Lua keeps most objects linked in list g->allgc. The link uses field +** 'next' of the CommonHeader. +** +** Strings are kept in several lists headed by the array g->strt.hash. +** +** Open upvalues are not subject to independent garbage collection. They +** are collected together with their respective threads. Lua keeps a +** double-linked list with all open upvalues (g->uvhead) so that it can +** mark objects referred by them. (They are always gray, so they must +** be remarked in the atomic step. Usually their contents would be marked +** when traversing the respective threads, but the thread may already be +** dead, while the upvalue is still accessible through closures.) +** +** Objects with finalizers are kept in the list g->finobj. +** +** The list g->tobefnz links all objects being finalized. + +*/ + + +struct lua_longjmp; /* defined in ldo.c */ + + + +/* extra stack space to handle TM calls and some other extras */ +#define EXTRA_STACK 5 + + +#define BASIC_STACK_SIZE (2*LUA_MINSTACK) + + +/* kinds of Garbage Collection */ +#define KGC_NORMAL 0 +#define KGC_EMERGENCY 1 /* gc was forced by an allocation failure */ +#define KGC_GEN 2 /* generational collection */ + + +typedef struct stringtable { + GCObject **hash; + lu_int32 nuse; /* number of elements */ + int size; +} stringtable; + + +/* +** information about a call +*/ +typedef struct CallInfo { + StkId func; /* function index in the stack */ + StkId top; /* top for this function */ + struct CallInfo *previous, *next; /* dynamic call link */ + short nresults; /* expected number of results from this function */ + lu_byte callstatus; + ptrdiff_t extra; + union { + struct { /* only for Lua functions */ + StkId base; /* base for this function */ + const Instruction *savedpc; + } l; + struct { /* only for C functions */ + int ctx; /* context info. in case of yields */ + lua_CFunction k; /* continuation in case of yields */ + ptrdiff_t old_errfunc; + lu_byte old_allowhook; + lu_byte status; + } c; + } u; +} CallInfo; + + +/* +** Bits in CallInfo status +*/ +#define CIST_LUA (1<<0) /* call is running a Lua function */ +#define CIST_HOOKED (1<<1) /* call is running a debug hook */ +#define CIST_REENTRY (1<<2) /* call is running on same invocation of + luaV_execute of previous call */ +#define CIST_YIELDED (1<<3) /* call reentered after suspension */ +#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ +#define CIST_STAT (1<<5) /* call has an error status (pcall) */ +#define CIST_TAIL (1<<6) /* call was tail called */ +#define CIST_HOOKYIELD (1<<7) /* last hook called yielded */ + + +#define isLua(ci) ((ci)->callstatus & CIST_LUA) + + +/* +** `global state', shared by all threads of this state +*/ +typedef struct global_State { + lua_Alloc frealloc; /* function to reallocate memory */ + void *ud; /* auxiliary data to `frealloc' */ + lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */ + l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ + lu_mem GCmemtrav; /* memory traversed by the GC */ + lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ + stringtable strt; /* hash table for strings */ + TValue l_registry; + unsigned int seed; /* randomized seed for hashes */ + lu_byte currentwhite; + lu_byte gcstate; /* state of garbage collector */ + lu_byte gckind; /* kind of GC running */ + lu_byte gcrunning; /* true if GC is running */ + int sweepstrgc; /* position of sweep in `strt' */ + GCObject *allgc; /* list of all collectable objects */ + GCObject *finobj; /* list of collectable objects with finalizers */ + GCObject **sweepgc; /* current position of sweep in list 'allgc' */ + GCObject **sweepfin; /* current position of sweep in list 'finobj' */ + GCObject *gray; /* list of gray objects */ + GCObject *grayagain; /* list of objects to be traversed atomically */ + GCObject *weak; /* list of tables with weak values */ + GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ + GCObject *allweak; /* list of all-weak tables */ + GCObject *tobefnz; /* list of userdata to be GC */ + UpVal uvhead; /* head of double-linked list of all open upvalues */ + Mbuffer buff; /* temporary buffer for string concatenation */ + int gcpause; /* size of pause between successive GCs */ + int gcmajorinc; /* pause between major collections (only in gen. mode) */ + int gcstepmul; /* GC `granularity' */ + lua_CFunction panic; /* to be called in unprotected errors */ + struct lua_State *mainthread; + const lua_Number *version; /* pointer to version number */ + TString *memerrmsg; /* memory-error message */ + TString *tmname[TM_N]; /* array with tag-method names */ + struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ +} global_State; + + +/* +** `per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte status; + StkId top; /* first free slot in the stack */ + global_State *l_G; + CallInfo *ci; /* call info for current function */ + const Instruction *oldpc; /* last pc traced */ + StkId stack_last; /* last free slot in the stack */ + StkId stack; /* stack base */ + int stacksize; + unsigned short nny; /* number of non-yieldable calls in stack */ + unsigned short nCcalls; /* number of nested C calls */ + lu_byte hookmask; + lu_byte allowhook; + int basehookcount; + int hookcount; + lua_Hook hook; + GCObject *openupval; /* list of open upvalues in this stack */ + GCObject *gclist; + struct lua_longjmp *errorJmp; /* current error recover point */ + ptrdiff_t errfunc; /* current error handling function (stack index) */ + CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ +}; + + +#define G(L) (L->l_G) + + +/* +** Union of all collectable objects +*/ +union GCObject { + GCheader gch; /* common header */ + union TString ts; + union Udata u; + union Closure cl; + struct Table h; + struct Proto p; + struct UpVal uv; + struct lua_State th; /* thread */ +}; + + +#define gch(o) (&(o)->gch) + +/* macros to convert a GCObject into a specific value */ +#define rawgco2ts(o) \ + check_exp(novariant((o)->gch.tt) == LUA_TSTRING, &((o)->ts)) +#define gco2ts(o) (&rawgco2ts(o)->tsv) +#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) +#define gco2u(o) (&rawgco2u(o)->uv) +#define gco2lcl(o) check_exp((o)->gch.tt == LUA_TLCL, &((o)->cl.l)) +#define gco2ccl(o) check_exp((o)->gch.tt == LUA_TCCL, &((o)->cl.c)) +#define gco2cl(o) \ + check_exp(novariant((o)->gch.tt) == LUA_TFUNCTION, &((o)->cl)) +#define gco2t(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) +#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) +#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) +#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) + +/* macro to convert any Lua object into a GCObject */ +#define obj2gco(v) (cast(GCObject *, (v))) + + +/* actual number of total bytes allocated */ +#define gettotalbytes(g) ((g)->totalbytes + (g)->GCdebt) + +LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); +LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); +LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); +LUAI_FUNC void luaE_freeCI (lua_State *L); + + +#endif + diff --git a/usr/src/uts/common/fs/zfs/lua/lstring.c b/usr/src/uts/common/fs/zfs/lua/lstring.c new file mode 100644 index 0000000000..e20ab04b12 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lstring.c @@ -0,0 +1,185 @@ +/* +** $Id: lstring.c,v 2.26.1.1 2013/04/12 18:48:47 roberto Exp $ +** String table (keeps all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define lstring_c +#define LUA_CORE + +#include "lua.h" + +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" + + +/* +** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to +** compute its hash +*/ +#if !defined(LUAI_HASHLIMIT) +#define LUAI_HASHLIMIT 5 +#endif + + +/* +** equality for long strings +*/ +int luaS_eqlngstr (TString *a, TString *b) { + size_t len = a->tsv.len; + lua_assert(a->tsv.tt == LUA_TLNGSTR && b->tsv.tt == LUA_TLNGSTR); + return (a == b) || /* same instance or... */ + ((len == b->tsv.len) && /* equal length and ... */ + (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */ +} + + +/* +** equality for strings +*/ +int luaS_eqstr (TString *a, TString *b) { + return (a->tsv.tt == b->tsv.tt) && + (a->tsv.tt == LUA_TSHRSTR ? eqshrstr(a, b) : luaS_eqlngstr(a, b)); +} + + +unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) { + unsigned int h = seed ^ cast(unsigned int, l); + size_t l1; + size_t step = (l >> LUAI_HASHLIMIT) + 1; + for (l1 = l; l1 >= step; l1 -= step) + h = h ^ ((h<<5) + (h>>2) + cast_byte(str[l1 - 1])); + return h; +} + + +/* +** resizes the string table +*/ +void luaS_resize (lua_State *L, int newsize) { + int i; + stringtable *tb = &G(L)->strt; + /* cannot resize while GC is traversing strings */ + luaC_runtilstate(L, ~bitmask(GCSsweepstring)); + if (newsize > tb->size) { + luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *); + for (i = tb->size; i < newsize; i++) tb->hash[i] = NULL; + } + /* rehash */ + for (i=0; i<tb->size; i++) { + GCObject *p = tb->hash[i]; + tb->hash[i] = NULL; + while (p) { /* for each node in the list */ + GCObject *next = gch(p)->next; /* save next */ + unsigned int h = lmod(gco2ts(p)->hash, newsize); /* new position */ + gch(p)->next = tb->hash[h]; /* chain it */ + tb->hash[h] = p; + resetoldbit(p); /* see MOVE OLD rule */ + p = next; + } + } + if (newsize < tb->size) { + /* shrinking slice must be empty */ + lua_assert(tb->hash[newsize] == NULL && tb->hash[tb->size - 1] == NULL); + luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *); + } + tb->size = newsize; +} + + +/* +** creates a new string object +*/ +static TString *createstrobj (lua_State *L, const char *str, size_t l, + int tag, unsigned int h, GCObject **list) { + TString *ts; + size_t totalsize; /* total size of TString object */ + totalsize = sizeof(TString) + ((l + 1) * sizeof(char)); + ts = &luaC_newobj(L, tag, totalsize, list, 0)->ts; + ts->tsv.len = l; + ts->tsv.hash = h; + ts->tsv.extra = 0; + memcpy(ts+1, str, l*sizeof(char)); + ((char *)(ts+1))[l] = '\0'; /* ending 0 */ + return ts; +} + + +/* +** creates a new short string, inserting it into string table +*/ +static TString *newshrstr (lua_State *L, const char *str, size_t l, + unsigned int h) { + GCObject **list; /* (pointer to) list where it will be inserted */ + stringtable *tb = &G(L)->strt; + TString *s; + if (tb->nuse >= cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) + luaS_resize(L, tb->size*2); /* too crowded */ + list = &tb->hash[lmod(h, tb->size)]; + s = createstrobj(L, str, l, LUA_TSHRSTR, h, list); + tb->nuse++; + return s; +} + + +/* +** checks whether short string exists and reuses it or creates a new one +*/ +static TString *internshrstr (lua_State *L, const char *str, size_t l) { + GCObject *o; + global_State *g = G(L); + unsigned int h = luaS_hash(str, l, g->seed); + for (o = g->strt.hash[lmod(h, g->strt.size)]; + o != NULL; + o = gch(o)->next) { + TString *ts = rawgco2ts(o); + if (h == ts->tsv.hash && + l == ts->tsv.len && + (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { + if (isdead(G(L), o)) /* string is dead (but was not collected yet)? */ + changewhite(o); /* resurrect it */ + return ts; + } + } + return newshrstr(L, str, l, h); /* not found; create a new string */ +} + + +/* +** new string (with explicit length) +*/ +TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { + if (l <= LUAI_MAXSHORTLEN) /* short string? */ + return internshrstr(L, str, l); + else { + if (l + 1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) + luaM_toobig(L); + return createstrobj(L, str, l, LUA_TLNGSTR, G(L)->seed, NULL); + } +} + + +/* +** new zero-terminated string +*/ +TString *luaS_new (lua_State *L, const char *str) { + return luaS_newlstr(L, str, strlen(str)); +} + + +Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { + Udata *u; + if (s > MAX_SIZET - sizeof(Udata)) + luaM_toobig(L); + u = &luaC_newobj(L, LUA_TUSERDATA, sizeof(Udata) + s, NULL, 0)->u; + u->uv.len = s; + u->uv.metatable = NULL; + u->uv.env = e; + return u; +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lstring.h b/usr/src/uts/common/fs/zfs/lua/lstring.h new file mode 100644 index 0000000000..260e7f169b --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lstring.h @@ -0,0 +1,46 @@ +/* +** $Id: lstring.h,v 1.49.1.1 2013/04/12 18:48:47 roberto Exp $ +** String table (keep all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#ifndef lstring_h +#define lstring_h + +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" + + +#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char)) + +#define sizeudata(u) (sizeof(union Udata)+(u)->len) + +#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ + (sizeof(s)/sizeof(char))-1)) + +#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT) + + +/* +** test whether a string is a reserved word +*/ +#define isreserved(s) ((s)->tsv.tt == LUA_TSHRSTR && (s)->tsv.extra > 0) + + +/* +** equality for short strings, which are always internalized +*/ +#define eqshrstr(a,b) check_exp((a)->tsv.tt == LUA_TSHRSTR, (a) == (b)) + + +LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); +LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); +LUAI_FUNC int luaS_eqstr (TString *a, TString *b); +LUAI_FUNC void luaS_resize (lua_State *L, int newsize); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); +LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); +LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lstrlib.c b/usr/src/uts/common/fs/zfs/lua/lstrlib.c new file mode 100644 index 0000000000..b6279edc4a --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lstrlib.c @@ -0,0 +1,1043 @@ +/* +** $Id: lstrlib.c,v 1.178.1.1 2013/04/12 18:48:47 roberto Exp $ +** Standard library for string operations and pattern-matching +** See Copyright Notice in lua.h +*/ + + +#include <sys/ctype.h> +#include <sys/zfs_context.h> + +#define lstrlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* +** maximum number of captures that a pattern can do during +** pattern-matching. This limit is arbitrary. +*/ +#if !defined(LUA_MAXCAPTURES) +#define LUA_MAXCAPTURES 32 +#endif + + +/* macro to `unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + +/* + * PATCHED: add missing character macros. + */ +#define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C)) +#define toupper(C) (((C) >= 'a' && (C) <= 'z') ? (C) - 'a' + 'A': (C)) +#define iscntrl(C) ((((C) >= 0) && ((C) <= 0x1f)) || ((C) == 0x7f)) +#define isgraph(C) ((C) >= 0x21 && (C) <= 0x7E) +#define ispunct(C) (((C) >= 0x21 && (C) <= 0x2F) || \ + ((C) >= 0x3A && (C) <= 0x40) || \ + ((C) >= 0x5B && (C) <= 0x60) || \ + ((C) >= 0x7B && (C) <= 0x7E)) + +/* + * The provided version of sprintf returns a char *, but str_format expects + * it to return the number of characters printed. This version has the expected + * behavior. + */ +static size_t str_sprintf(char *buf, const char *fmt, ...) { + va_list args; + size_t len; + + va_start(args, fmt); + len = vsnprintf(buf, INT_MAX, fmt, args); + va_end(args); + + return len; +} + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushinteger(L, (lua_Integer)l); + return 1; +} + + +/* translate a relative string position: negative means back from end */ +static size_t posrelat (ptrdiff_t pos, size_t len) { + if (pos >= 0) return (size_t)pos; + else if (0u - (size_t)pos > len) return 0; + else return len - ((size_t)-pos) + 1; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t start = posrelat(luaL_checkinteger(L, 2), l); + size_t end = posrelat(luaL_optinteger(L, 3, -1), l); + if (start < 1) start = 1; + if (end > l) end = l; + if (start <= end) + lua_pushlstring(L, s + start - 1, end - start + 1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_reverse (lua_State *L) { + size_t l, i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i = 0; i < l; i++) + p[i] = s[l - i - 1]; + luaL_pushresultsize(&b, l); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i=0; i<l; i++) + p[i] = tolower(uchar(s[i])); + luaL_pushresultsize(&b, l); + return 1; +} + + +static int str_upper (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i=0; i<l; i++) + p[i] = toupper(uchar(s[i])); + luaL_pushresultsize(&b, l); + return 1; +} + + +/* reasonable limit to avoid arithmetic overflow */ +#define MAXSIZE ((~(size_t)0) >> 1) + +static int str_rep (lua_State *L) { + size_t l, lsep; + const char *s = luaL_checklstring(L, 1, &l); + int n = luaL_checkint(L, 2); + const char *sep = luaL_optlstring(L, 3, "", &lsep); + if (n <= 0) lua_pushliteral(L, ""); + else if (l + lsep < l || l + lsep >= MAXSIZE / n) /* may overflow? */ + return luaL_error(L, "resulting string too large"); + else { + size_t totallen = n * l + (n - 1) * lsep; + luaL_Buffer b; + char *p = luaL_buffinitsize(L, &b, totallen); + while (n-- > 1) { /* first n-1 copies (followed by separator) */ + memcpy(p, s, l * sizeof(char)); p += l; + if (lsep > 0) { /* avoid empty 'memcpy' (may be expensive) */ + memcpy(p, sep, lsep * sizeof(char)); p += lsep; + } + } + memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ + luaL_pushresultsize(&b, totallen); + } + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t posi = posrelat(luaL_optinteger(L, 2, 1), l); + size_t pose = posrelat(luaL_optinteger(L, 3, posi), l); + int n, i; + if (posi < 1) posi = 1; + if (pose > l) pose = l; + if (posi > pose) return 0; /* empty interval; return no values */ + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* (size_t -> int) overflow? */ + return luaL_error(L, "string slice too long"); + luaL_checkstack(L, n, "string slice too long"); + for (i=0; i<n; i++) + lua_pushinteger(L, uchar(s[posi+i-1])); + return n; +} + + +static int str_char (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_Buffer b; + char *p = luaL_buffinitsize(L, &b, n); + for (i=1; i<=n; i++) { + int c = luaL_checkint(L, i); + luaL_argcheck(L, uchar(c) == c, i, "value out of range"); + p[i - 1] = uchar(c); + } + luaL_pushresultsize(&b, n); + return 1; +} + + +static int writer (lua_State *L, const void* b, size_t size, void* B) { + (void)L; + luaL_addlstring((luaL_Buffer*) B, (const char *)b, size); + return 0; +} + + +static int str_dump (lua_State *L) { + luaL_Buffer b; + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 1); + luaL_buffinit(L,&b); + if (lua_dump(L, writer, &b) != 0) + return luaL_error(L, "unable to dump given function"); + luaL_pushresult(&b); + return 1; +} + + + +/* +** {====================================================== +** PATTERN MATCHING +** ======================================================= +*/ + + +#define CAP_UNFINISHED (-1) +#define CAP_POSITION (-2) + + +typedef struct MatchState { + int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ + const char *src_init; /* init of source string */ + const char *src_end; /* end ('\0') of source string */ + const char *p_end; /* end ('\0') of pattern */ + lua_State *L; + int level; /* total number of captures (finished or unfinished) */ + struct { + const char *init; + ptrdiff_t len; + } capture[LUA_MAXCAPTURES]; +} MatchState; + + +/* recursive function */ +static const char *match (MatchState *ms, const char *s, const char *p); + + +/* maximum recursion depth for 'match' */ +#if !defined(MAXCCALLS) +#define MAXCCALLS 200 +#endif + + +#define L_ESC '%' +#define SPECIALS "^$*+?.([%-" + + +static int check_capture (MatchState *ms, int l) { + l -= '1'; + if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) + return luaL_error(ms->L, "invalid capture index %%%d", l + 1); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *classend (MatchState *ms, const char *p) { + switch (*p++) { + case L_ESC: { + if (p == ms->p_end) + luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a `]' */ + if (p == ms->p_end) + luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); + if (*(p++) == L_ESC && p < ms->p_end) + p++; /* skip escapes (e.g. `%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'g' : res = isgraph(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; /* deprecated option */ + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the `^' */ + } + while (++p < ec) { + if (*p == L_ESC) { + p++; + if (match_class(c, uchar(*p))) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int singlematch (MatchState *ms, const char *s, const char *p, + const char *ep) { + if (s >= ms->src_end) + return 0; + else { + int c = uchar(*s); + switch (*p) { + case '.': return 1; /* matches any char */ + case L_ESC: return match_class(c, uchar(*(p+1))); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } + } +} + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (p >= ms->p_end - 1) + luaL_error(ms->L, "malformed pattern " + "(missing arguments to " LUA_QL("%%b") ")"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + ptrdiff_t i = 0; /* counts maximum expand for item */ + while (singlematch(ms, s + i, p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (singlematch(ms, s, p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + if (ms->matchdepth-- == 0) + luaL_error(ms->L, "pattern too complex"); + init: /* using goto's to optimize tail recursion */ + if (p != ms->p_end) { /* end of pattern? */ + switch (*p) { + case '(': { /* start capture */ + if (*(p + 1) == ')') /* position capture? */ + s = start_capture(ms, s, p + 2, CAP_POSITION); + else + s = start_capture(ms, s, p + 1, CAP_UNFINISHED); + break; + } + case ')': { /* end capture */ + s = end_capture(ms, s, p + 1); + break; + } + case '$': { + if ((p + 1) != ms->p_end) /* is the `$' the last char in pattern? */ + goto dflt; /* no; go to default */ + s = (s == ms->src_end) ? s : NULL; /* check end of string */ + break; + } + case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ + switch (*(p + 1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p + 2); + if (s != NULL) { + p += 4; goto init; /* return match(ms, s, p + 4); */ + } /* else fail (s == NULL) */ + break; + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + luaL_error(ms->L, "missing " LUA_QL("[") " after " + LUA_QL("%%f") " in pattern"); + ep = classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s - 1); + if (!matchbracketclass(uchar(previous), p, ep - 1) && + matchbracketclass(uchar(*s), p, ep - 1)) { + p = ep; goto init; /* return match(ms, s, ep); */ + } + s = NULL; /* match failed */ + break; + } + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': { /* capture results (%0-%9)? */ + s = match_capture(ms, s, uchar(*(p + 1))); + if (s != NULL) { + p += 2; goto init; /* return match(ms, s, p + 2) */ + } + break; + } + default: goto dflt; + } + break; + } + default: dflt: { /* pattern class plus optional suffix */ + const char *ep = classend(ms, p); /* points to optional suffix */ + /* does not match at least once? */ + if (!singlematch(ms, s, p, ep)) { + if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ + p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ + } + else /* '+' or no suffix */ + s = NULL; /* fail */ + } + else { /* matched once */ + switch (*ep) { /* handle optional suffix */ + case '?': { /* optional */ + const char *res; + if ((res = match(ms, s + 1, ep + 1)) != NULL) + s = res; + else { + p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ + } + break; + } + case '+': /* 1 or more repetitions */ + s++; /* 1 match already done */ + /* go through */ + case '*': /* 0 or more repetitions */ + s = max_expand(ms, s, p, ep); + break; + case '-': /* 0 or more repetitions (minimum) */ + s = min_expand(ms, s, p, ep); + break; + default: /* no suffix */ + s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ + } + } + break; + } + } + } + ms->matchdepth++; + return s; +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else { + const char *init; /* to search for a `*s2' inside `s1' */ + l2--; /* 1st char will be checked by `memchr' */ + l1 = l1-l2; /* `s2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct `l1' and `s1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +static void push_onecapture (MatchState *ms, int i, const char *s, + const char *e) { + if (i >= ms->level) { + if (i == 0) /* ms->level == 0, too */ + lua_pushlstring(ms->L, s, e - s); /* add whole match */ + else + luaL_error(ms->L, "invalid capture index"); + } + else { + ptrdiff_t l = ms->capture[i].len; + if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); + if (l == CAP_POSITION) + lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); + else + lua_pushlstring(ms->L, ms->capture[i].init, l); + } +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + int nlevels = (ms->level == 0 && s) ? 1 : ms->level; + luaL_checkstack(ms->L, nlevels, "too many captures"); + for (i = 0; i < nlevels; i++) + push_onecapture(ms, i, s, e); + return nlevels; /* number of strings pushed */ +} + + +/* check whether pattern has no special characters */ +static int nospecials (const char *p, size_t l) { + size_t upto = 0; + do { + if (strpbrk(p + upto, SPECIALS)) + return 0; /* pattern has a special character */ + upto += strlen(p + upto) + 1; /* may have more after \0 */ + } while (upto <= l); + return 1; /* no special chars found */ +} + + +static int str_find_aux (lua_State *L, int find) { + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + size_t init = posrelat(luaL_optinteger(L, 3, 1), ls); + if (init < 1) init = 1; + else if (init > ls + 1) { /* start after string's end? */ + lua_pushnil(L); /* cannot find anything */ + return 1; + } + /* explicit request or no special characters? */ + if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { + /* do a plain search */ + const char *s2 = lmemfind(s + init - 1, ls - init + 1, p, lp); + if (s2) { + lua_pushinteger(L, s2 - s + 1); + lua_pushinteger(L, s2 - s + lp); + return 2; + } + } + else { + MatchState ms; + const char *s1 = s + init - 1; + int anchor = (*p == '^'); + if (anchor) { + p++; lp--; /* skip anchor character */ + } + ms.L = L; + ms.matchdepth = MAXCCALLS; + ms.src_init = s; + ms.src_end = s + ls; + ms.p_end = p + lp; + do { + const char *res; + ms.level = 0; + lua_assert(ms.matchdepth == MAXCCALLS); + if ((res=match(&ms, s1, p)) != NULL) { + if (find) { + lua_pushinteger(L, s1 - s + 1); /* start */ + lua_pushinteger(L, res - s); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + else + return push_captures(&ms, s1, res); + } + } while (s1++ < ms.src_end && !anchor); + } + lua_pushnil(L); /* not found */ + return 1; +} + + +static int str_find (lua_State *L) { + return str_find_aux(L, 1); +} + + +static int str_match (lua_State *L) { + return str_find_aux(L, 0); +} + + +static int gmatch_aux (lua_State *L) { + MatchState ms; + size_t ls, lp; + const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); + const char *p = lua_tolstring(L, lua_upvalueindex(2), &lp); + const char *src; + ms.L = L; + ms.matchdepth = MAXCCALLS; + ms.src_init = s; + ms.src_end = s+ls; + ms.p_end = p + lp; + for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); + src <= ms.src_end; + src++) { + const char *e; + ms.level = 0; + lua_assert(ms.matchdepth == MAXCCALLS); + if ((e = match(&ms, src, p)) != NULL) { + lua_Integer newstart = e-s; + if (e == src) newstart++; /* empty match? go at least one position */ + lua_pushinteger(L, newstart); + lua_replace(L, lua_upvalueindex(3)); + return push_captures(&ms, src, e); + } + } + return 0; /* not found */ +} + + +static int str_gmatch (lua_State *L) { + luaL_checkstring(L, 1); + luaL_checkstring(L, 2); + lua_settop(L, 2); + lua_pushinteger(L, 0); + lua_pushcclosure(L, gmatch_aux, 3); + return 1; +} + + +static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + size_t l, i; + const char *news = lua_tolstring(ms->L, 3, &l); + for (i = 0; i < l; i++) { + if (news[i] != L_ESC) + luaL_addchar(b, news[i]); + else { + i++; /* skip ESC */ + if (!isdigit(uchar(news[i]))) { + if (news[i] != L_ESC) + luaL_error(ms->L, "invalid use of " LUA_QL("%c") + " in replacement string", L_ESC); + luaL_addchar(b, news[i]); + } + else if (news[i] == '0') + luaL_addlstring(b, s, e - s); + else { + push_onecapture(ms, news[i] - '1', s, e); + luaL_addvalue(b); /* add capture to accumulated result */ + } + } + } +} + + +static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e, int tr) { + lua_State *L = ms->L; + switch (tr) { + case LUA_TFUNCTION: { + int n; + lua_pushvalue(L, 3); + n = push_captures(ms, s, e); + lua_call(L, n, 1); + break; + } + case LUA_TTABLE: { + push_onecapture(ms, 0, s, e); + lua_gettable(L, 3); + break; + } + default: { /* LUA_TNUMBER or LUA_TSTRING */ + add_s(ms, b, s, e); + return; + } + } + if (!lua_toboolean(L, -1)) { /* nil or false? */ + lua_pop(L, 1); + lua_pushlstring(L, s, e - s); /* keep original text */ + } + else if (!lua_isstring(L, -1)) + luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); + luaL_addvalue(b); /* add result to accumulator */ +} + + +static int str_gsub (lua_State *L) { + size_t srcl, lp; + const char *src = luaL_checklstring(L, 1, &srcl); + const char *p = luaL_checklstring(L, 2, &lp); + int tr = lua_type(L, 3); + size_t max_s = luaL_optinteger(L, 4, srcl+1); + int anchor = (*p == '^'); + size_t n = 0; + MatchState ms; + luaL_Buffer b; + luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, + "string/function/table expected"); + luaL_buffinit(L, &b); + if (anchor) { + p++; lp--; /* skip anchor character */ + } + ms.L = L; + ms.matchdepth = MAXCCALLS; + ms.src_init = src; + ms.src_end = src+srcl; + ms.p_end = p + lp; + while (n < max_s) { + const char *e; + ms.level = 0; + lua_assert(ms.matchdepth == MAXCCALLS); + e = match(&ms, src, p); + if (e) { + n++; + add_value(&ms, &b, src, e, tr); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < ms.src_end) + luaL_addchar(&b, *src++); + else break; + if (anchor) break; + } + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); + lua_pushinteger(L, n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** STRING FORMAT +** ======================================================= +*/ + +/* +** LUA_INTFRMLEN is the length modifier for integer conversions in +** 'string.format'; LUA_INTFRM_T is the integer type corresponding to +** the previous length +*/ +#if !defined(LUA_INTFRMLEN) /* { */ +#if defined(LUA_USE_LONGLONG) + +#define LUA_INTFRMLEN "ll" +#define LUA_INTFRM_T long long + +#else + +#define LUA_INTFRMLEN "l" +#define LUA_INTFRM_T long + +#endif +#endif /* } */ + + +/* +** LUA_FLTFRMLEN is the length modifier for float conversions in +** 'string.format'; LUA_FLTFRM_T is the float type corresponding to +** the previous length +*/ +#if !defined(LUA_FLTFRMLEN) + +#define LUA_FLTFRMLEN "" +#define LUA_FLTFRM_T double + +#endif + + +/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ +#define MAX_ITEM 512 +/* valid flags in a format specification */ +#define FLAGS "-+ #0" +/* +** maximum size of each format specification (such as '%-099.99d') +** (+10 accounts for %99.99x plus margin of error) +*/ +#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) + + +static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + luaL_addchar(b, '"'); + while (l--) { + if (*s == '"' || *s == '\\' || *s == '\n') { + luaL_addchar(b, '\\'); + luaL_addchar(b, *s); + } + else if (*s == '\0' || iscntrl(uchar(*s))) { + char buff[10]; + if (!isdigit(uchar(*(s+1)))) + sprintf(buff, "\\%d", (int)uchar(*s)); + else + sprintf(buff, "\\%03d", (int)uchar(*s)); + luaL_addstring(b, buff); + } + else + luaL_addchar(b, *s); + s++; + } + luaL_addchar(b, '"'); +} + +static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { + const char *p = strfrmt; + while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ + if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) + luaL_error(L, "invalid format (repeated flags)"); + if (isdigit(uchar(*p))) p++; /* skip width */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + if (isdigit(uchar(*p))) p++; /* skip precision */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + } + if (isdigit(uchar(*p))) + luaL_error(L, "invalid format (width or precision too long)"); + *(form++) = '%'; + memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char)); + form += p - strfrmt + 1; + *form = '\0'; + return p; +} + + +/* +** add length modifier into formats +*/ +static void addlenmod (char *form, const char *lenmod) { + size_t l = strlen(form); + size_t lm = strlen(lenmod); + char spec = form[l - 1]; + strcpy(form + l - 1, lenmod); + form[l + lm - 1] = spec; + form[l + lm] = '\0'; +} + + +static int str_format (lua_State *L) { + int top = lua_gettop(L); + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != L_ESC) + luaL_addchar(&b, *strfrmt++); + else if (*++strfrmt == L_ESC) + luaL_addchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format (`%...') */ + char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ + int nb = 0; /* number of bytes in added item */ + if (++arg > top) + luaL_argerror(L, arg, "no value"); + strfrmt = scanformat(L, strfrmt, form); + switch (*strfrmt++) { + case 'c': { + nb = str_sprintf(buff, form, luaL_checkint(L, arg)); + break; + } + case 'd': case 'i': { + lua_Number n = luaL_checknumber(L, arg); + LUA_INTFRM_T ni = (LUA_INTFRM_T)n; + lua_Number diff = n - (lua_Number)ni; + luaL_argcheck(L, -1 < diff && diff < 1, arg, + "not a number in proper range"); + addlenmod(form, LUA_INTFRMLEN); + nb = str_sprintf(buff, form, ni); + break; + } + case 'o': case 'u': case 'x': case 'X': { + lua_Number n = luaL_checknumber(L, arg); + unsigned LUA_INTFRM_T ni = (unsigned LUA_INTFRM_T)n; + lua_Number diff = n - (lua_Number)ni; + luaL_argcheck(L, -1 < diff && diff < 1, arg, + "not a non-negative number in proper range"); + addlenmod(form, LUA_INTFRMLEN); + nb = str_sprintf(buff, form, ni); + break; + } + case 'e': case 'E': case 'f': +#if defined(LUA_USE_AFORMAT) + case 'a': case 'A': +#endif + case 'g': case 'G': { + addlenmod(form, LUA_FLTFRMLEN); + nb = str_sprintf(buff, form, (LUA_FLTFRM_T)luaL_checknumber(L, arg)); + break; + } + case 'q': { + addquoted(L, &b, arg); + break; + } + case 's': { + size_t l; + const char *s = luaL_tolstring(L, arg, &l); + if (!strchr(form, '.') && l >= 100) { + /* no precision and string is too long to be formatted; + keep original string */ + luaL_addvalue(&b); + break; + } + else { + nb = str_sprintf(buff, form, s); + lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ + break; + } + } + default: { /* also treat cases `pnLlh' */ + return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " + LUA_QL("format"), *(strfrmt - 1)); + } + } + luaL_addsize(&b, nb); + } + } + luaL_pushresult(&b); + return 1; +} + +/* }====================================================== */ + + +static const luaL_Reg strlib[] = { + {"byte", str_byte}, + {"char", str_char}, + {"dump", str_dump}, + {"find", str_find}, + {"format", str_format}, + {"gmatch", str_gmatch}, + {"gsub", str_gsub}, + {"len", str_len}, + {"lower", str_lower}, + {"match", str_match}, + {"rep", str_rep}, + {"reverse", str_reverse}, + {"sub", str_sub}, + {"upper", str_upper}, + {NULL, NULL} +}; + + +static void createmetatable (lua_State *L) { + lua_createtable(L, 0, 1); /* table to be metatable for strings */ + lua_pushliteral(L, ""); /* dummy string */ + lua_pushvalue(L, -2); /* copy table */ + lua_setmetatable(L, -2); /* set table as metatable for strings */ + lua_pop(L, 1); /* pop dummy string */ + lua_pushvalue(L, -2); /* get string library */ + lua_setfield(L, -2, "__index"); /* metatable.__index = string */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** Open string library +*/ +LUAMOD_API int luaopen_string (lua_State *L) { + luaL_newlib(L, strlib); + createmetatable(L); + return 1; +} + diff --git a/usr/src/uts/common/fs/zfs/lua/ltable.c b/usr/src/uts/common/fs/zfs/lua/ltable.c new file mode 100644 index 0000000000..a5279011c4 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ltable.c @@ -0,0 +1,588 @@ +/* +** $Id: ltable.c,v 2.72.1.1 2013/04/12 18:48:47 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + + +/* +** Implementation of tables (aka arrays, objects, or hash tables). +** Tables keep its elements in two parts: an array part and a hash part. +** Non-negative integer keys are all candidates to be kept in the array +** part. The actual size of the array is the largest `n' such that at +** least half the slots between 0 and n are in use. +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the `original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ + +#include <sys/zfs_context.h> + +#define ltable_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lvm.h" + + +/* +** max size of array part is 2^MAXBITS +*/ +#if LUAI_BITSINT >= 32 +#define MAXBITS 30 +#else +#define MAXBITS (LUAI_BITSINT-2) +#endif + +#define MAXASIZE (1 << MAXBITS) + + +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) + +#define hashstr(t,str) hashpow2(t, (str)->tsv.hash) +#define hashboolean(t,p) hashpow2(t, p) + + +/* +** for some types, it is better to avoid modulus by power of 2, as +** they tend to have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + +#define hashpointer(t,p) hashmod(t, IntPoint(p)) + + +#define dummynode (&dummynode_) + +#define isdummy(n) ((n) == dummynode) + +static const Node dummynode_ = { + {NILCONSTANT}, /* value */ + {{NILCONSTANT, NULL}} /* key */ +}; + + +/* +** hash for lua_Numbers +*/ +static Node *hashnum (const Table *t, lua_Number n) { + int i; + luai_hashnum(i, n); + if (i < 0) { + if (cast(unsigned int, i) == 0u - i) /* use unsigned to avoid overflows */ + i = 0; /* handle INT_MIN */ + i = -i; /* must be a positive value */ + } + return hashmod(t, i); +} + + + +/* +** returns the `main' position of an element in a table (that is, the index +** of its hash value) +*/ +static Node *mainposition (const Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNUMBER: + return hashnum(t, nvalue(key)); + case LUA_TLNGSTR: { + TString *s = rawtsvalue(key); + if (s->tsv.extra == 0) { /* no hash? */ + s->tsv.hash = luaS_hash(getstr(s), s->tsv.len, s->tsv.hash); + s->tsv.extra = 1; /* now it has its hash */ + } + return hashstr(t, rawtsvalue(key)); + } + case LUA_TSHRSTR: + return hashstr(t, rawtsvalue(key)); + case LUA_TBOOLEAN: + return hashboolean(t, bvalue(key)); + case LUA_TLIGHTUSERDATA: + return hashpointer(t, pvalue(key)); + case LUA_TLCF: + return hashpointer(t, fvalue(key)); + default: + return hashpointer(t, gcvalue(key)); + } +} + + +/* +** returns the index for `key' if `key' is an appropriate key to live in +** the array part of the table, -1 otherwise. +*/ +static int arrayindex (const TValue *key) { + if (ttisnumber(key)) { + lua_Number n = nvalue(key); + int k; + lua_number2int(k, n); + if (luai_numeq(cast_num(k), n)) + return k; + } + return -1; /* `key' did not match some condition */ +} + + +/* +** returns the index of a `key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signaled by -1. +*/ +static int findindex (lua_State *L, Table *t, StkId key) { + int i; + if (ttisnil(key)) return -1; /* first iteration */ + i = arrayindex(key); + if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ + return i-1; /* yes; that's the index (corrected to C) */ + else { + Node *n = mainposition(t, key); + for (;;) { /* check whether `key' is somewhere in the chain */ + /* key may be dead already, but it is ok to use it in `next' */ + if (luaV_rawequalobj(gkey(n), key) || + (ttisdeadkey(gkey(n)) && iscollectable(key) && + deadvalue(gkey(n)) == gcvalue(key))) { + i = cast_int(n - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return i + t->sizearray; + } + else n = gnext(n); + if (n == NULL) + luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ + } + } +} + + +int luaH_next (lua_State *L, Table *t, StkId key) { + int i = findindex(L, t, key); /* find original element */ + for (i++; i < t->sizearray; i++) { /* try first array part */ + if (!ttisnil(&t->array[i])) { /* a non-nil value? */ + setnvalue(key, cast_num(i+1)); + setobj2s(L, key+1, &t->array[i]); + return 1; + } + } + for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ + if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ + setobj2s(L, key, gkey(gnode(t, i))); + setobj2s(L, key+1, gval(gnode(t, i))); + return 1; + } + } + return 0; /* no more elements */ +} + + +/* +** {============================================================= +** Rehash +** ============================================================== +*/ + + +static int computesizes (int nums[], int *narray) { + int i; + int twotoi; /* 2^i */ + int a = 0; /* number of elements smaller than 2^i */ + int na = 0; /* number of elements to go to array part */ + int n = 0; /* optimal size for array part */ + for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { + if (nums[i] > 0) { + a += nums[i]; + if (a > twotoi/2) { /* more than half elements present? */ + n = twotoi; /* optimal size (till now) */ + na = a; /* all elements smaller than n will go to array part */ + } + } + if (a == *narray) break; /* all elements already counted */ + } + *narray = n; + lua_assert(*narray/2 <= na && na <= *narray); + return na; +} + + +static int countint (const TValue *key, int *nums) { + int k = arrayindex(key); + if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ + nums[luaO_ceillog2(k)]++; /* count as such */ + return 1; + } + else + return 0; +} + + +static int numusearray (const Table *t, int *nums) { + int lg; + int ttlg; /* 2^lg */ + int ause = 0; /* summation of `nums' */ + int i = 1; /* count to traverse all array keys */ + for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ + int lc = 0; /* counter */ + int lim = ttlg; + if (lim > t->sizearray) { + lim = t->sizearray; /* adjust upper limit */ + if (i > lim) + break; /* no more elements to count */ + } + /* count elements in range (2^(lg-1), 2^lg] */ + for (; i <= lim; i++) { + if (!ttisnil(&t->array[i-1])) + lc++; + } + nums[lg] += lc; + ause += lc; + } + return ause; +} + + +static int numusehash (const Table *t, int *nums, int *pnasize) { + int totaluse = 0; /* total number of elements */ + int ause = 0; /* summation of `nums' */ + int i = sizenode(t); + while (i--) { + Node *n = &t->node[i]; + if (!ttisnil(gval(n))) { + ause += countint(gkey(n), nums); + totaluse++; + } + } + *pnasize += ause; + return totaluse; +} + + +static void setarrayvector (lua_State *L, Table *t, int size) { + int i; + luaM_reallocvector(L, t->array, t->sizearray, size, TValue); + for (i=t->sizearray; i<size; i++) + setnilvalue(&t->array[i]); + t->sizearray = size; +} + + +static void setnodevector (lua_State *L, Table *t, int size) { + int lsize; + if (size == 0) { /* no elements to hash part? */ + t->node = cast(Node *, dummynode); /* use common `dummynode' */ + lsize = 0; + } + else { + int i; + lsize = luaO_ceillog2(size); + if (lsize > MAXBITS) + luaG_runerror(L, "table overflow"); + size = twoto(lsize); + t->node = luaM_newvector(L, size, Node); + for (i=0; i<size; i++) { + Node *n = gnode(t, i); + gnext(n) = NULL; + setnilvalue(gkey(n)); + setnilvalue(gval(n)); + } + } + t->lsizenode = cast_byte(lsize); + t->lastfree = gnode(t, size); /* all positions are free */ +} + + +void luaH_resize (lua_State *L, Table *t, int nasize, int nhsize) { + int i; + int oldasize = t->sizearray; + int oldhsize = t->lsizenode; + Node *nold = t->node; /* save old hash ... */ + if (nasize > oldasize) /* array part must grow? */ + setarrayvector(L, t, nasize); + /* create new hash part with appropriate size */ + setnodevector(L, t, nhsize); + if (nasize < oldasize) { /* array part must shrink? */ + t->sizearray = nasize; + /* re-insert elements from vanishing slice */ + for (i=nasize; i<oldasize; i++) { + if (!ttisnil(&t->array[i])) + luaH_setint(L, t, i + 1, &t->array[i]); + } + /* shrink array */ + luaM_reallocvector(L, t->array, oldasize, nasize, TValue); + } + /* re-insert elements from hash part */ + for (i = twoto(oldhsize) - 1; i >= 0; i--) { + Node *old = nold+i; + if (!ttisnil(gval(old))) { + /* doesn't need barrier/invalidate cache, as entry was + already present in the table */ + setobjt2t(L, luaH_set(L, t, gkey(old)), gval(old)); + } + } + if (!isdummy(nold)) + luaM_freearray(L, nold, cast(size_t, twoto(oldhsize))); /* free old array */ +} + + +void luaH_resizearray (lua_State *L, Table *t, int nasize) { + int nsize = isdummy(t->node) ? 0 : sizenode(t); + luaH_resize(L, t, nasize, nsize); +} + + +static void rehash (lua_State *L, Table *t, const TValue *ek) { + int nasize, na; + int nums[MAXBITS+1]; /* nums[i] = number of keys with 2^(i-1) < k <= 2^i */ + int i; + int totaluse; + for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ + nasize = numusearray(t, nums); /* count keys in array part */ + totaluse = nasize; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ + /* count extra key */ + nasize += countint(ek, nums); + totaluse++; + /* compute new size for array part */ + na = computesizes(nums, &nasize); + /* resize the table to new computed sizes */ + luaH_resize(L, t, nasize, totaluse - na); +} + + + +/* +** }============================================================= +*/ + + +Table *luaH_new (lua_State *L) { + Table *t = &luaC_newobj(L, LUA_TTABLE, sizeof(Table), NULL, 0)->h; + t->metatable = NULL; + t->flags = cast_byte(~0); + t->array = NULL; + t->sizearray = 0; + setnodevector(L, t, 0); + return t; +} + + +void luaH_free (lua_State *L, Table *t) { + if (!isdummy(t->node)) + luaM_freearray(L, t->node, cast(size_t, sizenode(t))); + luaM_freearray(L, t->array, t->sizearray); + luaM_free(L, t); +} + + +static Node *getfreepos (Table *t) { + while (t->lastfree > t->node) { + t->lastfree--; + if (ttisnil(gkey(t->lastfree))) + return t->lastfree; + } + return NULL; /* could not find a free place */ +} + + + +/* +** inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place and +** put new key in its main position; otherwise (colliding node is in its main +** position), new key goes to an empty position. +*/ +TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) { + Node *mp; + if (ttisnil(key)) luaG_runerror(L, "table index is nil"); + else if (ttisnumber(key) && luai_numisnan(L, nvalue(key))) + luaG_runerror(L, "table index is NaN"); + mp = mainposition(t, key); + if (!ttisnil(gval(mp)) || isdummy(mp)) { /* main position is taken? */ + Node *othern; + Node *n = getfreepos(t); /* get a free place */ + if (n == NULL) { /* cannot find a free place? */ + rehash(L, t, key); /* grow table */ + /* whatever called 'newkey' take care of TM cache and GC barrier */ + return luaH_set(L, t, key); /* insert key into grown table */ + } + lua_assert(!isdummy(n)); + othern = mainposition(t, gkey(mp)); + if (othern != mp) { /* is colliding node out of its main position? */ + /* yes; move colliding node into free position */ + while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ + gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ + *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + gnext(mp) = NULL; /* now `mp' is free */ + setnilvalue(gval(mp)); + } + else { /* colliding node is in its own main position */ + /* new node will go into free position */ + gnext(n) = gnext(mp); /* chain new position */ + gnext(mp) = n; + mp = n; + } + } + setobj2t(L, gkey(mp), key); + luaC_barrierback(L, obj2gco(t), key); + lua_assert(ttisnil(gval(mp))); + return gval(mp); +} + + +/* +** search function for integers +*/ +const TValue *luaH_getint (Table *t, int key) { + /* (1 <= key && key <= t->sizearray) */ + if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) + return &t->array[key-1]; + else { + lua_Number nk = cast_num(key); + Node *n = hashnum(t, nk); + do { /* check whether `key' is somewhere in the chain */ + if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } +} + + +/* +** search function for short strings +*/ +const TValue *luaH_getstr (Table *t, TString *key) { + Node *n = hashstr(t, key); + lua_assert(key->tsv.tt == LUA_TSHRSTR); + do { /* check whether `key' is somewhere in the chain */ + if (ttisshrstring(gkey(n)) && eqshrstr(rawtsvalue(gkey(n)), key)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; +} + + +/* +** main search function +*/ +const TValue *luaH_get (Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TSHRSTR: return luaH_getstr(t, rawtsvalue(key)); + case LUA_TNIL: return luaO_nilobject; + case LUA_TNUMBER: { + int k; + lua_Number n = nvalue(key); + lua_number2int(k, n); + if (luai_numeq(cast_num(k), n)) /* index is int? */ + return luaH_getint(t, k); /* use specialized version */ + /* else go through */ + } + default: { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (luaV_rawequalobj(gkey(n), key)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } + } +} + + +/* +** beware: when using this function you probably need to check a GC +** barrier and invalidate the TM cache. +*/ +TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { + const TValue *p = luaH_get(t, key); + if (p != luaO_nilobject) + return cast(TValue *, p); + else return luaH_newkey(L, t, key); +} + + +void luaH_setint (lua_State *L, Table *t, int key, TValue *value) { + const TValue *p = luaH_getint(t, key); + TValue *cell; + if (p != luaO_nilobject) + cell = cast(TValue *, p); + else { + TValue k; + setnvalue(&k, cast_num(key)); + cell = luaH_newkey(L, t, &k); + } + setobj2t(L, cell, value); +} + + +static int unbound_search (Table *t, unsigned int j) { + unsigned int i = j; /* i is zero or a present index */ + j++; + /* find `i' and `j' such that i is present and j is not */ + while (!ttisnil(luaH_getint(t, j))) { + i = j; + j *= 2; + if (j > cast(unsigned int, MAX_INT)) { /* overflow? */ + /* table was built with bad purposes: resort to linear search */ + i = 1; + while (!ttisnil(luaH_getint(t, i))) i++; + return i - 1; + } + } + /* now do a binary search between them */ + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(luaH_getint(t, m))) j = m; + else i = m; + } + return i; +} + + +/* +** Try to find a boundary in table `t'. A `boundary' is an integer index +** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). +*/ +int luaH_getn (Table *t) { + unsigned int j = t->sizearray; + if (j > 0 && ttisnil(&t->array[j - 1])) { + /* there is a boundary in the array part: (binary) search for it */ + unsigned int i = 0; + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(&t->array[m - 1])) j = m; + else i = m; + } + return i; + } + /* else must find a boundary in hash part */ + else if (isdummy(t->node)) /* hash part is empty? */ + return j; /* that is easy... */ + else return unbound_search(t, j); +} + + + +#if defined(LUA_DEBUG) + +Node *luaH_mainposition (const Table *t, const TValue *key) { + return mainposition(t, key); +} + +int luaH_isdummy (Node *n) { return isdummy(n); } + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/ltable.h b/usr/src/uts/common/fs/zfs/lua/ltable.h new file mode 100644 index 0000000000..d69449b2b8 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ltable.h @@ -0,0 +1,45 @@ +/* +** $Id: ltable.h,v 2.16.1.2 2013/08/30 15:49:41 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#ifndef ltable_h +#define ltable_h + +#include "lobject.h" + + +#define gnode(t,i) (&(t)->node[i]) +#define gkey(n) (&(n)->i_key.tvk) +#define gval(n) (&(n)->i_val) +#define gnext(n) ((n)->i_key.nk.next) + +#define invalidateTMcache(t) ((t)->flags = 0) + +/* returns the key, given the value of a table entry */ +#define keyfromval(v) \ + (gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val)))) + + +LUAI_FUNC const TValue *luaH_getint (Table *t, int key); +LUAI_FUNC void luaH_setint (lua_State *L, Table *t, int key, TValue *value); +LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); +LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); +LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key); +LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); +LUAI_FUNC Table *luaH_new (lua_State *L); +LUAI_FUNC void luaH_resize (lua_State *L, Table *t, int nasize, int nhsize); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); +LUAI_FUNC void luaH_free (lua_State *L, Table *t); +LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); +LUAI_FUNC int luaH_getn (Table *t); + + +#if defined(LUA_DEBUG) +LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); +LUAI_FUNC int luaH_isdummy (Node *n); +#endif + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/ltablib.c b/usr/src/uts/common/fs/zfs/lua/ltablib.c new file mode 100644 index 0000000000..ac9a662448 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ltablib.c @@ -0,0 +1,284 @@ +/* +** $Id: ltablib.c,v 1.65.1.2 2014/05/07 16:32:55 roberto Exp $ +** Library for Table Manipulation +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define ltablib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_len(L, n)) + + + +#if defined(LUA_COMPAT_MAXN) +static int maxn (lua_State *L) { + lua_Number max = 0; + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pop(L, 1); /* remove value */ + if (lua_type(L, -1) == LUA_TNUMBER) { + lua_Number v = lua_tonumber(L, -1); + if (v > max) max = v; + } + } + lua_pushnumber(L, max); + return 1; +} +#endif + + +static int tinsert (lua_State *L) { + int e = aux_getn(L, 1) + 1; /* first empty element */ + int pos; /* where to insert new element */ + switch (lua_gettop(L)) { + case 2: { /* called with only 2 arguments */ + pos = e; /* insert new element at the end */ + break; + } + case 3: { + int i; + pos = luaL_checkint(L, 2); /* 2nd argument is the position */ + luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds"); + for (i = e; i > pos; i--) { /* move up elements */ + lua_rawgeti(L, 1, i-1); + lua_rawseti(L, 1, i); /* t[i] = t[i-1] */ + } + break; + } + default: { + return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); + } + } + lua_rawseti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int tremove (lua_State *L) { + int size = aux_getn(L, 1); + int pos = luaL_optint(L, 2, size); + if (pos != size) /* validate 'pos' if given */ + luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds"); + lua_rawgeti(L, 1, pos); /* result = t[pos] */ + for ( ; pos < size; pos++) { + lua_rawgeti(L, 1, pos+1); + lua_rawseti(L, 1, pos); /* t[pos] = t[pos+1] */ + } + lua_pushnil(L); + lua_rawseti(L, 1, pos); /* t[pos] = nil */ + return 1; +} + + +static void addfield (lua_State *L, luaL_Buffer *b, int i) { + lua_rawgeti(L, 1, i); + if (!lua_isstring(L, -1)) + luaL_error(L, "invalid value (%s) at index %d in table for " + LUA_QL("concat"), luaL_typename(L, -1), i); + luaL_addvalue(b); +} + + +static int tconcat (lua_State *L) { + luaL_Buffer b; + size_t lsep; + int i, last; + const char *sep = luaL_optlstring(L, 2, "", &lsep); + luaL_checktype(L, 1, LUA_TTABLE); + i = luaL_optint(L, 3, 1); + last = luaL_opt(L, luaL_checkint, 4, luaL_len(L, 1)); + luaL_buffinit(L, &b); + for (; i < last; i++) { + addfield(L, &b, i); + luaL_addlstring(&b, sep, lsep); + } + if (i == last) /* add last value (if interval was not empty) */ + addfield(L, &b, i); + luaL_pushresult(&b); + return 1; +} + + +/* +** {====================================================== +** Pack/unpack +** ======================================================= +*/ + +static int pack (lua_State *L) { + int n = lua_gettop(L); /* number of elements to pack */ + lua_createtable(L, n, 1); /* create result table */ + lua_pushinteger(L, n); + lua_setfield(L, -2, "n"); /* t.n = number of elements */ + if (n > 0) { /* at least one element? */ + int i; + lua_pushvalue(L, 1); + lua_rawseti(L, -2, 1); /* insert first element */ + lua_replace(L, 1); /* move table into index 1 */ + for (i = n; i >= 2; i--) /* assign other elements */ + lua_rawseti(L, 1, i); + } + return 1; /* return table */ +} + + +static int unpack (lua_State *L) { + int i, e; + unsigned int n; + luaL_checktype(L, 1, LUA_TTABLE); + i = luaL_optint(L, 2, 1); + e = luaL_opt(L, luaL_checkint, 3, luaL_len(L, 1)); + if (i > e) return 0; /* empty range */ + n = (unsigned int)e - (unsigned int)i; /* number of elements minus 1 */ + if (n > (INT_MAX - 10) || !lua_checkstack(L, ++n)) + return luaL_error(L, "too many results to unpack"); + lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */ + while (i++ < e) /* push arg[i + 1...e] */ + lua_rawgeti(L, 1, i); + return n; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Quicksort +** (based on `Algorithms in MODULA-3', Robert Sedgewick; +** Addison-Wesley, 1993.) +** ======================================================= +*/ + + +static void set2 (lua_State *L, int i, int j) { + lua_rawseti(L, 1, i); + lua_rawseti(L, 1, j); +} + +static int sort_comp (lua_State *L, int a, int b) { + if (!lua_isnil(L, 2)) { /* function? */ + int res; + lua_pushvalue(L, 2); + lua_pushvalue(L, a-1); /* -1 to compensate function */ + lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */ + lua_call(L, 2, 1); + res = lua_toboolean(L, -1); + lua_pop(L, 1); + return res; + } + else /* a < b? */ + return lua_compare(L, a, b, LUA_OPLT); +} + +static void auxsort (lua_State *L, int l, int u) { + while (l < u) { /* for tail recursion */ + int i, j; + /* sort elements a[l], a[(l+u)/2] and a[u] */ + lua_rawgeti(L, 1, l); + lua_rawgeti(L, 1, u); + if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */ + set2(L, l, u); /* swap a[l] - a[u] */ + else + lua_pop(L, 2); + if (u-l == 1) break; /* only 2 elements */ + i = (l+u)/2; + lua_rawgeti(L, 1, i); + lua_rawgeti(L, 1, l); + if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */ + set2(L, i, l); + else { + lua_pop(L, 1); /* remove a[l] */ + lua_rawgeti(L, 1, u); + if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */ + set2(L, i, u); + else + lua_pop(L, 2); + } + if (u-l == 2) break; /* only 3 elements */ + lua_rawgeti(L, 1, i); /* Pivot */ + lua_pushvalue(L, -1); + lua_rawgeti(L, 1, u-1); + set2(L, i, u-1); + /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */ + i = l; j = u-1; + for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */ + /* repeat ++i until a[i] >= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>=u) luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j<=l) luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[j] */ + } + if (j<i) { + lua_pop(L, 3); /* pop pivot, a[i], a[j] */ + break; + } + set2(L, i, j); + } + lua_rawgeti(L, 1, u-1); + lua_rawgeti(L, 1, i); + set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */ + /* a[l..i-1] <= a[i] == P <= a[i+1..u] */ + /* adjust so that smaller half is in [j..i] and larger one in [l..u] */ + if (i-l < u-i) { + j=l; i=i-1; l=i+2; + } + else { + j=i+1; i=u; u=j-2; + } + auxsort(L, j, i); /* call recursively the smaller one */ + } /* repeat the routine for the larger one */ +} + +static int sort (lua_State *L) { + int n = aux_getn(L, 1); + luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */ + if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_settop(L, 2); /* make sure there is two arguments */ + auxsort(L, 1, n); + return 0; +} + +/* }====================================================== */ + + +static const luaL_Reg tab_funcs[] = { + {"concat", tconcat}, +#if defined(LUA_COMPAT_MAXN) + {"maxn", maxn}, +#endif + {"insert", tinsert}, + {"pack", pack}, + {"unpack", unpack}, + {"remove", tremove}, + {"sort", sort}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_table (lua_State *L) { + luaL_newlib(L, tab_funcs); +#if defined(LUA_COMPAT_UNPACK) + /* _G.unpack = table.unpack */ + lua_getfield(L, -1, "unpack"); + lua_setglobal(L, "unpack"); +#endif + return 1; +} + diff --git a/usr/src/uts/common/fs/zfs/lua/ltm.c b/usr/src/uts/common/fs/zfs/lua/ltm.c new file mode 100644 index 0000000000..671ff82505 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ltm.c @@ -0,0 +1,77 @@ +/* +** $Id: ltm.c,v 2.14.1.1 2013/04/12 18:48:47 roberto Exp $ +** Tag methods +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define ltm_c +#define LUA_CORE + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +static const char udatatypename[] = "userdata"; + +LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTAGS] = { + "no value", + "nil", "boolean", udatatypename, "number", + "string", "table", "function", udatatypename, "thread", + "proto", "upval" /* these last two cases are used for tests only */ +}; + + +void luaT_init (lua_State *L) { + static const char *const luaT_eventname[] = { /* ORDER TM */ + "__index", "__newindex", + "__gc", "__mode", "__len", "__eq", + "__add", "__sub", "__mul", "__div", "__mod", + "__pow", "__unm", "__lt", "__le", + "__concat", "__call" + }; + int i; + for (i=0; i<TM_N; i++) { + G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]); + luaS_fix(G(L)->tmname[i]); /* never collect these names */ + } +} + + +/* +** function to be used with macro "fasttm": optimized for absence of +** tag methods +*/ +const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { + const TValue *tm = luaH_getstr(events, ename); + lua_assert(event <= TM_EQ); + if (ttisnil(tm)) { /* no tag method? */ + events->flags |= cast_byte(1u<<event); /* cache this fact */ + return NULL; + } + else return tm; +} + + +const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) { + Table *mt; + switch (ttypenv(o)) { + case LUA_TTABLE: + mt = hvalue(o)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(o)->metatable; + break; + default: + mt = G(L)->mt[ttypenv(o)]; + } + return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); +} + diff --git a/usr/src/uts/common/fs/zfs/lua/ltm.h b/usr/src/uts/common/fs/zfs/lua/ltm.h new file mode 100644 index 0000000000..7f89c841f9 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/ltm.h @@ -0,0 +1,57 @@ +/* +** $Id: ltm.h,v 2.11.1.1 2013/04/12 18:48:47 roberto Exp $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#ifndef ltm_h +#define ltm_h + + +#include "lobject.h" + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER TM" +*/ +typedef enum { + TM_INDEX, + TM_NEWINDEX, + TM_GC, + TM_MODE, + TM_LEN, + TM_EQ, /* last tag method with `fast' access */ + TM_ADD, + TM_SUB, + TM_MUL, + TM_DIV, + TM_MOD, + TM_POW, + TM_UNM, + TM_LT, + TM_LE, + TM_CONCAT, + TM_CALL, + TM_N /* number of elements in the enum */ +} TMS; + + + +#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ + ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) + +#define fasttm(l,et,e) gfasttm(G(l), et, e) + +#define ttypename(x) luaT_typenames_[(x) + 1] +#define objtypename(x) ttypename(ttypenv(x)) + +LUAI_DDEC const char *const luaT_typenames_[LUA_TOTALTAGS]; + + +LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); +LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, + TMS event); +LUAI_FUNC void luaT_init (lua_State *L); + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lua.h b/usr/src/uts/common/fs/zfs/lua/lua.h new file mode 100644 index 0000000000..4610dad45e --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lua.h @@ -0,0 +1,443 @@ +/* +** $Id: lua.h,v 1.285.1.4 2015/02/21 14:04:50 roberto Exp $ +** Lua - A Scripting Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include <sys/zfs_context.h> + +#include "luaconf.h" + + +#define LUA_VERSION_MAJOR "5" +#define LUA_VERSION_MINOR "2" +#define LUA_VERSION_NUM 502 +#define LUA_VERSION_RELEASE "4" + +#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2015 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" + + +/* mark for precompiled code ('<esc>Lua') */ +#define LUA_SIGNATURE "\033Lua" + +/* option for multiple returns in 'lua_pcall' and 'lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** pseudo-indices +*/ +#define LUA_REGISTRYINDEX LUAI_FIRSTPSEUDOIDX +#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) + + +/* thread status */ +#define LUA_OK 0 +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRGCMM 5 +#define LUA_ERRERR 6 + + +typedef struct lua_State lua_State; + +typedef int (*lua_CFunction) (lua_State *L); + + +/* +** functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); + + +/* +** prototype for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + +#define LUA_NUMTAGS 9 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* predefined values in the registry */ +#define LUA_RIDX_MAINTHREAD 1 +#define LUA_RIDX_GLOBALS 2 +#define LUA_RIDX_LAST LUA_RIDX_GLOBALS + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + +/* unsigned integer type */ +typedef LUA_UNSIGNED lua_Unsigned; + + + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* +** RCS ident string +*/ +extern const char lua_ident[]; + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +LUA_API const lua_Number *(lua_version) (lua_State *L); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_absindex) (lua_State *L, int idx); +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_remove) (lua_State *L, int idx); +LUA_API void (lua_insert) (lua_State *L, int idx); +LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx); +LUA_API int (lua_checkstack) (lua_State *L, int sz); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); +LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); +LUA_API lua_Unsigned (lua_tounsignedx) (lua_State *L, int idx, int *isnum); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_rawlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** Comparison and arithmetic functions +*/ + +#define LUA_OPADD 0 /* ORDER TM */ +#define LUA_OPSUB 1 +#define LUA_OPMUL 2 +#define LUA_OPDIV 3 +#define LUA_OPMOD 4 +#define LUA_OPPOW 5 +#define LUA_OPUNM 6 + +LUA_API void (lua_arith) (lua_State *L, int op); + +#define LUA_OPEQ 0 +#define LUA_OPLT 1 +#define LUA_OPLE 2 + +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API void (lua_pushunsigned) (lua_State *L, lua_Unsigned n); +LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API void (lua_getglobal) (lua_State *L, const char *var); +LUA_API void (lua_gettable) (lua_State *L, int idx); +LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawget) (lua_State *L, int idx); +LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); +LUA_API void (lua_rawgetp) (lua_State *L, int idx, const void *p); +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API void (lua_getuservalue) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_setglobal) (lua_State *L, const char *var); +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API void (lua_setuservalue) (lua_State *L, int idx); + + +/* +** 'load' and 'call' functions (load and run Lua code) +*/ +LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, int ctx, + lua_CFunction k); +#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) + +LUA_API int (lua_getctx) (lua_State *L, int *ctx); + +LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, + int ctx, lua_CFunction k); +#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) + +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname, + const char *mode); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yieldk) (lua_State *L, int nresults, int ctx, + lua_CFunction k); +#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) +LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg); +LUA_API int (lua_status) (lua_State *L); + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 +#define LUA_GCSETMAJORINC 8 +#define LUA_GCISRUNNING 9 +#define LUA_GCGEN 10 +#define LUA_GCINC 11 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); +LUA_API void (lua_len) (lua_State *L, int idx); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_tonumber(L,i) lua_tonumberx(L,i,NULL) +#define lua_tointeger(L,i) lua_tointegerx(L,i,NULL) +#define lua_tounsigned(L,i) lua_tounsignedx(L,i,NULL) + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) \ + lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) + +#define lua_pushglobaltable(L) \ + lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILCALL 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debugger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); +LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n); +LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n); + +LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n); +LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1, + int fidx2, int n2); + +LUA_API int (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook (lua_gethook) (lua_State *L); +LUA_API int (lua_gethookmask) (lua_State *L); +LUA_API int (lua_gethookcount) (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ + const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + unsigned char nups; /* (u) number of upvalues */ + unsigned char nparams;/* (u) number of parameters */ + char isvararg; /* (u) */ + char istailcall; /* (t) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + struct CallInfo *i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2015 Lua.org, PUC-Rio. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/luaconf.h b/usr/src/uts/common/fs/zfs/lua/luaconf.h new file mode 100644 index 0000000000..f73aa0565c --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/luaconf.h @@ -0,0 +1,552 @@ +/* +** $Id: luaconf.h,v 1.176.1.2 2013/11/21 17:26:16 roberto Exp $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef lconfig_h +#define lconfig_h + +#include <sys/zfs_context.h> + +#include <sys/int_fmtio.h> + +extern ssize_t lcompat_sprintf(char *, const char *, ...); +extern int64_t lcompat_strtoll(const char *, char **); +extern int64_t lcompat_pow(int64_t, int64_t); + +/* +** ================================================================== +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +@@ LUA_ANSI controls the use of non-ansi features. +** CHANGE it (define it) if you want Lua to avoid the use of any +** non-ansi feature or library. +*/ +#if !defined(LUA_ANSI) && defined(__STRICT_ANSI__) +#define LUA_ANSI +#endif + + +#if !defined(LUA_ANSI) && defined(_WIN32) && !defined(_WIN32_WCE) +#define LUA_WIN /* enable goodies for regular Windows platforms */ +#endif + +#if defined(LUA_WIN) +#define LUA_DL_DLL +#define LUA_USE_AFORMAT /* assume 'printf' handles 'aA' specifiers */ +#endif + + + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_USE_READLINE /* needs some extra libraries */ +#define LUA_USE_STRTODHEX /* assume 'strtod' handles hex formats */ +#define LUA_USE_AFORMAT /* assume 'printf' handles 'aA' specifiers */ +#define LUA_USE_LONGLONG /* assume support for long long */ +#endif + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* does not need -ldl */ +#define LUA_USE_READLINE /* needs an extra library: -lreadline */ +#define LUA_USE_STRTODHEX /* assume 'strtod' handles hex formats */ +#define LUA_USE_AFORMAT /* assume 'printf' handles 'aA' specifiers */ +#define LUA_USE_LONGLONG /* assume support for long long */ +#endif + + + +/* +@@ LUA_USE_POSIX includes all functionality listed as X/Open System +@* Interfaces Extension (XSI). +** CHANGE it (define it) if your system is XSI compatible. +*/ +#if defined(LUA_USE_POSIX) +#define LUA_USE_MKSTEMP +#define LUA_USE_ISATTY +#define LUA_USE_POPEN +#define LUA_USE_ULONGJMP +#define LUA_USE_GMTIME_R +#endif + + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +@* Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +@* C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ +#if defined(_WIN32) /* { */ +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" ".\\?.lua" +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll;" ".\\?.dll" + +#else /* }{ */ + +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "/" +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR +#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" "./?.lua" +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" +#endif /* } */ + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + + +/* +@@ LUA_ENV is the name of the variable that holds the current +@@ environment, used to access global names. +** CHANGE it if you do not like this name. +*/ +#define LUA_ENV "_ENV" + + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all auxiliary library functions. +@@ LUAMOD_API is a mark for all standard library opening functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) /* { */ + +#if defined(LUA_CORE) || defined(LUA_LIB) /* { */ +#define LUA_API __declspec(dllexport) +#else /* }{ */ +#define LUA_API __declspec(dllimport) +#endif /* } */ + +#else /* }{ */ + +#define LUA_API extern + +#endif /* } */ + + +/* more often than not the libs go together with the core */ +#define LUALIB_API LUA_API +#define LUAMOD_API LUALIB_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +@* exported to outside modules. +@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables +@* that are not to be exported to outside modules (LUAI_DDEF for +@* definitions and LUAI_DDEC for declarations). +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. Not all elf targets support +** this attribute. Unfortunately, gcc does not offer a way to check +** whether the target offers that support, and those without support +** give a warning about it. To avoid these warnings, change to the +** default definition. +*/ +#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) /* { */ +#define LUAI_FUNC __attribute__((visibility("hidden"))) extern +#define LUAI_DDEC LUAI_FUNC +#define LUAI_DDEF /* empty */ + +#else /* }{ */ +#define LUAI_FUNC extern +#define LUAI_DDEC extern +#define LUAI_DDEF /* empty */ +#endif /* } */ + + + +/* +@@ LUA_QL describes how error messages quote program elements. +** CHANGE it if you want a different appearance. +*/ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@* of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +@@ luai_writestringerror defines how to print error messages. +** (A format string with one argument is enough for Lua...) +*/ +#ifdef _KERNEL +#define luai_writestringerror(s,p) \ + (zfs_dbgmsg((s), (p))) +#else +#define luai_writestringerror(s,p) \ + (fprintf(stderr, (s), (p)), fflush(stderr)) +#endif + + +/* +@@ LUAI_MAXSHORTLEN is the maximum length for short strings, that is, +** strings that are internalized. (Cannot be smaller than reserved words +** or tags for metamethods, as these strings must be internalized; +** #("function") = 8, #("__newindex") = 10.) +*/ +#define LUAI_MAXSHORTLEN 40 + + + +/* +** {================================================================== +** Compatibility with previous versions +** =================================================================== +*/ + +/* +@@ LUA_COMPAT_ALL controls all compatibility options. +** You can define it to get all options, or change specific options +** to fit your specific needs. +*/ +#if defined(LUA_COMPAT_ALL) /* { */ + +/* +@@ LUA_COMPAT_UNPACK controls the presence of global 'unpack'. +** You can replace it with 'table.unpack'. +*/ +#define LUA_COMPAT_UNPACK + +/* +@@ LUA_COMPAT_LOADERS controls the presence of table 'package.loaders'. +** You can replace it with 'package.searchers'. +*/ +#define LUA_COMPAT_LOADERS + +/* +@@ macro 'lua_cpcall' emulates deprecated function lua_cpcall. +** You can call your C function directly (with light C functions). +*/ +#define lua_cpcall(L,f,u) \ + (lua_pushcfunction(L, (f)), \ + lua_pushlightuserdata(L,(u)), \ + lua_pcall(L,1,0,0)) + + +/* +@@ LUA_COMPAT_LOG10 defines the function 'log10' in the math library. +** You can rewrite 'log10(x)' as 'log(x, 10)'. +*/ +#define LUA_COMPAT_LOG10 + +/* +@@ LUA_COMPAT_LOADSTRING defines the function 'loadstring' in the base +** library. You can rewrite 'loadstring(s)' as 'load(s)'. +*/ +#define LUA_COMPAT_LOADSTRING + +/* +@@ LUA_COMPAT_MAXN defines the function 'maxn' in the table library. +*/ +#define LUA_COMPAT_MAXN + +/* +@@ The following macros supply trivial compatibility for some +** changes in the API. The macros themselves document how to +** change your code to avoid using them. +*/ +#define lua_strlen(L,i) lua_rawlen(L, (i)) + +#define lua_objlen(L,i) lua_rawlen(L, (i)) + +#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) +#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) + +/* +@@ LUA_COMPAT_MODULE controls compatibility with previous +** module functions 'module' (Lua) and 'luaL_register' (C). +*/ +#define LUA_COMPAT_MODULE + +#endif /* } */ + +/* }================================================================== */ + + + +/* +@@ LUAI_BITSINT defines the number of bits in an int. +** CHANGE here if Lua cannot automatically detect the number of bits of +** your machine. Probably you do not need to change this. +*/ +/* avoid overflows in comparison */ +#if INT_MAX-20 < 32760 /* { */ +#define LUAI_BITSINT 16 +#elif INT_MAX > 2147483640L /* }{ */ +/* int has at least 32 bits */ +#define LUAI_BITSINT 32 +#else /* }{ */ +#error "you must define LUA_BITSINT with number of bits in an integer" +#endif /* } */ + + +/* +@@ LUA_INT32 is a signed integer with exactly 32 bits. +@@ LUAI_UMEM is an unsigned integer big enough to count the total +@* memory used by Lua. +@@ LUAI_MEM is a signed integer big enough to count the total memory +@* used by Lua. +** CHANGE here if for some weird reason the default definitions are not +** good enough for your machine. Probably you do not need to change +** this. +*/ +#if LUAI_BITSINT >= 32 /* { */ +#define LUA_INT32 int +#define LUAI_UMEM size_t +#define LUAI_MEM ptrdiff_t +#else /* }{ */ +/* 16-bit ints */ +#define LUA_INT32 long +#define LUAI_UMEM unsigned long +#define LUAI_MEM long +#endif /* } */ + + +/* +@@ LUAI_MAXSTACK limits the size of the Lua stack. +** CHANGE it if you need a different limit. This limit is arbitrary; +** its only purpose is to stop Lua from consuming unlimited stack +** space (and to reserve some numbers for pseudo-indices). +*/ +#if LUAI_BITSINT >= 32 +#define LUAI_MAXSTACK 1000000 +#else +#define LUAI_MAXSTACK 15000 +#endif + +/* reserve some space for error handling */ +#define LUAI_FIRSTPSEUDOIDX (-LUAI_MAXSTACK - 1000) + + + + +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +** CHANGE it if it uses too much C-stack space. +*/ +#define LUAL_BUFFERSIZE 1024 + + + + +/* +** {================================================================== +@@ LUA_NUMBER is the type of numbers in Lua. +** CHANGE the following definitions only if you want to build Lua +** with a number type different from double. You may also need to +** change lua_number2int & lua_number2integer. +** =================================================================== +*/ + +#define LUA_NUMBER int64_t + +/* +@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' +@* over a number. +*/ +#define LUAI_UACNUMBER int64_t + + +/* +@@ LUA_NUMBER_SCAN is the format for reading numbers. +@@ LUA_NUMBER_FMT is the format for writing numbers. +@@ lua_number2str converts a number to a string. +@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. +*/ +#define LUA_NUMBER_FMT "%" PRId64 +#define lua_number2str(s,n) lcompat_sprintf((s), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ + + +/* +@@ l_mathop allows the addition of an 'l' or 'f' to all math operations +*/ +#define l_mathop(x) (x ## l) + + +/* +@@ lua_str2number converts a decimal numeric string to a number. +@@ lua_strx2number converts an hexadecimal numeric string to a number. +** In C99, 'strtod' does both conversions. C89, however, has no function +** to convert floating hexadecimal strings to numbers. For these +** systems, you can leave 'lua_strx2number' undefined and Lua will +** provide its own implementation. +*/ +#define lua_str2number(s,p) lcompat_strtoll((s), (p)) + +#if defined(LUA_USE_STRTODHEX) +#define lua_strx2number(s,p) lcompat_strtoll((s), (p)) +#endif + + +/* +@@ The luai_num* macros define the primitive operations over numbers. +*/ + +/* the following operations need the math library */ +#if defined(lobject_c) || defined(lvm_c) +#define luai_nummod(L,a,b) ((a) % (b)) +#define luai_numpow(L,a,b) (lcompat_pow((a),(b))) +#endif + +/* these are quite standard operations */ +#if defined(LUA_CORE) +#define luai_numadd(L,a,b) ((a)+(b)) +#define luai_numsub(L,a,b) ((a)-(b)) +#define luai_nummul(L,a,b) ((a)*(b)) +#define luai_numdiv(L,a,b) ((a)/(b)) +#define luai_numunm(L,a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(L,a,b) ((a)<(b)) +#define luai_numle(L,a,b) ((a)<=(b)) +#define luai_numisnan(L,a) (!luai_numeq((a), (a))) +#endif + + + +/* +@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. +** CHANGE that if ptrdiff_t is not adequate on your machine. (On most +** machines, ptrdiff_t gives a good choice between int or long.) +*/ +#define LUA_INTEGER ptrdiff_t + +/* +@@ LUA_UNSIGNED is the integral type used by lua_pushunsigned/lua_tounsigned. +** It must have at least 32 bits. +*/ +#define LUA_UNSIGNED uint64_t + + + +/* +** Some tricks with doubles +*/ + +#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */ +/* +** The next definitions activate some tricks to speed up the +** conversion from doubles to integer types, mainly to LUA_UNSIGNED. +** +@@ LUA_MSASMTRICK uses Microsoft assembler to avoid clashes with a +** DirectX idiosyncrasy. +** +@@ LUA_IEEE754TRICK uses a trick that should work on any machine +** using IEEE754 with a 32-bit integer type. +** +@@ LUA_IEEELL extends the trick to LUA_INTEGER; should only be +** defined when LUA_INTEGER is a 32-bit integer. +** +@@ LUA_IEEEENDIAN is the endianness of doubles in your machine +** (0 for little endian, 1 for big endian); if not defined, Lua will +** check it dynamically for LUA_IEEE754TRICK (but not for LUA_NANTRICK). +** +@@ LUA_NANTRICK controls the use of a trick to pack all types into +** a single double value, using NaN values to represent non-number +** values. The trick only works on 32-bit machines (ints and pointers +** are 32-bit values) with numbers represented as IEEE 754-2008 doubles +** with conventional endianess (12345678 or 87654321), in CPUs that do +** not produce signaling NaN values (all NaNs are quiet). +*/ + +/* Microsoft compiler on a Pentium (32 bit) ? */ +#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86) /* { */ + +#define LUA_MSASMTRICK +#define LUA_IEEEENDIAN 0 +#define LUA_NANTRICK + + +/* pentium 32 bits? */ +#elif defined(__i386__) || defined(__i386) || defined(__X86__) /* }{ */ + +#define LUA_IEEE754TRICK +#define LUA_IEEELL +#define LUA_IEEEENDIAN 0 +#define LUA_NANTRICK + +/* pentium 64 bits? */ +#elif defined(__x86_64) /* }{ */ + +#define LUA_IEEE754TRICK +#define LUA_IEEEENDIAN 0 + +#elif defined(__POWERPC__) || defined(__ppc__) /* }{ */ + +#define LUA_IEEE754TRICK +#define LUA_IEEEENDIAN 1 + +#else /* }{ */ + +/* assume IEEE754 and a 32-bit integer type */ +#define LUA_IEEE754TRICK + +#endif /* } */ + +#endif /* } */ + +/* }================================================================== */ + + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + +#define getlocaledecpoint() ('.') + +#define abs(x) (((x) < 0) ? -(x) : (x)) + +#if !defined(UCHAR_MAX) +#define UCHAR_MAX (0xff) +#endif + +#endif + diff --git a/usr/src/uts/common/fs/zfs/lua/lualib.h b/usr/src/uts/common/fs/zfs/lua/lualib.h new file mode 100644 index 0000000000..da82005c9d --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lualib.h @@ -0,0 +1,55 @@ +/* +** $Id: lualib.h,v 1.43.1.1 2013/04/12 18:48:47 roberto Exp $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + + +LUAMOD_API int (luaopen_base) (lua_State *L); + +#define LUA_COLIBNAME "coroutine" +LUAMOD_API int (luaopen_coroutine) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUAMOD_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUAMOD_API int (luaopen_io) (lua_State *L); + +#define LUA_OSLIBNAME "os" +LUAMOD_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUAMOD_API int (luaopen_string) (lua_State *L); + +#define LUA_BITLIBNAME "bit32" +LUAMOD_API int (luaopen_bit32) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +LUAMOD_API int (luaopen_math) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +LUAMOD_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUAMOD_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + + +#if !defined(lua_assert) +#define lua_assert(x) ((void)0) +#endif + + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lundump.c b/usr/src/uts/common/fs/zfs/lua/lundump.c new file mode 100644 index 0000000000..4d53749a02 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lundump.c @@ -0,0 +1,258 @@ +/* +** $Id: lundump.c,v 2.22.1.1 2013/04/12 18:48:47 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#include <sys/zfs_context.h> + +#define lundump_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstring.h" +#include "lundump.h" +#include "lzio.h" + +typedef struct { + lua_State* L; + ZIO* Z; + Mbuffer* b; + const char* name; +} LoadState; + +static l_noret error(LoadState* S, const char* why) +{ + luaO_pushfstring(S->L,"%s: %s precompiled chunk",S->name,why); + luaD_throw(S->L,LUA_ERRSYNTAX); +} + +#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) +#define LoadByte(S) (lu_byte)LoadChar(S) +#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) +#define LoadVector(S,b,n,size) LoadMem(S,b,n,size) + +#if !defined(luai_verifycode) +#define luai_verifycode(L,b,f) /* empty */ +#endif + +static void LoadBlock(LoadState* S, void* b, size_t size) +{ + if (luaZ_read(S->Z,b,size)!=0) error(S,"truncated"); +} + +static int LoadChar(LoadState* S) +{ + char x; + LoadVar(S,x); + return x; +} + +static int LoadInt(LoadState* S) +{ + int x; + LoadVar(S,x); + if (x<0) error(S,"corrupted"); + return x; +} + +static lua_Number LoadNumber(LoadState* S) +{ + lua_Number x; + LoadVar(S,x); + return x; +} + +static TString* LoadString(LoadState* S) +{ + size_t size; + LoadVar(S,size); + if (size==0) + return NULL; + else + { + char* s=luaZ_openspace(S->L,S->b,size); + LoadBlock(S,s,size*sizeof(char)); + return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */ + } +} + +static void LoadCode(LoadState* S, Proto* f) +{ + int n=LoadInt(S); + f->code=luaM_newvector(S->L,n,Instruction); + f->sizecode=n; + LoadVector(S,f->code,n,sizeof(Instruction)); +} + +static void LoadFunction(LoadState* S, Proto* f); + +static void LoadConstants(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + f->k=luaM_newvector(S->L,n,TValue); + f->sizek=n; + for (i=0; i<n; i++) setnilvalue(&f->k[i]); + for (i=0; i<n; i++) + { + TValue* o=&f->k[i]; + int t=LoadChar(S); + switch (t) + { + case LUA_TNIL: + setnilvalue(o); + break; + case LUA_TBOOLEAN: + setbvalue(o,LoadChar(S)); + break; + case LUA_TNUMBER: + setnvalue(o,LoadNumber(S)); + break; + case LUA_TSTRING: + setsvalue2n(S->L,o,LoadString(S)); + break; + default: lua_assert(0); + } + } + n=LoadInt(S); + f->p=luaM_newvector(S->L,n,Proto*); + f->sizep=n; + for (i=0; i<n; i++) f->p[i]=NULL; + for (i=0; i<n; i++) + { + f->p[i]=luaF_newproto(S->L); + LoadFunction(S,f->p[i]); + } +} + +static void LoadUpvalues(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + f->upvalues=luaM_newvector(S->L,n,Upvaldesc); + f->sizeupvalues=n; + for (i=0; i<n; i++) f->upvalues[i].name=NULL; + for (i=0; i<n; i++) + { + f->upvalues[i].instack=LoadByte(S); + f->upvalues[i].idx=LoadByte(S); + } +} + +static void LoadDebug(LoadState* S, Proto* f) +{ + int i,n; + f->source=LoadString(S); + n=LoadInt(S); + f->lineinfo=luaM_newvector(S->L,n,int); + f->sizelineinfo=n; + LoadVector(S,f->lineinfo,n,sizeof(int)); + n=LoadInt(S); + f->locvars=luaM_newvector(S->L,n,LocVar); + f->sizelocvars=n; + for (i=0; i<n; i++) f->locvars[i].varname=NULL; + for (i=0; i<n; i++) + { + f->locvars[i].varname=LoadString(S); + f->locvars[i].startpc=LoadInt(S); + f->locvars[i].endpc=LoadInt(S); + } + n=LoadInt(S); + for (i=0; i<n; i++) f->upvalues[i].name=LoadString(S); +} + +static void LoadFunction(LoadState* S, Proto* f) +{ + f->linedefined=LoadInt(S); + f->lastlinedefined=LoadInt(S); + f->numparams=LoadByte(S); + f->is_vararg=LoadByte(S); + f->maxstacksize=LoadByte(S); + LoadCode(S,f); + LoadConstants(S,f); + LoadUpvalues(S,f); + LoadDebug(S,f); +} + +/* the code below must be consistent with the code in luaU_header */ +#define N0 LUAC_HEADERSIZE +#define N1 (sizeof(LUA_SIGNATURE)-sizeof(char)) +#define N2 N1+2 +#define N3 N2+6 + +static void LoadHeader(LoadState* S) +{ + lu_byte h[LUAC_HEADERSIZE]; + lu_byte s[LUAC_HEADERSIZE]; + luaU_header(h); + memcpy(s,h,sizeof(char)); /* first char already read */ + LoadBlock(S,s+sizeof(char),LUAC_HEADERSIZE-sizeof(char)); + if (memcmp(h,s,N0)==0) return; + if (memcmp(h,s,N1)!=0) error(S,"not a"); + if (memcmp(h,s,N2)!=0) error(S,"version mismatch in"); + if (memcmp(h,s,N3)!=0) error(S,"incompatible"); else error(S,"corrupted"); +} + +/* +** load precompiled chunk +*/ +Closure* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) +{ + LoadState S; + Closure* cl; + if (*name=='@' || *name=='=') + S.name=name+1; + else if (*name==LUA_SIGNATURE[0]) + S.name="binary string"; + else + S.name=name; + S.L=L; + S.Z=Z; + S.b=buff; + LoadHeader(&S); + cl=luaF_newLclosure(L,1); + setclLvalue(L,L->top,cl); incr_top(L); + cl->l.p=luaF_newproto(L); + LoadFunction(&S,cl->l.p); + if (cl->l.p->sizeupvalues != 1) + { + Proto* p=cl->l.p; + cl=luaF_newLclosure(L,cl->l.p->sizeupvalues); + cl->l.p=p; + setclLvalue(L,L->top-1,cl); + } + luai_verifycode(L,buff,cl->l.p); + return cl; +} + +#define MYINT(s) (s[0]-'0') +#define VERSION MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR) +#define FORMAT 0 /* this is the official format */ + +/* +* make header for precompiled chunks +* if you change the code below be sure to update LoadHeader and FORMAT above +* and LUAC_HEADERSIZE in lundump.h +*/ +void luaU_header (lu_byte* h) +{ + int x=1; + memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-sizeof(char)); + h+=sizeof(LUA_SIGNATURE)-sizeof(char); + *h++=cast_byte(VERSION); + *h++=cast_byte(FORMAT); + *h++=cast_byte(*(char*)&x); /* endianness */ + *h++=cast_byte(sizeof(int)); + *h++=cast_byte(sizeof(size_t)); + *h++=cast_byte(sizeof(Instruction)); + *h++=cast_byte(sizeof(lua_Number)); + *h++=cast_byte(((lua_Number)0.5)==0); /* is lua_Number integral? */ + memcpy(h,LUAC_TAIL,sizeof(LUAC_TAIL)-sizeof(char)); +} diff --git a/usr/src/uts/common/fs/zfs/lua/lundump.h b/usr/src/uts/common/fs/zfs/lua/lundump.h new file mode 100644 index 0000000000..5255db259d --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lundump.h @@ -0,0 +1,28 @@ +/* +** $Id: lundump.h,v 1.39.1.1 2013/04/12 18:48:47 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef lundump_h +#define lundump_h + +#include "lobject.h" +#include "lzio.h" + +/* load one chunk; from lundump.c */ +LUAI_FUNC Closure* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); + +/* make header; from lundump.c */ +LUAI_FUNC void luaU_header (lu_byte* h); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); + +/* data to catch conversion errors */ +#define LUAC_TAIL "\x19\x93\r\n\x1a\n" + +/* size in bytes of header of binary files */ +#define LUAC_HEADERSIZE (sizeof(LUA_SIGNATURE)-sizeof(char)+2+6+sizeof(LUAC_TAIL)-sizeof(char)) + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lvm.c b/usr/src/uts/common/fs/zfs/lua/lvm.c new file mode 100644 index 0000000000..a06e36e5ce --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lvm.c @@ -0,0 +1,930 @@ +/* +** $Id: lvm.c,v 2.155.1.1 2013/04/12 18:48:47 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define strcoll(l,r) (strcmp((l),(r))) + +#define lvm_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +/* limit for table tag-method chains (to avoid loops) */ +#define MAXTAGLOOP 100 + + +const TValue *luaV_tonumber (const TValue *obj, TValue *n) { + lua_Number num; + if (ttisnumber(obj)) return obj; + if (ttisstring(obj) && luaO_str2d(svalue(obj), tsvalue(obj)->len, &num)) { + setnvalue(n, num); + return n; + } + else + return NULL; +} + + +int luaV_tostring (lua_State *L, StkId obj) { + if (!ttisnumber(obj)) + return 0; + else { + char s[LUAI_MAXNUMBER2STR]; + lua_Number n = nvalue(obj); + int l = lua_number2str(s, n); + setsvalue2s(L, obj, luaS_newlstr(L, s, l)); + return 1; + } +} + + +static void traceexec (lua_State *L) { + CallInfo *ci = L->ci; + lu_byte mask = L->hookmask; + int counthook = ((mask & LUA_MASKCOUNT) && L->hookcount == 0); + if (counthook) + resethookcount(L); /* reset count */ + if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ + ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ + return; /* do not call hook again (VM yielded, so it did not move) */ + } + if (counthook) + luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */ + if (mask & LUA_MASKLINE) { + Proto *p = ci_func(ci)->p; + int npc = pcRel(ci->u.l.savedpc, p); + int newline = getfuncline(p, npc); + if (npc == 0 || /* call linehook when enter a new function, */ + ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */ + newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */ + luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */ + } + L->oldpc = ci->u.l.savedpc; + if (L->status == LUA_YIELD) { /* did hook yield? */ + if (counthook) + L->hookcount = 1; /* undo decrement to zero */ + ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ + ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ + ci->func = L->top - 1; /* protect stack below results */ + luaD_throw(L, LUA_YIELD); + } +} + + +static void callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, TValue *p3, int hasres) { + ptrdiff_t result = savestack(L, p3); + setobj2s(L, L->top++, f); /* push function */ + setobj2s(L, L->top++, p1); /* 1st argument */ + setobj2s(L, L->top++, p2); /* 2nd argument */ + if (!hasres) /* no result? 'p3' is third argument */ + setobj2s(L, L->top++, p3); /* 3rd argument */ + /* metamethod may yield only when called from Lua code */ + luaD_call(L, L->top - (4 - hasres), hasres, isLua(L->ci)); + if (hasres) { /* if has result, move it to its place */ + p3 = restorestack(L, result); + setobjs2s(L, p3, --L->top); + } +} + + +void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t)) { /* `t' is a table? */ + Table *h = hvalue(t); + const TValue *res = luaH_get(h, key); /* do a primitive get */ + if (!ttisnil(res) || /* result is not nil? */ + (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ + setobj2s(L, val, res); + return; + } + /* else will try the tag method */ + } + else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) + luaG_typeerror(L, t, "index"); + if (ttisfunction(tm)) { + callTM(L, tm, t, key, val, 1); + return; + } + t = tm; /* else repeat with 'tm' */ + } + luaG_runerror(L, "loop in gettable"); +} + + +void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t)) { /* `t' is a table? */ + Table *h = hvalue(t); + TValue *oldval = cast(TValue *, luaH_get(h, key)); + /* if previous value is not nil, there must be a previous entry + in the table; moreover, a metamethod has no relevance */ + if (!ttisnil(oldval) || + /* previous value is nil; must check the metamethod */ + ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL && + /* no metamethod; is there a previous entry in the table? */ + (oldval != luaO_nilobject || + /* no previous entry; must create one. (The next test is + always true; we only need the assignment.) */ + (oldval = luaH_newkey(L, h, key), 1)))) { + /* no metamethod and (now) there is an entry with given key */ + setobj2t(L, oldval, val); /* assign new value to that entry */ + invalidateTMcache(h); + luaC_barrierback(L, obj2gco(h), val); + return; + } + /* else will try the metamethod */ + } + else /* not a table; check metamethod */ + if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) + luaG_typeerror(L, t, "index"); + /* there is a metamethod */ + if (ttisfunction(tm)) { + callTM(L, tm, t, key, val, 0); + return; + } + t = tm; /* else repeat with 'tm' */ + } + luaG_runerror(L, "loop in settable"); +} + + +static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (ttisnil(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (ttisnil(tm)) return 0; + callTM(L, tm, p1, p2, res, 1); + return 1; +} + + +static const TValue *get_equalTM (lua_State *L, Table *mt1, Table *mt2, + TMS event) { + const TValue *tm1 = fasttm(L, mt1, event); + const TValue *tm2; + if (tm1 == NULL) return NULL; /* no metamethod */ + if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ + tm2 = fasttm(L, mt2, event); + if (tm2 == NULL) return NULL; /* no metamethod */ + if (luaV_rawequalobj(tm1, tm2)) /* same metamethods? */ + return tm1; + return NULL; +} + + +static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, + TMS event) { + if (!call_binTM(L, p1, p2, L->top, event)) + return -1; /* no metamethod */ + else + return !l_isfalse(L->top); +} + + +static int l_strcmp (const TString *ls, const TString *rs) { + const char *l = getstr(ls); + size_t ll = ls->tsv.len; + const char *r = getstr(rs); + size_t lr = rs->tsv.len; + for (;;) { + int temp = strcoll(l, r); + if (temp != 0) return temp; + else { /* strings are equal up to a `\0' */ + size_t len = strlen(l); /* index of first `\0' in both strings */ + if (len == lr) /* r is finished? */ + return (len == ll) ? 0 : 1; + else if (len == ll) /* l is finished? */ + return -1; /* l is smaller than r (because r is not finished) */ + /* both strings longer than `len'; go on comparing (after the `\0') */ + len++; + l += len; ll -= len; r += len; lr -= len; + } + } +} + + +int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttisnumber(l) && ttisnumber(r)) + return luai_numlt(L, nvalue(l), nvalue(r)); + else if (ttisstring(l) && ttisstring(r)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; + else if ((res = call_orderTM(L, l, r, TM_LT)) < 0) + luaG_ordererror(L, l, r); + return res; +} + + +int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttisnumber(l) && ttisnumber(r)) + return luai_numle(L, nvalue(l), nvalue(r)); + else if (ttisstring(l) && ttisstring(r)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; + else if ((res = call_orderTM(L, l, r, TM_LE)) >= 0) /* first try `le' */ + return res; + else if ((res = call_orderTM(L, r, l, TM_LT)) < 0) /* else try `lt' */ + luaG_ordererror(L, l, r); + return !res; +} + + +/* +** equality of Lua values. L == NULL means raw equality (no metamethods) +*/ +int luaV_equalobj_ (lua_State *L, const TValue *t1, const TValue *t2) { + const TValue *tm; + lua_assert(ttisequal(t1, t2)); + switch (ttype(t1)) { + case LUA_TNIL: return 1; + case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ + case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_TLCF: return fvalue(t1) == fvalue(t2); + case LUA_TSHRSTR: return eqshrstr(rawtsvalue(t1), rawtsvalue(t2)); + case LUA_TLNGSTR: return luaS_eqlngstr(rawtsvalue(t1), rawtsvalue(t2)); + case LUA_TUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = get_equalTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_TTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = get_equalTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + default: + lua_assert(iscollectable(t1)); + return gcvalue(t1) == gcvalue(t2); + } + if (tm == NULL) return 0; /* no TM? */ + callTM(L, tm, t1, t2, L->top, 1); /* call TM */ + return !l_isfalse(L->top); +} + + +void luaV_concat (lua_State *L, int total) { + lua_assert(total >= 2); + do { + StkId top = L->top; + int n = 2; /* number of elements handled in this pass (at least 2) */ + if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) { + if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) + luaG_concaterror(L, top-2, top-1); + } + else if (tsvalue(top-1)->len == 0) /* second operand is empty? */ + (void)tostring(L, top - 2); /* result is first operand */ + else if (ttisstring(top-2) && tsvalue(top-2)->len == 0) { + setobjs2s(L, top - 2, top - 1); /* result is second op. */ + } + else { + /* at least two non-empty string values; get as many as possible */ + size_t tl = tsvalue(top-1)->len; + char *buffer; + int i; + /* collect total length */ + for (i = 1; i < total && tostring(L, top-i-1); i++) { + size_t l = tsvalue(top-i-1)->len; + if (l >= (MAX_SIZET/sizeof(char)) - tl) + luaG_runerror(L, "string length overflow"); + tl += l; + } + buffer = luaZ_openspace(L, &G(L)->buff, tl); + tl = 0; + n = i; + do { /* concat all strings */ + size_t l = tsvalue(top-i)->len; + memcpy(buffer+tl, svalue(top-i), l * sizeof(char)); + tl += l; + } while (--i > 0); + setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); + } + total -= n-1; /* got 'n' strings to create 1 new */ + L->top -= n-1; /* popped 'n' strings and pushed one */ + } while (total > 1); /* repeat until only 1 result left */ +} + + +void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { + const TValue *tm; + switch (ttypenv(rb)) { + case LUA_TTABLE: { + Table *h = hvalue(rb); + tm = fasttm(L, h->metatable, TM_LEN); + if (tm) break; /* metamethod? break switch to call it */ + setnvalue(ra, cast_num(luaH_getn(h))); /* else primitive len */ + return; + } + case LUA_TSTRING: { + setnvalue(ra, cast_num(tsvalue(rb)->len)); + return; + } + default: { /* try metamethod */ + tm = luaT_gettmbyobj(L, rb, TM_LEN); + if (ttisnil(tm)) /* no metamethod? */ + luaG_typeerror(L, rb, "get length of"); + break; + } + } + callTM(L, tm, rb, rb, ra, 1); +} + +/* + * luaV_div and luaV_mod patched in from Lua 5.3.2 in order to properly handle + * div/mod by zero (instead of crashing, which is the default behavior in + * Lua 5.2) + */ + +/* +** Integer division; return 'm // n', that is, floor(m/n). +** C division truncates its result (rounds towards zero). +** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer, +** otherwise 'floor(q) == trunc(q) - 1'. +*/ +static lua_Number luaV_div (lua_State *L, lua_Number m, lua_Number n) { + if ((lua_Unsigned)(n) + 1u <= 1u) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to divide by zero"); + return (0 - m); /* n==-1; avoid overflow with 0x80000...//-1 */ + } + else { + lua_Number q = m / n; /* perform C division */ + if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */ + q -= 1; /* correct result for different rounding */ + return q; + } +} + + +/* +** Integer modulus; return 'm % n'. (Assume that C '%' with +** negative operands follows C99 behavior. See previous comment +** about luaV_div.) +*/ +static lua_Number luaV_mod (lua_State *L, lua_Number m, lua_Number n) { + if ((lua_Unsigned)(n) + 1u <= 1u) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to perform 'n%%0'"); + return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */ + } + else { + lua_Number r = m % n; + if (r != 0 && (m ^ n) < 0) /* 'm/n' would be non-integer negative? */ + r += n; /* correct result for different rounding */ + return r; + } +} + +/* + * End patch from 5.3.2 + */ + +void luaV_arith (lua_State *L, StkId ra, const TValue *rb, + const TValue *rc, TMS op) { + TValue tempb, tempc; + const TValue *b, *c; + if ((b = luaV_tonumber(rb, &tempb)) != NULL && + (c = luaV_tonumber(rc, &tempc)) != NULL) { + /* + * Patched: if dividing or modding, use patched functions from 5.3 + */ + lua_Number res; + int lop = op - TM_ADD + LUA_OPADD; + if (lop == LUA_OPDIV) { + res = luaV_div(L, nvalue(b), nvalue(c)); + } else if (lop == LUA_OPMOD) { + res = luaV_mod(L, nvalue(b), nvalue(c)); + } else { + res = luaO_arith(op - TM_ADD + LUA_OPADD, nvalue(b), nvalue(c)); + } + setnvalue(ra, res); + } + else if (!call_binTM(L, rb, rc, ra, op)) + luaG_aritherror(L, rb, rc); +} + + +/* +** check whether cached closure in prototype 'p' may be reused, that is, +** whether there is a cached closure with the same upvalues needed by +** new closure to be created. +*/ +static Closure *getcached (Proto *p, UpVal **encup, StkId base) { + Closure *c = p->cache; + if (c != NULL) { /* is there a cached closure? */ + int nup = p->sizeupvalues; + Upvaldesc *uv = p->upvalues; + int i; + for (i = 0; i < nup; i++) { /* check whether it has right upvalues */ + TValue *v = uv[i].instack ? base + uv[i].idx : encup[uv[i].idx]->v; + if (c->l.upvals[i]->v != v) + return NULL; /* wrong upvalue; cannot reuse closure */ + } + } + return c; /* return cached closure (or NULL if no cached closure) */ +} + + +/* +** create a new Lua closure, push it in the stack, and initialize +** its upvalues. Note that the call to 'luaC_barrierproto' must come +** before the assignment to 'p->cache', as the function needs the +** original value of that field. +*/ +static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, + StkId ra) { + int nup = p->sizeupvalues; + Upvaldesc *uv = p->upvalues; + int i; + Closure *ncl = luaF_newLclosure(L, nup); + ncl->l.p = p; + setclLvalue(L, ra, ncl); /* anchor new closure in stack */ + for (i = 0; i < nup; i++) { /* fill in its upvalues */ + if (uv[i].instack) /* upvalue refers to local variable? */ + ncl->l.upvals[i] = luaF_findupval(L, base + uv[i].idx); + else /* get upvalue from enclosing function */ + ncl->l.upvals[i] = encup[uv[i].idx]; + } + luaC_barrierproto(L, p, ncl); + p->cache = ncl; /* save it on cache for reuse */ +} + + +/* +** finish execution of an opcode interrupted by an yield +*/ +void luaV_finishOp (lua_State *L) { + CallInfo *ci = L->ci; + StkId base = ci->u.l.base; + Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ + OpCode op = GET_OPCODE(inst); + switch (op) { /* finish its execution */ + case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: + case OP_MOD: case OP_POW: case OP_UNM: case OP_LEN: + case OP_GETTABUP: case OP_GETTABLE: case OP_SELF: { + setobjs2s(L, base + GETARG_A(inst), --L->top); + break; + } + case OP_LE: case OP_LT: case OP_EQ: { + int res = !l_isfalse(L->top - 1); + L->top--; + /* metamethod should not be called when operand is K */ + lua_assert(!ISK(GETARG_B(inst))); + if (op == OP_LE && /* "<=" using "<" instead? */ + ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE))) + res = !res; /* invert result */ + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); + if (res != GETARG_A(inst)) /* condition failed? */ + ci->u.l.savedpc++; /* skip jump instruction */ + break; + } + case OP_CONCAT: { + StkId top = L->top - 1; /* top when 'call_binTM' was called */ + int b = GETARG_B(inst); /* first element to concatenate */ + int total = cast_int(top - 1 - (base + b)); /* yet to concatenate */ + setobj2s(L, top - 2, top); /* put TM result in proper position */ + if (total > 1) { /* are there elements to concat? */ + L->top = top - 1; /* top is one after last element (at top-2) */ + luaV_concat(L, total); /* concat them (may yield again) */ + } + /* move final result to final position */ + setobj2s(L, ci->u.l.base + GETARG_A(inst), L->top - 1); + L->top = ci->top; /* restore top */ + break; + } + case OP_TFORCALL: { + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_TFORLOOP); + L->top = ci->top; /* correct top */ + break; + } + case OP_CALL: { + if (GETARG_C(inst) - 1 >= 0) /* nresults >= 0? */ + L->top = ci->top; /* adjust results */ + break; + } + case OP_TAILCALL: case OP_SETTABUP: case OP_SETTABLE: + break; + default: lua_assert(0); + } +} + + + +/* +** some macros for common tasks in `luaV_execute' +*/ + +#if !defined luai_runtimecheck +#define luai_runtimecheck(L, c) /* void */ +#endif + + +#define RA(i) (base+GETARG_A(i)) +/* to be used after possible stack reallocation */ +#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) +#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) +#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) +#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) +#define KBx(i) \ + (k + (GETARG_Bx(i) != 0 ? GETARG_Bx(i) - 1 : GETARG_Ax(*ci->u.l.savedpc++))) + + +/* execute a jump instruction */ +#define dojump(ci,i,e) \ + { int a = GETARG_A(i); \ + if (a > 0) luaF_close(L, ci->u.l.base + a - 1); \ + ci->u.l.savedpc += GETARG_sBx(i) + e; } + +/* for test instructions, execute the jump instruction that follows it */ +#define donextjump(ci) { i = *ci->u.l.savedpc; dojump(ci, i, 1); } + + +#define Protect(x) { {x;}; base = ci->u.l.base; } + +#define checkGC(L,c) \ + Protect( luaC_condGC(L,{L->top = (c); /* limit of live values */ \ + luaC_step(L); \ + L->top = ci->top;}) /* restore top */ \ + luai_threadyield(L); ) + + +#define arith_op(op,tm) { \ + TValue *rb = RKB(i); \ + TValue *rc = RKC(i); \ + if (ttisnumber(rb) && ttisnumber(rc)) { \ + lua_Number nb = nvalue(rb), nc = nvalue(rc); \ + setnvalue(ra, op(L, nb, nc)); \ + } \ + else { Protect(luaV_arith(L, ra, rb, rc, tm)); } } + + +#define vmdispatch(o) switch(o) +#define vmcase(l,b) case l: {b} break; +#define vmcasenb(l,b) case l: {b} /* nb = no break */ + +void luaV_execute (lua_State *L) { + CallInfo *ci = L->ci; + LClosure *cl; + TValue *k; + StkId base; + newframe: /* reentry point when frame changes (call/return) */ + lua_assert(ci == L->ci); + cl = clLvalue(ci->func); + k = cl->p->k; + base = ci->u.l.base; + /* main loop of interpreter */ + for (;;) { + Instruction i = *(ci->u.l.savedpc++); + StkId ra; + if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && + (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { + Protect(traceexec(L)); + } + /* WARNING: several calls may realloc the stack and invalidate `ra' */ + ra = RA(i); + lua_assert(base == ci->u.l.base); + lua_assert(base <= L->top && L->top < L->stack + L->stacksize); + vmdispatch (GET_OPCODE(i)) { + vmcase(OP_MOVE, + setobjs2s(L, ra, RB(i)); + ) + vmcase(OP_LOADK, + TValue *rb = k + GETARG_Bx(i); + setobj2s(L, ra, rb); + ) + vmcase(OP_LOADKX, + TValue *rb; + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); + rb = k + GETARG_Ax(*ci->u.l.savedpc++); + setobj2s(L, ra, rb); + ) + vmcase(OP_LOADBOOL, + setbvalue(ra, GETARG_B(i)); + if (GETARG_C(i)) ci->u.l.savedpc++; /* skip next instruction (if C) */ + ) + vmcase(OP_LOADNIL, + int b = GETARG_B(i); + do { + setnilvalue(ra++); + } while (b--); + ) + vmcase(OP_GETUPVAL, + int b = GETARG_B(i); + setobj2s(L, ra, cl->upvals[b]->v); + ) + vmcase(OP_GETTABUP, + int b = GETARG_B(i); + Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra)); + ) + vmcase(OP_GETTABLE, + Protect(luaV_gettable(L, RB(i), RKC(i), ra)); + ) + vmcase(OP_SETTABUP, + int a = GETARG_A(i); + Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i))); + ) + vmcase(OP_SETUPVAL, + UpVal *uv = cl->upvals[GETARG_B(i)]; + setobj(L, uv->v, ra); + luaC_barrier(L, uv, ra); + ) + vmcase(OP_SETTABLE, + Protect(luaV_settable(L, ra, RKB(i), RKC(i))); + ) + vmcase(OP_NEWTABLE, + int b = GETARG_B(i); + int c = GETARG_C(i); + Table *t = luaH_new(L); + sethvalue(L, ra, t); + if (b != 0 || c != 0) + luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); + checkGC(L, ra + 1); + ) + vmcase(OP_SELF, + StkId rb = RB(i); + setobjs2s(L, ra+1, rb); + Protect(luaV_gettable(L, rb, RKC(i), ra)); + ) + vmcase(OP_ADD, + arith_op(luai_numadd, TM_ADD); + ) + vmcase(OP_SUB, + arith_op(luai_numsub, TM_SUB); + ) + vmcase(OP_MUL, + arith_op(luai_nummul, TM_MUL); + ) + /* + * Patched: use luaV_* instead of luai_* to handle div/mod by 0 + */ + vmcase(OP_DIV, + arith_op(luaV_div, TM_DIV); + ) + vmcase(OP_MOD, + arith_op(luaV_mod, TM_MOD); + ) + vmcase(OP_POW, + arith_op(luai_numpow, TM_POW); + ) + vmcase(OP_UNM, + TValue *rb = RB(i); + if (ttisnumber(rb)) { + lua_Number nb = nvalue(rb); + setnvalue(ra, luai_numunm(L, nb)); + } + else { + Protect(luaV_arith(L, ra, rb, rb, TM_UNM)); + } + ) + vmcase(OP_NOT, + TValue *rb = RB(i); + int res = l_isfalse(rb); /* next assignment may change this value */ + setbvalue(ra, res); + ) + vmcase(OP_LEN, + Protect(luaV_objlen(L, ra, RB(i))); + ) + vmcase(OP_CONCAT, + int b = GETARG_B(i); + int c = GETARG_C(i); + StkId rb; + L->top = base + c + 1; /* mark the end of concat operands */ + Protect(luaV_concat(L, c - b + 1)); + ra = RA(i); /* 'luav_concat' may invoke TMs and move the stack */ + rb = b + base; + setobjs2s(L, ra, rb); + checkGC(L, (ra >= rb ? ra + 1 : rb)); + L->top = ci->top; /* restore top */ + ) + vmcase(OP_JMP, + dojump(ci, i, 0); + ) + vmcase(OP_EQ, + TValue *rb = RKB(i); + TValue *rc = RKC(i); + Protect( + if (cast_int(equalobj(L, rb, rc)) != GETARG_A(i)) + ci->u.l.savedpc++; + else + donextjump(ci); + ) + ) + vmcase(OP_LT, + Protect( + if (luaV_lessthan(L, RKB(i), RKC(i)) != GETARG_A(i)) + ci->u.l.savedpc++; + else + donextjump(ci); + ) + ) + vmcase(OP_LE, + Protect( + if (luaV_lessequal(L, RKB(i), RKC(i)) != GETARG_A(i)) + ci->u.l.savedpc++; + else + donextjump(ci); + ) + ) + vmcase(OP_TEST, + if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra)) + ci->u.l.savedpc++; + else + donextjump(ci); + ) + vmcase(OP_TESTSET, + TValue *rb = RB(i); + if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb)) + ci->u.l.savedpc++; + else { + setobjs2s(L, ra, rb); + donextjump(ci); + } + ) + vmcase(OP_CALL, + int b = GETARG_B(i); + int nresults = GETARG_C(i) - 1; + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + if (luaD_precall(L, ra, nresults)) { /* C function? */ + if (nresults >= 0) L->top = ci->top; /* adjust results */ + base = ci->u.l.base; + } + else { /* Lua function */ + ci = L->ci; + ci->callstatus |= CIST_REENTRY; + goto newframe; /* restart luaV_execute over new Lua function */ + } + ) + vmcase(OP_TAILCALL, + int b = GETARG_B(i); + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); + if (luaD_precall(L, ra, LUA_MULTRET)) /* C function? */ + base = ci->u.l.base; + else { + /* tail call: put called frame (n) in place of caller one (o) */ + CallInfo *nci = L->ci; /* called frame */ + CallInfo *oci = nci->previous; /* caller frame */ + StkId nfunc = nci->func; /* called function */ + StkId ofunc = oci->func; /* caller function */ + /* last stack slot filled by 'precall' */ + StkId lim = nci->u.l.base + getproto(nfunc)->numparams; + int aux; + /* close all upvalues from previous call */ + if (cl->p->sizep > 0) luaF_close(L, oci->u.l.base); + /* move new frame into old one */ + for (aux = 0; nfunc + aux < lim; aux++) + setobjs2s(L, ofunc + aux, nfunc + aux); + oci->u.l.base = ofunc + (nci->u.l.base - nfunc); /* correct base */ + oci->top = L->top = ofunc + (L->top - nfunc); /* correct top */ + oci->u.l.savedpc = nci->u.l.savedpc; + oci->callstatus |= CIST_TAIL; /* function was tail called */ + ci = L->ci = oci; /* remove new frame */ + lua_assert(L->top == oci->u.l.base + getproto(ofunc)->maxstacksize); + goto newframe; /* restart luaV_execute over new Lua function */ + } + ) + vmcasenb(OP_RETURN, + int b = GETARG_B(i); + if (b != 0) L->top = ra+b-1; + if (cl->p->sizep > 0) luaF_close(L, base); + b = luaD_poscall(L, ra); + if (!(ci->callstatus & CIST_REENTRY)) /* 'ci' still the called one */ + return; /* external invocation: return */ + else { /* invocation via reentry: continue execution */ + ci = L->ci; + if (b) L->top = ci->top; + lua_assert(isLua(ci)); + lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL); + goto newframe; /* restart luaV_execute over new Lua function */ + } + ) + vmcase(OP_FORLOOP, + lua_Number step = nvalue(ra+2); + lua_Number idx = luai_numadd(L, nvalue(ra), step); /* increment index */ + lua_Number limit = nvalue(ra+1); + if (luai_numlt(L, 0, step) ? luai_numle(L, idx, limit) + : luai_numle(L, limit, idx)) { + ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ + setnvalue(ra, idx); /* update internal index... */ + setnvalue(ra+3, idx); /* ...and external index */ + } + ) + vmcase(OP_FORPREP, + const TValue *init = ra; + const TValue *plimit = ra+1; + const TValue *pstep = ra+2; + if (!tonumber(init, ra)) + luaG_runerror(L, LUA_QL("for") " initial value must be a number"); + else if (!tonumber(plimit, ra+1)) + luaG_runerror(L, LUA_QL("for") " limit must be a number"); + else if (!tonumber(pstep, ra+2)) + luaG_runerror(L, LUA_QL("for") " step must be a number"); + setnvalue(ra, luai_numsub(L, nvalue(ra), nvalue(pstep))); + ci->u.l.savedpc += GETARG_sBx(i); + ) + vmcasenb(OP_TFORCALL, + StkId cb = ra + 3; /* call base */ + setobjs2s(L, cb+2, ra+2); + setobjs2s(L, cb+1, ra+1); + setobjs2s(L, cb, ra); + L->top = cb + 3; /* func. + 2 args (state and index) */ + Protect(luaD_call(L, cb, GETARG_C(i), 1)); + L->top = ci->top; + i = *(ci->u.l.savedpc++); /* go to next instruction */ + ra = RA(i); + lua_assert(GET_OPCODE(i) == OP_TFORLOOP); + goto l_tforloop; + ) + vmcase(OP_TFORLOOP, + l_tforloop: + if (!ttisnil(ra + 1)) { /* continue loop? */ + setobjs2s(L, ra, ra + 1); /* save control variable */ + ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ + } + ) + vmcase(OP_SETLIST, + int n = GETARG_B(i); + int c = GETARG_C(i); + int last; + Table *h; + if (n == 0) n = cast_int(L->top - ra) - 1; + if (c == 0) { + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); + c = GETARG_Ax(*ci->u.l.savedpc++); + } + luai_runtimecheck(L, ttistable(ra)); + h = hvalue(ra); + last = ((c-1)*LFIELDS_PER_FLUSH) + n; + if (last > h->sizearray) /* needs more space? */ + luaH_resizearray(L, h, last); /* pre-allocate it at once */ + for (; n > 0; n--) { + TValue *val = ra+n; + luaH_setint(L, h, last--, val); + luaC_barrierback(L, obj2gco(h), val); + } + L->top = ci->top; /* correct top (in case of previous open call) */ + ) + vmcase(OP_CLOSURE, + Proto *p = cl->p->p[GETARG_Bx(i)]; + Closure *ncl = getcached(p, cl->upvals, base); /* cached closure */ + if (ncl == NULL) /* no match? */ + pushclosure(L, p, cl->upvals, base, ra); /* create a new one */ + else + setclLvalue(L, ra, ncl); /* push cashed closure */ + checkGC(L, ra + 1); + ) + vmcase(OP_VARARG, + int b = GETARG_B(i) - 1; + int j; + int n = cast_int(base - ci->func) - cl->p->numparams - 1; + if (b < 0) { /* B == 0? */ + b = n; /* get all var. arguments */ + Protect(luaD_checkstack(L, n)); + ra = RA(i); /* previous call may change the stack */ + L->top = ra + n; + } + for (j = 0; j < b; j++) { + if (j < n) { + setobjs2s(L, ra + j, base - n + j); + } + else { + setnilvalue(ra + j); + } + } + ) + vmcase(OP_EXTRAARG, + lua_assert(0); + ) + } + } +} + diff --git a/usr/src/uts/common/fs/zfs/lua/lvm.h b/usr/src/uts/common/fs/zfs/lua/lvm.h new file mode 100644 index 0000000000..5380270da6 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lvm.h @@ -0,0 +1,44 @@ +/* +** $Id: lvm.h,v 2.18.1.1 2013/04/12 18:48:47 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lvm_h +#define lvm_h + + +#include "ldo.h" +#include "lobject.h" +#include "ltm.h" + + +#define tostring(L,o) (ttisstring(o) || (luaV_tostring(L, o))) + +#define tonumber(o,n) (ttisnumber(o) || (((o) = luaV_tonumber(o,n)) != NULL)) + +#define equalobj(L,o1,o2) (ttisequal(o1, o2) && luaV_equalobj_(L, o1, o2)) + +#define luaV_rawequalobj(o1,o2) equalobj(NULL,o1,o2) + + +/* not to called directly */ +LUAI_FUNC int luaV_equalobj_ (lua_State *L, const TValue *t1, const TValue *t2); + + +LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); +LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); +LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_finishOp (lua_State *L); +LUAI_FUNC void luaV_execute (lua_State *L); +LUAI_FUNC void luaV_concat (lua_State *L, int total); +LUAI_FUNC void luaV_arith (lua_State *L, StkId ra, const TValue *rb, + const TValue *rc, TMS op); +LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); + +#endif diff --git a/usr/src/uts/common/fs/zfs/lua/lzio.c b/usr/src/uts/common/fs/zfs/lua/lzio.c new file mode 100644 index 0000000000..53e6a3daeb --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lzio.c @@ -0,0 +1,76 @@ +/* +** $Id: lzio.c,v 1.35.1.1 2013/04/12 18:48:47 roberto Exp $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + + +#include <sys/zfs_context.h> + +#define lzio_c +#define LUA_CORE + +#include "lua.h" + +#include "llimits.h" +#include "lmem.h" +#include "lstate.h" +#include "lzio.h" + + +int luaZ_fill (ZIO *z) { + size_t size; + lua_State *L = z->L; + const char *buff; + lua_unlock(L); + buff = z->reader(L, z->data, &size); + lua_lock(L); + if (buff == NULL || size == 0) + return EOZ; + z->n = size - 1; /* discount char being returned */ + z->p = buff; + return cast_uchar(*(z->p++)); +} + + +void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { + z->L = L; + z->reader = reader; + z->data = data; + z->n = 0; + z->p = NULL; +} + + +/* --------------------------------------------------------------- read --- */ +size_t luaZ_read (ZIO *z, void *b, size_t n) { + while (n) { + size_t m; + if (z->n == 0) { /* no bytes in buffer? */ + if (luaZ_fill(z) == EOZ) /* try to read more */ + return n; /* no more input; return number of missing bytes */ + else { + z->n++; /* luaZ_fill consumed first byte; put it back */ + z->p--; + } + } + m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ + memcpy(b, z->p, m); + z->n -= m; + z->p += m; + b = (char *)b + m; + n -= m; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ +char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { + if (n > buff->buffsize) { + if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; + luaZ_resizebuffer(L, buff, n); + } + return buff->buffer; +} + + diff --git a/usr/src/uts/common/fs/zfs/lua/lzio.h b/usr/src/uts/common/fs/zfs/lua/lzio.h new file mode 100644 index 0000000000..441f7479cb --- /dev/null +++ b/usr/src/uts/common/fs/zfs/lua/lzio.h @@ -0,0 +1,65 @@ +/* +** $Id: lzio.h,v 1.26.1.1 2013/04/12 18:48:47 roberto Exp $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + + +#ifndef lzio_h +#define lzio_h + +#include "lua.h" + +#include "lmem.h" + + +#define EOZ (-1) /* end of stream */ + +typedef struct Zio ZIO; + +#define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z)) + + +typedef struct Mbuffer { + char *buffer; + size_t n; + size_t buffsize; +} Mbuffer; + +#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) + +#define luaZ_buffer(buff) ((buff)->buffer) +#define luaZ_sizebuffer(buff) ((buff)->buffsize) +#define luaZ_bufflen(buff) ((buff)->n) + +#define luaZ_resetbuffer(buff) ((buff)->n = 0) + + +#define luaZ_resizebuffer(L, buff, size) \ + (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ + (buff)->buffsize = size) + +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) + + +LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); +LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, + void *data); +LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */ + + + +/* --------- Private Part ------------------ */ + +struct Zio { + size_t n; /* bytes still unread */ + const char *p; /* current position in buffer */ + lua_Reader reader; /* reader function */ + void* data; /* additional data */ + lua_State *L; /* Lua state (for reader) */ +}; + + +LUAI_FUNC int luaZ_fill (ZIO *z); + +#endif diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h index fc5117bc9b..f709395883 100644 --- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h +++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h @@ -227,6 +227,16 @@ dsl_dataset_phys(dsl_dataset_t *ds) return (ds->ds_dbuf->db_data); } +typedef struct dsl_dataset_promote_arg { + const char *ddpa_clonename; + dsl_dataset_t *ddpa_clone; + list_t shared_snaps, origin_snaps, clone_snaps; + dsl_dataset_t *origin_origin; /* origin of the origin */ + uint64_t used, comp, uncomp, unique, cloneusedsnap, originusedsnap; + nvlist_t *err_ds; + cred_t *cr; +} dsl_dataset_promote_arg_t; + /* * The max length of a temporary tag prefix is the number of hex digits * required to express UINT64_MAX plus one for the hyphen. @@ -260,6 +270,8 @@ uint64_t dsl_dataset_create_sync(dsl_dir_t *pds, const char *lastname, uint64_t dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, uint64_t flags, dmu_tx_t *tx); int dsl_dataset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors); +void dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx); +int dsl_dataset_promote_check(void *arg, dmu_tx_t *tx); int dsl_dataset_promote(const char *name, char *conflsnap); int dsl_dataset_clone_swap(dsl_dataset_t *clone, dsl_dataset_t *origin_head, boolean_t force); @@ -284,7 +296,36 @@ int dsl_dataset_block_kill(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx, boolean_t async); void dsl_dataset_dirty(dsl_dataset_t *ds, dmu_tx_t *tx); + +int get_clones_stat_impl(dsl_dataset_t *ds, nvlist_t *val); +char *get_receive_resume_stats_impl(dsl_dataset_t *ds); +char *get_child_receive_stats(dsl_dataset_t *ds); +uint64_t dsl_get_refratio(dsl_dataset_t *ds); +uint64_t dsl_get_logicalreferenced(dsl_dataset_t *ds); +uint64_t dsl_get_compressratio(dsl_dataset_t *ds); +uint64_t dsl_get_used(dsl_dataset_t *ds); +uint64_t dsl_get_creation(dsl_dataset_t *ds); +uint64_t dsl_get_creationtxg(dsl_dataset_t *ds); +uint64_t dsl_get_refquota(dsl_dataset_t *ds); +uint64_t dsl_get_refreservation(dsl_dataset_t *ds); +uint64_t dsl_get_guid(dsl_dataset_t *ds); +uint64_t dsl_get_unique(dsl_dataset_t *ds); +uint64_t dsl_get_objsetid(dsl_dataset_t *ds); +uint64_t dsl_get_userrefs(dsl_dataset_t *ds); +uint64_t dsl_get_defer_destroy(dsl_dataset_t *ds); +uint64_t dsl_get_referenced(dsl_dataset_t *ds); +uint64_t dsl_get_numclones(dsl_dataset_t *ds); +uint64_t dsl_get_inconsistent(dsl_dataset_t *ds); +uint64_t dsl_get_available(dsl_dataset_t *ds); +int dsl_get_written(dsl_dataset_t *ds, uint64_t *written); +int dsl_get_prev_snap(dsl_dataset_t *ds, char *snap); +int dsl_get_mountpoint(dsl_dataset_t *ds, const char *dsname, char *value, + char *source); + +void get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv); + void dsl_dataset_stats(dsl_dataset_t *os, nvlist_t *nv); + void dsl_dataset_fast_stat(dsl_dataset_t *ds, dmu_objset_stats_t *stat); void dsl_dataset_space(dsl_dataset_t *ds, uint64_t *refdbytesp, uint64_t *availbytesp, diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_destroy.h b/usr/src/uts/common/fs/zfs/sys/dsl_destroy.h index 3f638643b6..ae3ca0cfbd 100644 --- a/usr/src/uts/common/fs/zfs/sys/dsl_destroy.h +++ b/usr/src/uts/common/fs/zfs/sys/dsl_destroy.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ @@ -46,6 +46,21 @@ int dsl_destroy_snapshot_check_impl(struct dsl_dataset *, boolean_t); void dsl_destroy_snapshot_sync_impl(struct dsl_dataset *, boolean_t, struct dmu_tx *); +typedef struct dsl_destroy_snapshot_arg { + const char *ddsa_name; + boolean_t ddsa_defer; +} dsl_destroy_snapshot_arg_t; + +int dsl_destroy_snapshot_check(void *, dmu_tx_t *); +void dsl_destroy_snapshot_sync(void *, dmu_tx_t *); + +typedef struct dsl_destroy_head_arg { + const char *ddha_name; +} dsl_destroy_head_arg_t; + +int dsl_destroy_head_check(void *, dmu_tx_t *); +void dsl_destroy_head_sync(void *, dmu_tx_t *); + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_dir.h b/usr/src/uts/common/fs/zfs/sys/dsl_dir.h index a6414887c3..c5ba6e2f6e 100644 --- a/usr/src/uts/common/fs/zfs/sys/dsl_dir.h +++ b/usr/src/uts/common/fs/zfs/sys/dsl_dir.h @@ -131,6 +131,20 @@ void dsl_dir_name(dsl_dir_t *dd, char *buf); int dsl_dir_namelen(dsl_dir_t *dd); uint64_t dsl_dir_create_sync(dsl_pool_t *dp, dsl_dir_t *pds, const char *name, dmu_tx_t *tx); + +uint64_t dsl_dir_get_used(dsl_dir_t *dd); +uint64_t dsl_dir_get_quota(dsl_dir_t *dd); +uint64_t dsl_dir_get_reservation(dsl_dir_t *dd); +uint64_t dsl_dir_get_compressratio(dsl_dir_t *dd); +uint64_t dsl_dir_get_logicalused(dsl_dir_t *dd); +uint64_t dsl_dir_get_usedsnap(dsl_dir_t *dd); +uint64_t dsl_dir_get_usedds(dsl_dir_t *dd); +uint64_t dsl_dir_get_usedrefreserv(dsl_dir_t *dd); +uint64_t dsl_dir_get_usedchild(dsl_dir_t *dd); +void dsl_dir_get_origin(dsl_dir_t *dd, char *buf); +int dsl_dir_get_filesystem_count(dsl_dir_t *dd, uint64_t *count); +int dsl_dir_get_snapshot_count(dsl_dir_t *dd, uint64_t *count); + void dsl_dir_stats(dsl_dir_t *dd, nvlist_t *nv); uint64_t dsl_dir_space_available(dsl_dir_t *dd, dsl_dir_t *ancestor, int64_t delta, int ondiskonly); diff --git a/usr/src/uts/common/fs/zfs/sys/zcp.h b/usr/src/uts/common/fs/zfs/sys/zcp.h new file mode 100644 index 0000000000..443b94e26d --- /dev/null +++ b/usr/src/uts/common/fs/zfs/sys/zcp.h @@ -0,0 +1,146 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +#ifndef _SYS_ZCP_H +#define _SYS_ZCP_H + +#include <sys/dmu_tx.h> +#include <sys/dsl_pool.h> + +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZCP_RUN_INFO_KEY "runinfo" + +extern uint64_t zfs_lua_max_instrlimit; +extern uint64_t zfs_lua_max_memlimit; + +int zcp_argerror(lua_State *, int, const char *, ...); + +int zcp_eval(const char *, const char *, uint64_t, uint64_t, nvpair_t *, + nvlist_t *); + +int zcp_load_list_lib(lua_State *); + +int zcp_load_synctask_lib(lua_State *, boolean_t); + +typedef void (zcp_cleanup_t)(void *); + +typedef struct zcp_run_info { + dsl_pool_t *zri_pool; + + /* + * An estimate of the total ammount of space consumed by all + * synctasks we have successfully performed so far in this + * channel program. Used to generate ENOSPC errors for syncfuncs. + */ + int zri_space_used; + + /* + * The credentials of the thread which originally invoked the channel + * program. Since channel programs are always invoked from the synctask + * thread they should always do permissions checks against this cred + * rather than the 'current' thread's. + */ + cred_t *zri_cred; + + /* + * The tx in which this channel program is running. + */ + dmu_tx_t *zri_tx; + + /* + * The maximum number of Lua instructions the channel program is allowed + * to execute. If it takes longer than this it will time out. A value + * of 0 indicates no instruction limit. + */ + uint64_t zri_maxinstrs; + + /* + * The number of Lua instructions the channel program has executed. + */ + uint64_t zri_curinstrs; + + /* + * Boolean indicating whether or not the channel program exited + * because it timed out. + */ + boolean_t zri_timed_out; + + /* + * The currently registered cleanup function, which will be called + * with the stored argument if a fatal error occurs. + */ + zcp_cleanup_t *zri_cleanup; + void *zri_cleanup_arg; +} zcp_run_info_t; + +zcp_run_info_t *zcp_run_info(lua_State *); +void zcp_register_cleanup(lua_State *, zcp_cleanup_t, void *); +void zcp_clear_cleanup(lua_State *); +void zcp_cleanup(lua_State *); + +/* + * Argument parsing routines for channel program callback functions. + */ +typedef struct zcp_arg { + /* + * The name of this argument. For keyword arguments this is the name + * functions will use to set the argument. For positional arguments + * the name has no programatic meaning, but will appear in error + * messages and help output. + */ + const char *za_name; + + /* + * The Lua type this argument should have (e.g. LUA_TSTRING, + * LUA_TBOOLEAN) see the lua_type() function documentation for a + * complete list. Calling a function with an argument that does + * not match the expected type will result in the program terminating. + */ + const int za_lua_type; +} zcp_arg_t; + +void zcp_parse_args(lua_State *, const char *, const zcp_arg_t *, + const zcp_arg_t *); +int zcp_nvlist_to_lua(lua_State *, nvlist_t *, char *, int); +int zcp_dataset_hold_error(lua_State *, dsl_pool_t *, const char *, int); +struct dsl_dataset *zcp_dataset_hold(lua_State *, dsl_pool_t *, + const char *, void *); + +typedef int (zcp_lib_func_t)(lua_State *); +typedef struct zcp_lib_info { + const char *name; + zcp_lib_func_t *func; + const zcp_arg_t pargs[4]; + const zcp_arg_t kwargs[2]; +} zcp_lib_info_t; + +int zcp_nvlist_to_lua(lua_State *, nvlist_t *, char *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ZCP_H */ diff --git a/usr/src/uts/common/fs/zfs/sys/zcp_global.h b/usr/src/uts/common/fs/zfs/sys/zcp_global.h new file mode 100644 index 0000000000..e227f2f4b7 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/sys/zcp_global.h @@ -0,0 +1,35 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +#ifndef _SYS_ZCP_GLOBALS_H +#define _SYS_ZCP_GLOBALS_H + +#include "lua.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void zcp_load_globals(lua_State *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ZCP_GLOBALS_H */ diff --git a/usr/src/uts/common/fs/zfs/sys/zcp_iter.h b/usr/src/uts/common/fs/zfs/sys/zcp_iter.h new file mode 100644 index 0000000000..a021e1ce89 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/sys/zcp_iter.h @@ -0,0 +1,41 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 (c) 2016 by Delphix. All rights reserved. + */ + +#ifndef _SYS_ZCP_LIST_H +#define _SYS_ZCP_LIST_H + +#include "lua.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void zcp_load_list_funcs(lua_State *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ZCP_LIST_H */ diff --git a/usr/src/uts/common/fs/zfs/sys/zcp_prop.h b/usr/src/uts/common/fs/zfs/sys/zcp_prop.h new file mode 100644 index 0000000000..97b1761956 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/sys/zcp_prop.h @@ -0,0 +1,34 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +#ifndef _SYS_ZCP_PROP_H +#define _SYS_ZCP_PROP_H + +#ifdef __cplusplus +extern "C" { +#endif + +int zcp_load_get_lib(lua_State *state); +boolean_t prop_valid_for_ds(dsl_dataset_t *ds, zfs_prop_t zfs_prop); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ZCP_PROP_H */ diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h index 16514e6939..0657a12efa 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. - * Copyright (c) 2012, 2015 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright 2016 RackTop Systems. * Copyright (c) 2014 Integros [integros.com] */ @@ -414,6 +414,8 @@ typedef struct zfs_useracct { #define ZPOOL_EXPORT_AFTER_SPLIT 0x1 #ifdef _KERNEL +struct objset; +struct zfsvfs; typedef struct zfs_creat { nvlist_t *zct_zplprops; @@ -422,13 +424,13 @@ typedef struct zfs_creat { extern dev_info_t *zfs_dip; -extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr); -extern int zfs_secpolicy_rename_perms(const char *from, - const char *to, cred_t *cr); -extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr); +extern int zfs_secpolicy_snapshot_perms(const char *, cred_t *); +extern int zfs_secpolicy_rename_perms(const char *, const char *, cred_t *); +extern int zfs_secpolicy_destroy_perms(const char *, cred_t *); extern int zfs_busy(void); extern int zfs_unmount_snap(const char *); extern void zfs_destroy_unmount_origin(const char *); +extern int getzfsvfs_impl(struct objset *, struct zfsvfs **); /* * ZFS minor numbers can refer to either a control device instance or diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h index 3278171372..25d9d19dd6 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h @@ -150,6 +150,7 @@ extern boolean_t zfs_fuid_overquota(zfsvfs_t *zfsvfs, boolean_t isgroup, uint64_t fuid); extern int zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers); extern int zfsvfs_create(const char *name, zfsvfs_t **zfvp); +extern int zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os); extern void zfsvfs_free(zfsvfs_t *zfsvfs); extern int zfs_check_global_label(const char *dsname, const char *hexsl); diff --git a/usr/src/uts/common/fs/zfs/zcp.c b/usr/src/uts/common/fs/zfs/zcp.c new file mode 100644 index 0000000000..8dbde05262 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/zcp.c @@ -0,0 +1,1347 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +/* + * ZFS Channel Programs (ZCP) + * + * The ZCP interface allows various ZFS commands and operations ZFS + * administrative operations (e.g. creating and destroying snapshots, typically + * performed via an ioctl to /dev/zfs by the zfs(1M) command and + * libzfs/libzfs_core) to be run * programmatically as a Lua script. A ZCP + * script is run as a dsl_sync_task and fully executed during one transaction + * group sync. This ensures that no other changes can be written concurrently + * with a running Lua script. Combining multiple calls to the exposed ZFS + * functions into one script gives a number of benefits: + * + * 1. Atomicity. For some compound or iterative operations, it's useful to be + * able to guarantee that the state of a pool has not changed between calls to + * ZFS. + * + * 2. Performance. If a large number of changes need to be made (e.g. deleting + * many filesystems), there can be a significant performance penalty as a + * result of the need to wait for a transaction group sync to pass for every + * single operation. When expressed as a single ZCP script, all these changes + * can be performed at once in one txg sync. + * + * A modified version of the Lua 5.2 interpreter is used to run channel program + * scripts. The Lua 5.2 manual can be found at: + * + * http://www.lua.org/manual/5.2/ + * + * If being run by a user (via an ioctl syscall), executing a ZCP script + * requires root privileges in the global zone. + * + * Scripts are passed to zcp_eval() as a string, then run in a synctask by + * zcp_eval_sync(). Arguments can be passed into the Lua script as an nvlist, + * which will be converted to a Lua table. Similarly, values returned from + * a ZCP script will be converted to an nvlist. See zcp_lua_to_nvlist_impl() + * for details on exact allowed types and conversion. + * + * ZFS functionality is exposed to a ZCP script as a library of function calls. + * These calls are sorted into submodules, such as zfs.list and zfs.sync, for + * iterators and synctasks, respectively. Each of these submodules resides in + * its own source file, with a zcp_*_info structure describing each library + * call in the submodule. + * + * Error handling in ZCP scripts is handled by a number of different methods + * based on severity: + * + * 1. Memory and time limits are in place to prevent a channel program from + * consuming excessive system or running forever. If one of these limits is + * hit, the channel program will be stopped immediately and return from + * zcp_eval() with an error code. No attempt will be made to roll back or undo + * any changes made by the channel program before the error occured. + * Consumers invoking zcp_eval() from elsewhere in the kernel may pass a time + * limit of 0, disabling the time limit. + * + * 2. Internal Lua errors can occur as a result of a syntax error, calling a + * library function with incorrect arguments, invoking the error() function, + * failing an assert(), or other runtime errors. In these cases the channel + * program will stop executing and return from zcp_eval() with an error code. + * In place of a return value, an error message will also be returned in the + * 'result' nvlist containing information about the error. No attempt will be + * made to roll back or undo any changes made by the channel program before the + * error occured. + * + * 3. If an error occurs inside a ZFS library call which returns an error code, + * the error is returned to the Lua script to be handled as desired. + * + * In the first two cases, Lua's error-throwing mechanism is used, which + * longjumps out of the script execution with luaL_error() and returns with the + * error. + * + * See zfs-program(1M) for more information on high level usage. + */ + +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" + +#include <sys/dsl_prop.h> +#include <sys/dsl_synctask.h> +#include <sys/dsl_dataset.h> +#include <sys/zcp.h> +#include <sys/zcp_iter.h> +#include <sys/zcp_prop.h> +#include <sys/zcp_global.h> +#include <util/sscanf.h> + +uint64_t zfs_lua_check_instrlimit_interval = 100; +uint64_t zfs_lua_max_instrlimit = ZCP_MAX_INSTRLIMIT; +uint64_t zfs_lua_max_memlimit = ZCP_MAX_MEMLIMIT; + +static int zcp_nvpair_value_to_lua(lua_State *, nvpair_t *, char *, int); +static int zcp_lua_to_nvlist_impl(lua_State *, int, nvlist_t *, const char *, + int); + +typedef struct zcp_alloc_arg { + boolean_t aa_must_succeed; + int64_t aa_alloc_remaining; + int64_t aa_alloc_limit; +} zcp_alloc_arg_t; + +typedef struct zcp_eval_arg { + lua_State *ea_state; + zcp_alloc_arg_t *ea_allocargs; + cred_t *ea_cred; + nvlist_t *ea_outnvl; + int ea_result; + uint64_t ea_instrlimit; +} zcp_eval_arg_t; + +/*ARGSUSED*/ +static int +zcp_eval_check(void *arg, dmu_tx_t *tx) +{ + return (0); +} + +/* + * The outer-most error callback handler for use with lua_pcall(). On + * error Lua will call this callback with a single argument that + * represents the error value. In most cases this will be a string + * containing an error message, but channel programs can use Lua's + * error() function to return arbitrary objects as errors. This callback + * returns (on the Lua stack) the original error object along with a traceback. + * + * Fatal Lua errors can occur while resources are held, so we also call any + * registered cleanup function here. + */ +static int +zcp_error_handler(lua_State *state) +{ + const char *msg; + + zcp_cleanup(state); + + VERIFY3U(1, ==, lua_gettop(state)); + msg = lua_tostring(state, 1); + luaL_traceback(state, state, msg, 1); + return (1); +} + +int +zcp_argerror(lua_State *state, int narg, const char *msg, ...) +{ + va_list alist; + + va_start(alist, msg); + const char *buf = lua_pushvfstring(state, msg, alist); + va_end(alist); + + return (luaL_argerror(state, narg, buf)); +} + +/* + * Install a new cleanup function, which will be invoked with the given + * opaque argument if a fatal error causes the Lua interpreter to longjump out + * of a function call. + * + * If an error occurs, the cleanup function will be invoked exactly once and + * then unreigstered. + */ +void +zcp_register_cleanup(lua_State *state, zcp_cleanup_t cleanfunc, void *cleanarg) +{ + zcp_run_info_t *ri = zcp_run_info(state); + /* + * A cleanup function should always be explicitly removed before + * installing a new one to avoid accidental clobbering. + */ + ASSERT3P(ri->zri_cleanup, ==, NULL); + + ri->zri_cleanup = cleanfunc; + ri->zri_cleanup_arg = cleanarg; +} + +void +zcp_clear_cleanup(lua_State *state) +{ + zcp_run_info_t *ri = zcp_run_info(state); + + ri->zri_cleanup = NULL; + ri->zri_cleanup_arg = NULL; +} + +/* + * If it exists, execute the currently set cleanup function then unregister it. + */ +void +zcp_cleanup(lua_State *state) +{ + zcp_run_info_t *ri = zcp_run_info(state); + + if (ri->zri_cleanup != NULL) { + ri->zri_cleanup(ri->zri_cleanup_arg); + zcp_clear_cleanup(state); + } +} + +#define ZCP_NVLIST_MAX_DEPTH 20 + +/* + * Convert the lua table at the given index on the Lua stack to an nvlist + * and return it. + * + * If the table can not be converted for any reason, NULL is returned and + * an error message is pushed onto the Lua stack. + */ +static nvlist_t * +zcp_table_to_nvlist(lua_State *state, int index, int depth) +{ + nvlist_t *nvl; + /* + * Converting a Lua table to an nvlist with key uniqueness checking is + * O(n^2) in the number of keys in the nvlist, which can take a long + * time when we return a large table from a channel program. + * Furthermore, Lua's table interface *almost* guarantees unique keys + * on its own (details below). Therefore, we don't use fnvlist_alloc() + * here to avoid the built-in uniqueness checking. + * + * The *almost* is because it's possible to have key collisions between + * e.g. the string "1" and the number 1, or the string "true" and the + * boolean true, so we explicitly check that when we're looking at a + * key which is an integer / boolean or a string that can be parsed as + * one of those types. In the worst case this could still devolve into + * O(n^2), so we only start doing these checks on boolean/integer keys + * once we've seen a string key which fits this weird usage pattern. + * + * Ultimately, we still want callers to know that the keys in this + * nvlist are unique, so before we return this we set the nvlist's + * flags to reflect that. + */ + VERIFY0(nvlist_alloc(&nvl, 0, KM_SLEEP)); + + /* + * Push an empty stack slot where lua_next() will store each + * table key. + */ + lua_pushnil(state); + boolean_t saw_str_could_collide = B_FALSE; + while (lua_next(state, index) != 0) { + /* + * The next key-value pair from the table at index is + * now on the stack, with the key at stack slot -2 and + * the value at slot -1. + */ + int err = 0; + char buf[32]; + const char *key = NULL; + boolean_t key_could_collide = B_FALSE; + + switch (lua_type(state, -2)) { + case LUA_TSTRING: + key = lua_tostring(state, -2); + + /* check if this could collide with a number or bool */ + long long tmp; + int parselen; + if ((sscanf(key, "%lld%n", &tmp, &parselen) > 0 && + parselen == strlen(key)) || + strcmp(key, "true") == 0 || + strcmp(key, "false") == 0) { + key_could_collide = B_TRUE; + saw_str_could_collide = B_TRUE; + } + break; + case LUA_TBOOLEAN: + key = (lua_toboolean(state, -2) == B_TRUE ? + "true" : "false"); + if (saw_str_could_collide) { + key_could_collide = B_TRUE; + } + break; + case LUA_TNUMBER: + VERIFY3U(sizeof (buf), >, + snprintf(buf, sizeof (buf), "%lld", + (longlong_t)lua_tonumber(state, -2))); + key = buf; + if (saw_str_could_collide) { + key_could_collide = B_TRUE; + } + break; + default: + fnvlist_free(nvl); + (void) lua_pushfstring(state, "Invalid key " + "type '%s' in table", + lua_typename(state, lua_type(state, -2))); + return (NULL); + } + /* + * Check for type-mismatched key collisions, and throw an error. + */ + if (key_could_collide && nvlist_exists(nvl, key)) { + fnvlist_free(nvl); + (void) lua_pushfstring(state, "Collision of " + "key '%s' in table", key); + return (NULL); + } + /* + * Recursively convert the table value and insert into + * the new nvlist with the parsed key. To prevent + * stack overflow on circular or heavily nested tables, + * we track the current nvlist depth. + */ + if (depth >= ZCP_NVLIST_MAX_DEPTH) { + fnvlist_free(nvl); + (void) lua_pushfstring(state, "Maximum table " + "depth (%d) exceeded for table", + ZCP_NVLIST_MAX_DEPTH); + return (NULL); + } + err = zcp_lua_to_nvlist_impl(state, -1, nvl, key, + depth + 1); + if (err != 0) { + fnvlist_free(nvl); + /* + * Error message has been pushed to the lua + * stack by the recursive call. + */ + return (NULL); + } + /* + * Pop the value pushed by lua_next(). + */ + lua_pop(state, 1); + } + + /* + * Mark the nvlist as having unique keys. This is a little ugly, but we + * ensured above that there are no duplicate keys in the nvlist. + */ + nvl->nvl_nvflag |= NV_UNIQUE_NAME; + + return (nvl); +} + +/* + * Convert a value from the given index into the lua stack to an nvpair, adding + * it to an nvlist with the given key. + * + * Values are converted as follows: + * + * string -> string + * number -> int64 + * boolean -> boolean + * nil -> boolean (no value) + * + * Lua tables are converted to nvlists and then inserted. The table's keys + * are converted to strings then used as keys in the nvlist to store each table + * element. Keys are converted as follows: + * + * string -> no change + * number -> "%lld" + * boolean -> "true" | "false" + * nil -> error + * + * In the case of a key collision, an error is thrown. + * + * If an error is encountered, a nonzero error code is returned, and an error + * string will be pushed onto the Lua stack. + */ +static int +zcp_lua_to_nvlist_impl(lua_State *state, int index, nvlist_t *nvl, + const char *key, int depth) +{ + /* + * Verify that we have enough remaining space in the lua stack to parse + * a key-value pair and push an error. + */ + if (!lua_checkstack(state, 3)) { + (void) lua_pushstring(state, "Lua stack overflow"); + return (1); + } + + index = lua_absindex(state, index); + + switch (lua_type(state, index)) { + case LUA_TNIL: + fnvlist_add_boolean(nvl, key); + break; + case LUA_TBOOLEAN: + fnvlist_add_boolean_value(nvl, key, + lua_toboolean(state, index)); + break; + case LUA_TNUMBER: + fnvlist_add_int64(nvl, key, lua_tonumber(state, index)); + break; + case LUA_TSTRING: + fnvlist_add_string(nvl, key, lua_tostring(state, index)); + break; + case LUA_TTABLE: { + nvlist_t *value_nvl = zcp_table_to_nvlist(state, index, depth); + if (value_nvl == NULL) + return (EINVAL); + + fnvlist_add_nvlist(nvl, key, value_nvl); + fnvlist_free(value_nvl); + break; + } + default: + (void) lua_pushfstring(state, + "Invalid value type '%s' for key '%s'", + lua_typename(state, lua_type(state, index)), key); + return (EINVAL); + } + + return (0); +} + +/* + * Convert a lua value to an nvpair, adding it to an nvlist with the given key. + */ +void +zcp_lua_to_nvlist(lua_State *state, int index, nvlist_t *nvl, const char *key) +{ + /* + * On error, zcp_lua_to_nvlist_impl pushes an error string onto the Lua + * stack before returning with a nonzero error code. If an error is + * returned, throw a fatal lua error with the given string. + */ + if (zcp_lua_to_nvlist_impl(state, index, nvl, key, 0) != 0) + (void) lua_error(state); +} + +int +zcp_lua_to_nvlist_helper(lua_State *state) +{ + nvlist_t *nv = (nvlist_t *)lua_touserdata(state, 2); + const char *key = (const char *)lua_touserdata(state, 1); + zcp_lua_to_nvlist(state, 3, nv, key); + return (0); +} + +void +zcp_convert_return_values(lua_State *state, nvlist_t *nvl, + const char *key, zcp_eval_arg_t *evalargs) +{ + int err; + lua_pushcfunction(state, zcp_lua_to_nvlist_helper); + lua_pushlightuserdata(state, (char *)key); + lua_pushlightuserdata(state, nvl); + lua_pushvalue(state, 1); + lua_remove(state, 1); + err = lua_pcall(state, 3, 0, 0); /* zcp_lua_to_nvlist_helper */ + if (err != 0) { + zcp_lua_to_nvlist(state, 1, nvl, ZCP_RET_ERROR); + evalargs->ea_result = SET_ERROR(ECHRNG); + } +} + +/* + * Push a Lua table representing nvl onto the stack. If it can't be + * converted, return EINVAL, fill in errbuf, and push nothing. errbuf may + * be specified as NULL, in which case no error string will be output. + * + * Most nvlists are converted as simple key->value Lua tables, but we make + * an exception for the case where all nvlist entries are BOOLEANs (a string + * key without a value). In Lua, a table key pointing to a value of Nil + * (no value) is equivalent to the key not existing, so a BOOLEAN nvlist + * entry can't be directly converted to a Lua table entry. Nvlists of entirely + * BOOLEAN entries are frequently used to pass around lists of datasets, so for + * convenience we check for this case, and convert it to a simple Lua array of + * strings. + */ +int +zcp_nvlist_to_lua(lua_State *state, nvlist_t *nvl, + char *errbuf, int errbuf_len) +{ + nvpair_t *pair; + lua_newtable(state); + boolean_t has_values = B_FALSE; + /* + * If the list doesn't have any values, just convert it to a string + * array. + */ + for (pair = nvlist_next_nvpair(nvl, NULL); + pair != NULL; pair = nvlist_next_nvpair(nvl, pair)) { + if (nvpair_type(pair) != DATA_TYPE_BOOLEAN) { + has_values = B_TRUE; + break; + } + } + if (!has_values) { + int i = 1; + for (pair = nvlist_next_nvpair(nvl, NULL); + pair != NULL; pair = nvlist_next_nvpair(nvl, pair)) { + (void) lua_pushinteger(state, i); + (void) lua_pushstring(state, nvpair_name(pair)); + (void) lua_settable(state, -3); + i++; + } + } else { + for (pair = nvlist_next_nvpair(nvl, NULL); + pair != NULL; pair = nvlist_next_nvpair(nvl, pair)) { + int err = zcp_nvpair_value_to_lua(state, pair, + errbuf, errbuf_len); + if (err != 0) { + lua_pop(state, 1); + return (err); + } + (void) lua_setfield(state, -2, nvpair_name(pair)); + } + } + return (0); +} + +/* + * Push a Lua object representing the value of "pair" onto the stack. + * + * Only understands boolean_value, string, int64, nvlist, + * string_array, and int64_array type values. For other + * types, returns EINVAL, fills in errbuf, and pushes nothing. + */ +static int +zcp_nvpair_value_to_lua(lua_State *state, nvpair_t *pair, + char *errbuf, int errbuf_len) +{ + int err = 0; + + if (pair == NULL) { + lua_pushnil(state); + return (0); + } + + switch (nvpair_type(pair)) { + case DATA_TYPE_BOOLEAN_VALUE: + (void) lua_pushboolean(state, + fnvpair_value_boolean_value(pair)); + break; + case DATA_TYPE_STRING: + (void) lua_pushstring(state, fnvpair_value_string(pair)); + break; + case DATA_TYPE_INT64: + (void) lua_pushinteger(state, fnvpair_value_int64(pair)); + break; + case DATA_TYPE_NVLIST: + err = zcp_nvlist_to_lua(state, + fnvpair_value_nvlist(pair), errbuf, errbuf_len); + break; + case DATA_TYPE_STRING_ARRAY: { + char **strarr; + uint_t nelem; + (void) nvpair_value_string_array(pair, &strarr, &nelem); + lua_newtable(state); + for (int i = 0; i < nelem; i++) { + (void) lua_pushinteger(state, i + 1); + (void) lua_pushstring(state, strarr[i]); + (void) lua_settable(state, -3); + } + break; + } + case DATA_TYPE_UINT64_ARRAY: { + uint64_t *intarr; + uint_t nelem; + (void) nvpair_value_uint64_array(pair, &intarr, &nelem); + lua_newtable(state); + for (int i = 0; i < nelem; i++) { + (void) lua_pushinteger(state, i + 1); + (void) lua_pushinteger(state, intarr[i]); + (void) lua_settable(state, -3); + } + break; + } + case DATA_TYPE_INT64_ARRAY: { + int64_t *intarr; + uint_t nelem; + (void) nvpair_value_int64_array(pair, &intarr, &nelem); + lua_newtable(state); + for (int i = 0; i < nelem; i++) { + (void) lua_pushinteger(state, i + 1); + (void) lua_pushinteger(state, intarr[i]); + (void) lua_settable(state, -3); + } + break; + } + default: { + if (errbuf != NULL) { + (void) snprintf(errbuf, errbuf_len, + "Unhandled nvpair type %d for key '%s'", + nvpair_type(pair), nvpair_name(pair)); + } + return (EINVAL); + } + } + return (err); +} + +int +zcp_dataset_hold_error(lua_State *state, dsl_pool_t *dp, const char *dsname, + int error) +{ + if (error == ENOENT) { + (void) zcp_argerror(state, 1, "no such dataset '%s'", dsname); + return (NULL); /* not reached; zcp_argerror will longjmp */ + } else if (error == EXDEV) { + (void) zcp_argerror(state, 1, + "dataset '%s' is not in the target pool '%s'", + dsname, spa_name(dp->dp_spa)); + return (NULL); /* not reached; zcp_argerror will longjmp */ + } else if (error == EIO) { + (void) luaL_error(state, + "I/O error while accessing dataset '%s'", dsname); + return (NULL); /* not reached; luaL_error will longjmp */ + } else if (error != 0) { + (void) luaL_error(state, + "unexpected error %d while accessing dataset '%s'", + error, dsname); + return (NULL); /* not reached; luaL_error will longjmp */ + } + return (NULL); +} + +/* + * Note: will longjmp (via lua_error()) on error. + * Assumes that the dsname is argument #1 (for error reporting purposes). + */ +dsl_dataset_t * +zcp_dataset_hold(lua_State *state, dsl_pool_t *dp, const char *dsname, + void *tag) +{ + dsl_dataset_t *ds; + int error = dsl_dataset_hold(dp, dsname, tag, &ds); + (void) zcp_dataset_hold_error(state, dp, dsname, error); + return (ds); +} + +static int zcp_debug(lua_State *); +static zcp_lib_info_t zcp_debug_info = { + .name = "debug", + .func = zcp_debug, + .pargs = { + { .za_name = "debug string", .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {NULL, NULL} + } +}; + +static int +zcp_debug(lua_State *state) +{ + const char *dbgstring; + zcp_run_info_t *ri = zcp_run_info(state); + zcp_lib_info_t *libinfo = &zcp_debug_info; + + zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs); + + dbgstring = lua_tostring(state, 1); + + zfs_dbgmsg("txg %lld ZCP: %s", ri->zri_tx->tx_txg, dbgstring); + + return (0); +} + +static int zcp_exists(lua_State *); +static zcp_lib_info_t zcp_exists_info = { + .name = "exists", + .func = zcp_exists, + .pargs = { + { .za_name = "dataset", .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {NULL, NULL} + } +}; + +static int +zcp_exists(lua_State *state) +{ + zcp_run_info_t *ri = zcp_run_info(state); + dsl_pool_t *dp = ri->zri_pool; + zcp_lib_info_t *libinfo = &zcp_exists_info; + + zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs); + + const char *dsname = lua_tostring(state, 1); + + dsl_dataset_t *ds; + int error = dsl_dataset_hold(dp, dsname, FTAG, &ds); + if (error == 0) { + dsl_dataset_rele(ds, FTAG); + lua_pushboolean(state, B_TRUE); + } else if (error == ENOENT) { + lua_pushboolean(state, B_FALSE); + } else if (error == EXDEV) { + return (luaL_error(state, "dataset '%s' is not in the " + "target pool", dsname)); + } else if (error == EIO) { + return (luaL_error(state, "I/O error opening dataset '%s'", + dsname)); + } else if (error != 0) { + return (luaL_error(state, "unexpected error %d", error)); + } + + return (0); +} + +/* + * Allocate/realloc/free a buffer for the lua interpreter. + * + * When nsize is 0, behaves as free() and returns NULL. + * + * If ptr is NULL, behaves as malloc() and returns an allocated buffer of size + * at least nsize. + * + * Otherwise, behaves as realloc(), changing the allocation from osize to nsize. + * Shrinking the buffer size never fails. + * + * The original allocated buffer size is stored as a uint64 at the beginning of + * the buffer to avoid actually reallocating when shrinking a buffer, since lua + * requires that this operation never fail. + */ +static void * +zcp_lua_alloc(void *ud, void *ptr, size_t osize, size_t nsize) +{ + zcp_alloc_arg_t *allocargs = ud; + int flags = (allocargs->aa_must_succeed) ? + KM_SLEEP : (KM_NOSLEEP | KM_NORMALPRI); + + if (nsize == 0) { + if (ptr != NULL) { + int64_t *allocbuf = (int64_t *)ptr - 1; + int64_t allocsize = *allocbuf; + ASSERT3S(allocsize, >, 0); + ASSERT3S(allocargs->aa_alloc_remaining + allocsize, <=, + allocargs->aa_alloc_limit); + allocargs->aa_alloc_remaining += allocsize; + kmem_free(allocbuf, allocsize); + } + return (NULL); + } else if (ptr == NULL) { + int64_t *allocbuf; + int64_t allocsize = nsize + sizeof (int64_t); + + if (!allocargs->aa_must_succeed && + (allocsize <= 0 || + allocsize > allocargs->aa_alloc_remaining)) { + return (NULL); + } + + allocbuf = kmem_alloc(allocsize, flags); + if (allocbuf == NULL) { + return (NULL); + } + allocargs->aa_alloc_remaining -= allocsize; + + *allocbuf = allocsize; + return (allocbuf + 1); + } else if (nsize <= osize) { + /* + * If shrinking the buffer, lua requires that the reallocation + * never fail. + */ + return (ptr); + } else { + ASSERT3U(nsize, >, osize); + + uint64_t *luabuf = zcp_lua_alloc(ud, NULL, 0, nsize); + if (luabuf == NULL) { + return (NULL); + } + (void) memcpy(luabuf, ptr, osize); + VERIFY3P(zcp_lua_alloc(ud, ptr, osize, 0), ==, NULL); + return (luabuf); + } +} + +/* ARGSUSED */ +static void +zcp_lua_counthook(lua_State *state, lua_Debug *ar) +{ + /* + * If we're called, check how many instructions the channel program has + * executed so far, and compare against the limit. + */ + lua_getfield(state, LUA_REGISTRYINDEX, ZCP_RUN_INFO_KEY); + zcp_run_info_t *ri = lua_touserdata(state, -1); + + ri->zri_curinstrs += zfs_lua_check_instrlimit_interval; + if (ri->zri_maxinstrs != 0 && ri->zri_curinstrs > ri->zri_maxinstrs) { + ri->zri_timed_out = B_TRUE; + (void) lua_pushstring(state, + "Channel program timed out."); + (void) lua_error(state); + } +} + +static int +zcp_panic_cb(lua_State *state) +{ + panic("unprotected error in call to Lua API (%s)\n", + lua_tostring(state, -1)); + return (0); +} + +static void +zcp_eval_sync(void *arg, dmu_tx_t *tx) +{ + int err; + zcp_run_info_t ri; + zcp_eval_arg_t *evalargs = arg; + lua_State *state = evalargs->ea_state; + + /* + * Open context should have setup the stack to contain: + * 1: Error handler callback + * 2: Script to run (converted to a Lua function) + * 3: nvlist input to function (converted to Lua table or nil) + */ + VERIFY3U(3, ==, lua_gettop(state)); + + /* + * Store the zcp_run_info_t struct for this run in the Lua registry. + * Registry entries are not directly accessible by the Lua scripts but + * can be accessed by our callbacks. + */ + ri.zri_space_used = 0; + ri.zri_pool = dmu_tx_pool(tx); + ri.zri_cred = evalargs->ea_cred; + ri.zri_tx = tx; + ri.zri_timed_out = B_FALSE; + ri.zri_cleanup = NULL; + ri.zri_cleanup_arg = NULL; + ri.zri_curinstrs = 0; + ri.zri_maxinstrs = evalargs->ea_instrlimit; + + lua_pushlightuserdata(state, &ri); + lua_setfield(state, LUA_REGISTRYINDEX, ZCP_RUN_INFO_KEY); + VERIFY3U(3, ==, lua_gettop(state)); + + /* + * Tell the Lua interpreter to call our handler every count + * instructions. Channel programs that execute too many instructions + * should die with ETIME. + */ + (void) lua_sethook(state, zcp_lua_counthook, LUA_MASKCOUNT, + zfs_lua_check_instrlimit_interval); + + /* + * Tell the Lua memory allocator to stop using KM_SLEEP before handing + * off control to the channel program. Channel programs that use too + * much memory should die with ENOSPC. + */ + evalargs->ea_allocargs->aa_must_succeed = B_FALSE; + + /* + * Call the Lua function that open-context passed us. This pops the + * function and its input from the stack and pushes any return + * or error values. + */ + err = lua_pcall(state, 1, LUA_MULTRET, 1); + + /* + * Let Lua use KM_SLEEP while we interpret the return values. + */ + evalargs->ea_allocargs->aa_must_succeed = B_TRUE; + + /* + * Remove the error handler callback from the stack. At this point, + * if there is a cleanup function registered, then it was registered + * but never run or removed, which should never occur. + */ + ASSERT3P(ri.zri_cleanup, ==, NULL); + lua_remove(state, 1); + + switch (err) { + case LUA_OK: { + /* + * Lua supports returning multiple values in a single return + * statement. Return values will have been pushed onto the + * stack: + * 1: Return value 1 + * 2: Return value 2 + * 3: etc... + * To simplify the process of retrieving a return value from a + * channel program, we disallow returning more than one value + * to ZFS from the Lua script, yielding a singleton return + * nvlist of the form { "return": Return value 1 }. + */ + int return_count = lua_gettop(state); + + if (return_count == 1) { + evalargs->ea_result = 0; + zcp_convert_return_values(state, evalargs->ea_outnvl, + ZCP_RET_RETURN, evalargs); + } else if (return_count > 1) { + evalargs->ea_result = SET_ERROR(ECHRNG); + (void) lua_pushfstring(state, "Multiple return " + "values not supported"); + zcp_convert_return_values(state, evalargs->ea_outnvl, + ZCP_RET_ERROR, evalargs); + } + break; + } + case LUA_ERRRUN: + case LUA_ERRGCMM: { + /* + * The channel program encountered a fatal error within the + * script, such as failing an assertion, or calling a function + * with incompatible arguments. The error value and the + * traceback generated by zcp_error_handler() should be on the + * stack. + */ + VERIFY3U(1, ==, lua_gettop(state)); + if (ri.zri_timed_out) { + evalargs->ea_result = SET_ERROR(ETIME); + } else { + evalargs->ea_result = SET_ERROR(ECHRNG); + } + + zcp_convert_return_values(state, evalargs->ea_outnvl, + ZCP_RET_ERROR, evalargs); + break; + } + case LUA_ERRERR: { + /* + * The channel program encountered a fatal error within the + * script, and we encountered another error while trying to + * compute the traceback in zcp_error_handler(). We can only + * return the error message. + */ + VERIFY3U(1, ==, lua_gettop(state)); + if (ri.zri_timed_out) { + evalargs->ea_result = SET_ERROR(ETIME); + } else { + evalargs->ea_result = SET_ERROR(ECHRNG); + } + + zcp_convert_return_values(state, evalargs->ea_outnvl, + ZCP_RET_ERROR, evalargs); + break; + } + case LUA_ERRMEM: + /* + * Lua ran out of memory while running the channel program. + * There's not much we can do. + */ + evalargs->ea_result = SET_ERROR(ENOSPC); + break; + default: + VERIFY0(err); + } +} + +int +zcp_eval(const char *poolname, const char *program, uint64_t instrlimit, + uint64_t memlimit, nvpair_t *nvarg, nvlist_t *outnvl) +{ + int err; + lua_State *state; + zcp_eval_arg_t evalargs; + + if (instrlimit > zfs_lua_max_instrlimit) + return (SET_ERROR(EINVAL)); + if (memlimit == 0 || memlimit > zfs_lua_max_memlimit) + return (SET_ERROR(EINVAL)); + + zcp_alloc_arg_t allocargs = { + .aa_must_succeed = B_TRUE, + .aa_alloc_remaining = (int64_t)memlimit, + .aa_alloc_limit = (int64_t)memlimit, + }; + + /* + * Creates a Lua state with a memory allocator that uses KM_SLEEP. + * This should never fail. + */ + state = lua_newstate(zcp_lua_alloc, &allocargs); + VERIFY(state != NULL); + (void) lua_atpanic(state, zcp_panic_cb); + + /* + * Load core Lua libraries we want access to. + */ + VERIFY3U(1, ==, luaopen_base(state)); + lua_pop(state, 1); + VERIFY3U(1, ==, luaopen_coroutine(state)); + lua_setglobal(state, LUA_COLIBNAME); + VERIFY0(lua_gettop(state)); + VERIFY3U(1, ==, luaopen_string(state)); + lua_setglobal(state, LUA_STRLIBNAME); + VERIFY0(lua_gettop(state)); + VERIFY3U(1, ==, luaopen_table(state)); + lua_setglobal(state, LUA_TABLIBNAME); + VERIFY0(lua_gettop(state)); + + /* + * Load globally visible variables such as errno aliases. + */ + zcp_load_globals(state); + VERIFY0(lua_gettop(state)); + + /* + * Load ZFS-specific modules. + */ + lua_newtable(state); + VERIFY3U(1, ==, zcp_load_list_lib(state)); + lua_setfield(state, -2, "list"); + VERIFY3U(1, ==, zcp_load_synctask_lib(state, B_FALSE)); + lua_setfield(state, -2, "check"); + VERIFY3U(1, ==, zcp_load_synctask_lib(state, B_TRUE)); + lua_setfield(state, -2, "sync"); + VERIFY3U(1, ==, zcp_load_get_lib(state)); + lua_pushcclosure(state, zcp_debug_info.func, 0); + lua_setfield(state, -2, zcp_debug_info.name); + lua_pushcclosure(state, zcp_exists_info.func, 0); + lua_setfield(state, -2, zcp_exists_info.name); + lua_setglobal(state, "zfs"); + VERIFY0(lua_gettop(state)); + + /* + * Push the error-callback that calculates Lua stack traces on + * unexpected failures. + */ + lua_pushcfunction(state, zcp_error_handler); + VERIFY3U(1, ==, lua_gettop(state)); + + /* + * Load the actual script as a function onto the stack as text ("t"). + * The only valid error condition is a syntax error in the script. + * ERRMEM should not be possible because our allocator is using + * KM_SLEEP. ERRGCMM should not be possible because we have not added + * any objects with __gc metamethods to the interpreter that could + * fail. + */ + err = luaL_loadbufferx(state, program, strlen(program), + "channel program", "t"); + if (err == LUA_ERRSYNTAX) { + fnvlist_add_string(outnvl, ZCP_RET_ERROR, + lua_tostring(state, -1)); + lua_close(state); + return (SET_ERROR(EINVAL)); + } + VERIFY0(err); + VERIFY3U(2, ==, lua_gettop(state)); + + /* + * Convert the input nvlist to a Lua object and put it on top of the + * stack. + */ + char errmsg[128]; + err = zcp_nvpair_value_to_lua(state, nvarg, + errmsg, sizeof (errmsg)); + if (err != 0) { + fnvlist_add_string(outnvl, ZCP_RET_ERROR, errmsg); + lua_close(state); + return (SET_ERROR(EINVAL)); + } + VERIFY3U(3, ==, lua_gettop(state)); + + evalargs.ea_state = state; + evalargs.ea_allocargs = &allocargs; + evalargs.ea_instrlimit = instrlimit; + evalargs.ea_cred = CRED(); + evalargs.ea_outnvl = outnvl; + evalargs.ea_result = 0; + + VERIFY0(dsl_sync_task(poolname, zcp_eval_check, + zcp_eval_sync, &evalargs, 0, ZFS_SPACE_CHECK_NONE)); + + lua_close(state); + + return (evalargs.ea_result); +} + +/* + * Retrieve metadata about the currently running channel program. + */ +zcp_run_info_t * +zcp_run_info(lua_State *state) +{ + zcp_run_info_t *ri; + + lua_getfield(state, LUA_REGISTRYINDEX, ZCP_RUN_INFO_KEY); + ri = lua_touserdata(state, -1); + lua_pop(state, 1); + return (ri); +} + +/* + * Argument Parsing + * ================ + * + * The Lua language allows methods to be called with any number + * of arguments of any type. When calling back into ZFS we need to sanitize + * arguments from channel programs to make sure unexpected arguments or + * arguments of the wrong type result in clear error messages. To do this + * in a uniform way all callbacks from channel programs should use the + * zcp_parse_args() function to interpret inputs. + * + * Positional vs Keyword Arguments + * =============================== + * + * Every callback function takes a fixed set of required positional arguments + * and optional keyword arguments. For example, the destroy function takes + * a single positional string argument (the name of the dataset to destroy) + * and an optional "defer" keyword boolean argument. When calling lua functions + * with parentheses, only positional arguments can be used: + * + * zfs.sync.snapshot("rpool@snap") + * + * To use keyword arguments functions should be called with a single argument + * that is a lua table containing mappings of integer -> positional arguments + * and string -> keyword arguments: + * + * zfs.sync.snapshot({1="rpool@snap", defer=true}) + * + * The lua language allows curly braces to be used in place of parenthesis as + * syntactic sugar for this calling convention: + * + * zfs.sync.snapshot{"rpool@snap", defer=true} + */ + +/* + * Throw an error and print the given arguments. If there are too many + * arguments to fit in the output buffer, only the error format string is + * output. + */ +static void +zcp_args_error(lua_State *state, const char *fname, const zcp_arg_t *pargs, + const zcp_arg_t *kwargs, const char *fmt, ...) +{ + int i; + char errmsg[512]; + size_t len = sizeof (errmsg); + size_t msglen = 0; + va_list argp; + + va_start(argp, fmt); + VERIFY3U(len, >, vsnprintf(errmsg, len, fmt, argp)); + va_end(argp); + + /* + * Calculate the total length of the final string, including extra + * formatting characters. If the argument dump would be too large, + * only print the error string. + */ + msglen = strlen(errmsg); + msglen += strlen(fname) + 4; /* : + {} + null terminator */ + for (i = 0; pargs[i].za_name != NULL; i++) { + msglen += strlen(pargs[i].za_name); + msglen += strlen(lua_typename(state, pargs[i].za_lua_type)); + if (pargs[i + 1].za_name != NULL || kwargs[0].za_name != NULL) + msglen += 5; /* < + ( + )> + , */ + else + msglen += 4; /* < + ( + )> */ + } + for (i = 0; kwargs[i].za_name != NULL; i++) { + msglen += strlen(kwargs[i].za_name); + msglen += strlen(lua_typename(state, kwargs[i].za_lua_type)); + if (kwargs[i + 1].za_name != NULL) + msglen += 4; /* =( + ) + , */ + else + msglen += 3; /* =( + ) */ + } + + if (msglen >= len) + (void) luaL_error(state, errmsg); + + VERIFY3U(len, >, strlcat(errmsg, ": ", len)); + VERIFY3U(len, >, strlcat(errmsg, fname, len)); + VERIFY3U(len, >, strlcat(errmsg, "{", len)); + for (i = 0; pargs[i].za_name != NULL; i++) { + VERIFY3U(len, >, strlcat(errmsg, "<", len)); + VERIFY3U(len, >, strlcat(errmsg, pargs[i].za_name, len)); + VERIFY3U(len, >, strlcat(errmsg, "(", len)); + VERIFY3U(len, >, strlcat(errmsg, + lua_typename(state, pargs[i].za_lua_type), len)); + VERIFY3U(len, >, strlcat(errmsg, ")>", len)); + if (pargs[i + 1].za_name != NULL || kwargs[0].za_name != NULL) { + VERIFY3U(len, >, strlcat(errmsg, ", ", len)); + } + } + for (i = 0; kwargs[i].za_name != NULL; i++) { + VERIFY3U(len, >, strlcat(errmsg, kwargs[i].za_name, len)); + VERIFY3U(len, >, strlcat(errmsg, "=(", len)); + VERIFY3U(len, >, strlcat(errmsg, + lua_typename(state, kwargs[i].za_lua_type), len)); + VERIFY3U(len, >, strlcat(errmsg, ")", len)); + if (kwargs[i + 1].za_name != NULL) { + VERIFY3U(len, >, strlcat(errmsg, ", ", len)); + } + } + VERIFY3U(len, >, strlcat(errmsg, "}", len)); + + (void) luaL_error(state, errmsg); + panic("unreachable code"); +} + +static void +zcp_parse_table_args(lua_State *state, const char *fname, + const zcp_arg_t *pargs, const zcp_arg_t *kwargs) +{ + int i; + int type; + + for (i = 0; pargs[i].za_name != NULL; i++) { + /* + * Check the table for this positional argument, leaving it + * on the top of the stack once we finish validating it. + */ + lua_pushinteger(state, i + 1); + lua_gettable(state, 1); + + type = lua_type(state, -1); + if (type == LUA_TNIL) { + zcp_args_error(state, fname, pargs, kwargs, + "too few arguments"); + panic("unreachable code"); + } else if (type != pargs[i].za_lua_type) { + zcp_args_error(state, fname, pargs, kwargs, + "arg %d wrong type (is '%s', expected '%s')", + i + 1, lua_typename(state, type), + lua_typename(state, pargs[i].za_lua_type)); + panic("unreachable code"); + } + + /* + * Remove the positional argument from the table. + */ + lua_pushinteger(state, i + 1); + lua_pushnil(state); + lua_settable(state, 1); + } + + for (i = 0; kwargs[i].za_name != NULL; i++) { + /* + * Check the table for this keyword argument, which may be + * nil if it was omitted. Leave the value on the top of + * the stack after validating it. + */ + lua_getfield(state, 1, kwargs[i].za_name); + + type = lua_type(state, -1); + if (type != LUA_TNIL && type != kwargs[i].za_lua_type) { + zcp_args_error(state, fname, pargs, kwargs, + "kwarg '%s' wrong type (is '%s', expected '%s')", + kwargs[i].za_name, lua_typename(state, type), + lua_typename(state, kwargs[i].za_lua_type)); + panic("unreachable code"); + } + + /* + * Remove the keyword argument from the table. + */ + lua_pushnil(state); + lua_setfield(state, 1, kwargs[i].za_name); + } + + /* + * Any entries remaining in the table are invalid inputs, print + * an error message based on what the entry is. + */ + lua_pushnil(state); + if (lua_next(state, 1)) { + if (lua_isnumber(state, -2) && lua_tointeger(state, -2) > 0) { + zcp_args_error(state, fname, pargs, kwargs, + "too many positional arguments"); + } else if (lua_isstring(state, -2)) { + zcp_args_error(state, fname, pargs, kwargs, + "invalid kwarg '%s'", lua_tostring(state, -2)); + } else { + zcp_args_error(state, fname, pargs, kwargs, + "kwarg keys must be strings"); + } + panic("unreachable code"); + } + + lua_remove(state, 1); +} + +static void +zcp_parse_pos_args(lua_State *state, const char *fname, const zcp_arg_t *pargs, + const zcp_arg_t *kwargs) +{ + int i; + int type; + + for (i = 0; pargs[i].za_name != NULL; i++) { + type = lua_type(state, i + 1); + if (type == LUA_TNONE) { + zcp_args_error(state, fname, pargs, kwargs, + "too few arguments"); + panic("unreachable code"); + } else if (type != pargs[i].za_lua_type) { + zcp_args_error(state, fname, pargs, kwargs, + "arg %d wrong type (is '%s', expected '%s')", + i + 1, lua_typename(state, type), + lua_typename(state, pargs[i].za_lua_type)); + panic("unreachable code"); + } + } + if (lua_gettop(state) != i) { + zcp_args_error(state, fname, pargs, kwargs, + "too many positional arguments"); + panic("unreachable code"); + } + + for (i = 0; kwargs[i].za_name != NULL; i++) { + lua_pushnil(state); + } +} + +/* + * Checks the current Lua stack against an expected set of positional and + * keyword arguments. If the stack does not match the expected arguments + * aborts the current channel program with a useful error message, otherwise + * it re-arranges the stack so that it contains the positional arguments + * followed by the keyword argument values in declaration order. Any missing + * keyword argument will be represented by a nil value on the stack. + * + * If the stack contains exactly one argument of type LUA_TTABLE the curly + * braces calling convention is assumed, otherwise the stack is parsed for + * positional arguments only. + * + * This function should be used by every function callback. It should be called + * before the callback manipulates the Lua stack as it assumes the stack + * represents the function arguments. + */ +void +zcp_parse_args(lua_State *state, const char *fname, const zcp_arg_t *pargs, + const zcp_arg_t *kwargs) +{ + if (lua_gettop(state) == 1 && lua_istable(state, 1)) { + zcp_parse_table_args(state, fname, pargs, kwargs); + } else { + zcp_parse_pos_args(state, fname, pargs, kwargs); + } +} diff --git a/usr/src/uts/common/fs/zfs/zcp_get.c b/usr/src/uts/common/fs/zfs/zcp_get.c new file mode 100644 index 0000000000..bac02ee8e1 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/zcp_get.c @@ -0,0 +1,848 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" + +#include <zfs_prop.h> + +#include <sys/dsl_prop.h> +#include <sys/dsl_synctask.h> +#include <sys/dsl_dataset.h> +#include <sys/dsl_dir.h> +#include <sys/dmu_objset.h> +#include <sys/mntent.h> +#include <sys/sunddi.h> +#include <sys/zap.h> +#include <sys/zcp.h> +#include <sys/zcp_iter.h> +#include <sys/zcp_global.h> +#include <sys/zfs_ioctl.h> +#include <sys/zfs_znode.h> +#include <sys/zvol.h> + +#ifdef _KERNEL +#include <sys/zfs_vfsops.h> +#endif + +static int +get_objset_type(dsl_dataset_t *ds, zfs_type_t *type) +{ + int error; + objset_t *os; + error = dmu_objset_from_ds(ds, &os); + if (error != 0) + return (error); + if (ds->ds_is_snapshot) { + *type = ZFS_TYPE_SNAPSHOT; + } else { + switch (os->os_phys->os_type) { + case DMU_OST_ZFS: + *type = ZFS_TYPE_FILESYSTEM; + break; + case DMU_OST_ZVOL: + *type = ZFS_TYPE_VOLUME; + break; + default: + return (EINVAL); + } + } + return (0); +} + +/* + * Returns the string name of ds's type in str (a buffer which should be + * at least 12 bytes long). + */ +static int +get_objset_type_name(dsl_dataset_t *ds, char *str) +{ + int error; + zfs_type_t type; + error = get_objset_type(ds, &type); + if (error != 0) + return (error); + switch (type) { + case ZFS_TYPE_SNAPSHOT: + (void) strcpy(str, "snapshot"); + break; + case ZFS_TYPE_FILESYSTEM: + (void) strcpy(str, "filesystem"); + break; + case ZFS_TYPE_VOLUME: + (void) strcpy(str, "volume"); + break; + default: + return (EINVAL); + } + return (0); +} + +/* + * Determines the source of a property given its setpoint and + * property type. It pushes the source to the lua stack. + */ +static void +get_prop_src(lua_State *state, const char *setpoint, zfs_prop_t prop) +{ + if (zfs_prop_readonly(prop) || (prop == ZFS_PROP_VERSION)) { + lua_pushnil(state); + } else { + const char *src; + if (strcmp("", setpoint) == 0) { + src = "default"; + } else { + src = setpoint; + } + (void) lua_pushstring(state, src); + } +} + +/* + * Given an error encountered while getting properties, either longjmp's for + * a fatal error or pushes nothing to the stack for a non fatal one. + */ +static int +zcp_handle_error(lua_State *state, const char *dataset_name, + const char *property_name, int error) +{ + ASSERT3S(error, !=, 0); + if (error == ENOENT) { + return (0); + } else if (error == EINVAL) { + return (luaL_error(state, + "property '%s' is not a valid property on dataset '%s'", + property_name, dataset_name)); + } else if (error == EIO) { + return (luaL_error(state, + "I/O error while retrieving property '%s' on dataset '%s'", + property_name, dataset_name)); + } else { + return (luaL_error(state, "unexpected error %d while " + "retrieving property '%s' on dataset '%s'", + error, property_name, dataset_name)); + } +} + +/* + * Look up a user defined property in the zap object. If it exists, push it + * and the setpoint onto the stack, otherwise don't push anything. + */ +static int +zcp_get_user_prop(lua_State *state, dsl_pool_t *dp, const char *dataset_name, + const char *property_name) +{ + int error; + char *buf; + char setpoint[ZFS_MAX_DATASET_NAME_LEN]; + /* + * zcp_dataset_hold will either successfully return the requested + * dataset or throw a lua error and longjmp out of the zfs.get_prop call + * without returning. + */ + dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); + if (ds == NULL) + return (1); /* not reached; zcp_dataset_hold() longjmp'd */ + + buf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); + error = dsl_prop_get_ds(ds, property_name, 1, ZAP_MAXVALUELEN, + buf, setpoint); + dsl_dataset_rele(ds, FTAG); + + if (error != 0) { + kmem_free(buf, ZAP_MAXVALUELEN); + return (zcp_handle_error(state, dataset_name, property_name, + error)); + } + (void) lua_pushstring(state, buf); + (void) lua_pushstring(state, setpoint); + kmem_free(buf, ZAP_MAXVALUELEN); + return (2); +} + +/* + * Check if the property we're looking for is stored in the ds_dir. If so, + * return it in the 'val' argument. Return 0 on success and ENOENT and if + * the property is not present. + */ +static int +get_dsl_dir_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop, + uint64_t *val) +{ + dsl_dir_t *dd = ds->ds_dir; + mutex_enter(&dd->dd_lock); + switch (zfs_prop) { + case ZFS_PROP_USEDSNAP: + *val = dsl_dir_get_usedsnap(dd); + break; + case ZFS_PROP_USEDCHILD: + *val = dsl_dir_get_usedchild(dd); + break; + case ZFS_PROP_USEDDS: + *val = dsl_dir_get_usedds(dd); + break; + case ZFS_PROP_USEDREFRESERV: + *val = dsl_dir_get_usedrefreserv(dd); + break; + case ZFS_PROP_LOGICALUSED: + *val = dsl_dir_get_logicalused(dd); + break; + default: + mutex_exit(&dd->dd_lock); + return (ENOENT); + } + mutex_exit(&dd->dd_lock); + return (0); +} + +/* + * Takes a dataset, a property, a value and that value's setpoint as + * found in the ZAP. Checks if the property has been changed in the vfs. + * If so, val and setpoint will be overwritten with updated content. + * Otherwise, they are left unchanged. + */ +static int +get_temporary_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop, uint64_t *val, + char *setpoint) +{ +#ifndef _KERNEL + return (0); +#else + int error; + zfsvfs_t *zfvp; + vfs_t *vfsp; + objset_t *os; + uint64_t tmp = *val; + + error = dmu_objset_from_ds(ds, &os); + if (error != 0) + return (error); + + error = getzfsvfs_impl(os, &zfvp); + if (error != 0) + return (error); + + vfsp = zfvp->z_vfs; + + switch (zfs_prop) { + case ZFS_PROP_ATIME: + if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) + tmp = 0; + if (vfs_optionisset(vfsp, MNTOPT_ATIME, NULL)) + tmp = 1; + break; + case ZFS_PROP_DEVICES: + if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) + tmp = 0; + if (vfs_optionisset(vfsp, MNTOPT_DEVICES, NULL)) + tmp = 1; + break; + case ZFS_PROP_EXEC: + if (vfs_optionisset(vfsp, MNTOPT_NOEXEC, NULL)) + tmp = 0; + if (vfs_optionisset(vfsp, MNTOPT_EXEC, NULL)) + tmp = 1; + break; + case ZFS_PROP_SETUID: + if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) + tmp = 0; + if (vfs_optionisset(vfsp, MNTOPT_SETUID, NULL)) + tmp = 1; + break; + case ZFS_PROP_READONLY: + if (vfs_optionisset(vfsp, MNTOPT_RW, NULL)) + tmp = 0; + if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) + tmp = 1; + break; + case ZFS_PROP_XATTR: + if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL)) + tmp = 0; + if (vfs_optionisset(vfsp, MNTOPT_XATTR, NULL)) + tmp = 1; + break; + case ZFS_PROP_NBMAND: + if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) + tmp = 0; + if (vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL)) + tmp = 1; + break; + default: + VFS_RELE(vfsp); + return (ENOENT); + } + + VFS_RELE(vfsp); + if (tmp != *val) { + (void) strcpy(setpoint, "temporary"); + *val = tmp; + } + return (0); +#endif +} + +/* + * Check if the property we're looking for is stored at the dsl_dataset or + * dsl_dir level. If so, push the property value and source onto the lua stack + * and return 0. If it is not present or a failure occurs in lookup, return a + * non-zero error value. + */ +static int +get_special_prop(lua_State *state, dsl_dataset_t *ds, const char *dsname, + zfs_prop_t zfs_prop) +{ + int error = 0; + objset_t *os; + uint64_t numval; + char *strval = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); + char setpoint[ZFS_MAX_DATASET_NAME_LEN] = + "Internal error - setpoint not determined"; + zfs_type_t ds_type; + zprop_type_t prop_type = zfs_prop_get_type(zfs_prop); + (void) get_objset_type(ds, &ds_type); + + switch (zfs_prop) { + case ZFS_PROP_REFRATIO: + numval = dsl_get_refratio(ds); + break; + case ZFS_PROP_USED: + numval = dsl_get_used(ds); + break; + case ZFS_PROP_CLONES: { + nvlist_t *clones = fnvlist_alloc(); + error = get_clones_stat_impl(ds, clones); + if (error == 0) { + /* push list to lua stack */ + VERIFY0(zcp_nvlist_to_lua(state, clones, NULL, + NULL)); + /* source */ + (void) lua_pushnil(state); + } + nvlist_free(clones); + kmem_free(strval, ZAP_MAXVALUELEN); + return (error); + } + case ZFS_PROP_COMPRESSRATIO: + numval = dsl_get_compressratio(ds); + break; + case ZFS_PROP_CREATION: + numval = dsl_get_creation(ds); + break; + case ZFS_PROP_REFERENCED: + numval = dsl_get_referenced(ds); + break; + case ZFS_PROP_AVAILABLE: + numval = dsl_get_available(ds); + break; + case ZFS_PROP_LOGICALREFERENCED: + numval = dsl_get_logicalreferenced(ds); + break; + case ZFS_PROP_CREATETXG: + numval = dsl_get_creationtxg(ds); + break; + case ZFS_PROP_GUID: + numval = dsl_get_guid(ds); + break; + case ZFS_PROP_UNIQUE: + numval = dsl_get_unique(ds); + break; + case ZFS_PROP_OBJSETID: + numval = dsl_get_objsetid(ds); + break; + case ZFS_PROP_ORIGIN: + dsl_dir_get_origin(ds->ds_dir, strval); + break; + case ZFS_PROP_USERACCOUNTING: + error = dmu_objset_from_ds(ds, &os); + if (error == 0) + numval = dmu_objset_userspace_present(os); + break; + case ZFS_PROP_WRITTEN: + error = dsl_get_written(ds, &numval); + break; + case ZFS_PROP_TYPE: + error = get_objset_type_name(ds, strval); + break; + case ZFS_PROP_PREV_SNAP: + error = dsl_get_prev_snap(ds, strval); + break; + case ZFS_PROP_NAME: + dsl_dataset_name(ds, strval); + break; + case ZFS_PROP_MOUNTPOINT: + error = dsl_get_mountpoint(ds, dsname, strval, setpoint); + break; + case ZFS_PROP_VERSION: + /* should be a snapshot or filesystem */ + ASSERT(ds_type != ZFS_TYPE_VOLUME); + error = dmu_objset_from_ds(ds, &os); + /* look in the master node for the version */ + if (error == 0) { + error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, + sizeof (numval), 1, &numval); + } + break; + case ZFS_PROP_DEFER_DESTROY: + numval = dsl_get_defer_destroy(ds); + break; + case ZFS_PROP_USERREFS: + numval = dsl_get_userrefs(ds); + break; + case ZFS_PROP_FILESYSTEM_COUNT: + error = dsl_dir_get_filesystem_count(ds->ds_dir, &numval); + (void) strcpy(setpoint, ""); + break; + case ZFS_PROP_SNAPSHOT_COUNT: + error = dsl_dir_get_snapshot_count(ds->ds_dir, &numval); + (void) strcpy(setpoint, ""); + break; + case ZFS_PROP_NUMCLONES: + numval = dsl_get_numclones(ds); + break; + case ZFS_PROP_INCONSISTENT: + numval = dsl_get_inconsistent(ds); + break; + case ZFS_PROP_RECEIVE_RESUME_TOKEN: + VERIFY3U(strlcpy(strval, get_receive_resume_stats_impl(ds), + ZAP_MAXVALUELEN), <, ZAP_MAXVALUELEN); + if (strcmp(strval, "") == 0) { + VERIFY3U(strlcpy(strval, get_child_receive_stats(ds), + ZAP_MAXVALUELEN), <, ZAP_MAXVALUELEN); + if (strcmp(strval, "") == 0) + error = ENOENT; + } + break; + case ZFS_PROP_VOLSIZE: + ASSERT(ds_type == ZFS_TYPE_VOLUME); + error = dmu_objset_from_ds(ds, &os); + if (error == 0) { + error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", + sizeof (numval), 1, &numval); + } + if (error == 0) + (void) strcpy(setpoint, dsname); + + break; + case ZFS_PROP_VOLBLOCKSIZE: { + ASSERT(ds_type == ZFS_TYPE_VOLUME); + dmu_object_info_t doi; + error = dmu_objset_from_ds(ds, &os); + if (error == 0) { + error = dmu_object_info(os, ZVOL_OBJ, &doi); + if (error == 0) + numval = doi.doi_data_block_size; + } + break; + } + default: + /* Did not match these props, check in the dsl_dir */ + error = get_dsl_dir_prop(ds, zfs_prop, &numval); + } + if (error != 0) { + kmem_free(strval, ZAP_MAXVALUELEN); + return (error); + } + + switch (prop_type) { + case PROP_TYPE_NUMBER: { + (void) lua_pushnumber(state, numval); + break; + } + case PROP_TYPE_STRING: { + (void) lua_pushstring(state, strval); + break; + } + case PROP_TYPE_INDEX: { + const char *propval; + error = zfs_prop_index_to_string(zfs_prop, numval, &propval); + if (error != 0) { + kmem_free(strval, ZAP_MAXVALUELEN); + return (error); + } + (void) lua_pushstring(state, propval); + break; + } + } + kmem_free(strval, ZAP_MAXVALUELEN); + + /* Push the source to the stack */ + get_prop_src(state, setpoint, zfs_prop); + return (0); +} + +/* + * Look up a property and its source in the zap object. If the value is + * present and successfully retrieved, push the value and source on the + * lua stack and return 0. On failure, return a non-zero error value. + */ +static int +get_zap_prop(lua_State *state, dsl_dataset_t *ds, zfs_prop_t zfs_prop) +{ + int error = 0; + char setpoint[ZFS_MAX_DATASET_NAME_LEN]; + char *strval = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); + uint64_t numval; + const char *prop_name = zfs_prop_to_name(zfs_prop); + zprop_type_t prop_type = zfs_prop_get_type(zfs_prop); + + if (prop_type == PROP_TYPE_STRING) { + /* Push value to lua stack */ + error = dsl_prop_get_ds(ds, prop_name, 1, + ZAP_MAXVALUELEN, strval, setpoint); + if (error == 0) + (void) lua_pushstring(state, strval); + } else { + error = dsl_prop_get_ds(ds, prop_name, sizeof (numval), + 1, &numval, setpoint); + + /* Fill in temorary value for prop, if applicable */ + (void) get_temporary_prop(ds, zfs_prop, &numval, setpoint); + + /* Push value to lua stack */ + if (prop_type == PROP_TYPE_INDEX) { + const char *propval; + error = zfs_prop_index_to_string(zfs_prop, numval, + &propval); + if (error == 0) + (void) lua_pushstring(state, propval); + } else { + if (error == 0) + (void) lua_pushnumber(state, numval); + } + } + kmem_free(strval, ZAP_MAXVALUELEN); + if (error == 0) + get_prop_src(state, setpoint, zfs_prop); + return (error); +} + +/* + * Determine whether property is valid for a given dataset + */ +boolean_t +prop_valid_for_ds(dsl_dataset_t *ds, zfs_prop_t zfs_prop) +{ + int error; + zfs_type_t zfs_type; + + /* properties not supported */ + if ((zfs_prop == ZFS_PROP_ISCSIOPTIONS) || + (zfs_prop == ZFS_PROP_MOUNTED)) + return (B_FALSE); + + /* if we want the origin prop, ds must be a clone */ + if ((zfs_prop == ZFS_PROP_ORIGIN) && (!dsl_dir_is_clone(ds->ds_dir))) + return (B_FALSE); + + error = get_objset_type(ds, &zfs_type); + if (error != 0) + return (B_FALSE); + return (zfs_prop_valid_for_type(zfs_prop, zfs_type)); +} + +/* + * Look up a given dataset property. On success return 2, the number of + * values pushed to the lua stack (property value and source). On a fatal + * error, longjmp. On a non fatal error push nothing. + */ +static int +zcp_get_system_prop(lua_State *state, dsl_pool_t *dp, const char *dataset_name, + zfs_prop_t zfs_prop) +{ + int error; + /* + * zcp_dataset_hold will either successfully return the requested + * dataset or throw a lua error and longjmp out of the zfs.get_prop call + * without returning. + */ + dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); + if (ds == NULL) + return (1); /* not reached; zcp_dataset_hold() longjmp'd */ + + /* Check that the property is valid for the given dataset */ + const char *prop_name = zfs_prop_to_name(zfs_prop); + if (!prop_valid_for_ds(ds, zfs_prop)) { + dsl_dataset_rele(ds, FTAG); + return (0); + } + + /* Check if the property can be accessed directly */ + error = get_special_prop(state, ds, dataset_name, zfs_prop); + if (error == 0) { + dsl_dataset_rele(ds, FTAG); + /* The value and source have been pushed by get_special_prop */ + return (2); + } + if (error != ENOENT) { + dsl_dataset_rele(ds, FTAG); + return (zcp_handle_error(state, dataset_name, + prop_name, error)); + } + + /* If we were unable to find it, look in the zap object */ + error = get_zap_prop(state, ds, zfs_prop); + dsl_dataset_rele(ds, FTAG); + if (error != 0) { + return (zcp_handle_error(state, dataset_name, + prop_name, error)); + } + /* The value and source have been pushed by get_zap_prop */ + return (2); +} + +static zfs_userquota_prop_t +get_userquota_prop(const char *prop_name) +{ + zfs_userquota_prop_t type; + /* Figure out the property type ({user|group}{quota|used}) */ + for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { + if (strncmp(prop_name, zfs_userquota_prop_prefixes[type], + strlen(zfs_userquota_prop_prefixes[type])) == 0) + break; + } + return (type); +} + +#ifdef _KERNEL +/* + * Given the name of a zfs_userquota_prop, this function determines the + * prop type as well as the numeric group/user ids based on the string + * following the '@' in the property name. On success, returns 0. On failure, + * returns a non-zero error. + * 'domain' must be free'd by caller using strfree() + */ +static int +parse_userquota_prop(const char *prop_name, zfs_userquota_prop_t *type, + char **domain, uint64_t *rid) +{ + char *cp, *end, *domain_val; + + *type = get_userquota_prop(prop_name); + if (*type >= ZFS_NUM_USERQUOTA_PROPS) + return (EINVAL); + + *rid = 0; + cp = strchr(prop_name, '@') + 1; + if (strncmp(cp, "S-1-", 4) == 0) { + /* + * It's a numeric SID (eg "S-1-234-567-89") and we want to + * seperate the domain id and the rid + */ + int domain_len = strrchr(cp, '-') - cp; + domain_val = kmem_alloc(domain_len + 1, KM_SLEEP); + (void) strncpy(domain_val, cp, domain_len); + domain_val[domain_len] = '\0'; + cp += domain_len + 1; + + (void) ddi_strtoll(cp, &end, 10, (longlong_t *)rid); + if (*end != '\0') { + strfree(domain_val); + return (EINVAL); + } + } else { + /* It's only a user/group ID (eg "12345"), just get the rid */ + domain_val = NULL; + (void) ddi_strtoll(cp, &end, 10, (longlong_t *)rid); + if (*end != '\0') + return (EINVAL); + } + *domain = domain_val; + return (0); +} + +/* + * Look up {user|group}{quota|used} property for given dataset. On success + * push the value (quota or used amount) and the setpoint. On failure, push + * a lua error. + */ +static int +zcp_get_userquota_prop(lua_State *state, dsl_pool_t *dp, + const char *dataset_name, const char *prop_name) +{ + zfsvfs_t *zfvp; + zfsvfs_t *zfsvfs; + int error; + zfs_userquota_prop_t type; + char *domain; + uint64_t rid, value; + objset_t *os; + + dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); + if (ds == NULL) + return (1); /* not reached; zcp_dataset_hold() longjmp'd */ + + error = parse_userquota_prop(prop_name, &type, &domain, &rid); + if (error == 0) { + error = dmu_objset_from_ds(ds, &os); + if (error == 0) { + zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP); + error = zfsvfs_create_impl(&zfvp, zfsvfs, os); + if (error == 0) { + error = zfs_userspace_one(zfvp, type, domain, + rid, &value); + zfsvfs_free(zfvp); + } + } + if (domain != NULL) + strfree(domain); + } + dsl_dataset_rele(ds, FTAG); + + if ((value == 0) && ((type == ZFS_PROP_USERQUOTA) || + (type == ZFS_PROP_GROUPQUOTA))) + error = ENOENT; + if (error != 0) { + return (zcp_handle_error(state, dataset_name, + prop_name, error)); + } + + (void) lua_pushnumber(state, value); + (void) lua_pushstring(state, dataset_name); + return (2); +} +#endif + +/* + * Determines the name of the snapshot referenced in the written property + * name. Returns snapshot name in snap_name, a buffer that must be at least + * as large as ZFS_MAX_DATASET_NAME_LEN + */ +static void +parse_written_prop(const char *dataset_name, const char *prop_name, + char *snap_name) +{ + ASSERT(zfs_prop_written(prop_name)); + const char *name = prop_name + ZFS_WRITTEN_PROP_PREFIX_LEN; + if (strchr(name, '@') == NULL) { + (void) sprintf(snap_name, "%s@%s", dataset_name, name); + } else { + (void) strcpy(snap_name, name); + } +} + +/* + * Look up written@ property for given dataset. On success + * push the value and the setpoint. If error is fatal, we will + * longjmp, otherwise push nothing. + */ +static int +zcp_get_written_prop(lua_State *state, dsl_pool_t *dp, + const char *dataset_name, const char *prop_name) +{ + char snap_name[ZFS_MAX_DATASET_NAME_LEN]; + uint64_t used, comp, uncomp; + dsl_dataset_t *old; + int error = 0; + + parse_written_prop(dataset_name, prop_name, snap_name); + dsl_dataset_t *new = zcp_dataset_hold(state, dp, dataset_name, FTAG); + if (new == NULL) + return (1); /* not reached; zcp_dataset_hold() longjmp'd */ + + error = dsl_dataset_hold(dp, snap_name, FTAG, &old); + if (error != 0) { + dsl_dataset_rele(new, FTAG); + return (zcp_dataset_hold_error(state, dp, snap_name, + error)); + } + error = dsl_dataset_space_written(old, new, + &used, &comp, &uncomp); + + dsl_dataset_rele(old, FTAG); + dsl_dataset_rele(new, FTAG); + + if (error != 0) { + return (zcp_handle_error(state, dataset_name, + snap_name, error)); + } + (void) lua_pushnumber(state, used); + (void) lua_pushstring(state, dataset_name); + return (2); +} + +static int zcp_get_prop(lua_State *state); +static zcp_lib_info_t zcp_get_prop_info = { + .name = "get_prop", + .func = zcp_get_prop, + .pargs = { + { .za_name = "dataset", .za_lua_type = LUA_TSTRING}, + { .za_name = "property", .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {NULL, NULL} + } +}; + +static int +zcp_get_prop(lua_State *state) +{ + const char *dataset_name; + const char *property_name; + dsl_pool_t *dp = zcp_run_info(state)->zri_pool; + zcp_lib_info_t *libinfo = &zcp_get_prop_info; + + zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs); + + dataset_name = lua_tostring(state, 1); + property_name = lua_tostring(state, 2); + + /* User defined property */ + if (zfs_prop_user(property_name)) { + return (zcp_get_user_prop(state, dp, + dataset_name, property_name)); + } + /* userspace property */ + if (zfs_prop_userquota(property_name)) { +#ifdef _KERNEL + return (zcp_get_userquota_prop(state, dp, + dataset_name, property_name)); +#else + return (luaL_error(state, + "user quota properties only supported in kernel mode", + property_name)); +#endif + } + /* written@ property */ + if (zfs_prop_written(property_name)) { + return (zcp_get_written_prop(state, dp, + dataset_name, property_name)); + } + + zfs_prop_t zfs_prop = zfs_name_to_prop(property_name); + /* Valid system property */ + if (zfs_prop != ZPROP_INVAL) { + return (zcp_get_system_prop(state, dp, dataset_name, + zfs_prop)); + } + + /* Invalid property name */ + return (luaL_error(state, + "'%s' is not a valid property", property_name)); +} + +int +zcp_load_get_lib(lua_State *state) +{ + lua_pushcclosure(state, zcp_get_prop_info.func, 0); + lua_setfield(state, -2, zcp_get_prop_info.name); + + return (1); +} diff --git a/usr/src/uts/common/fs/zfs/zcp_global.c b/usr/src/uts/common/fs/zfs/zcp_global.c new file mode 100644 index 0000000000..efc1cf92d3 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/zcp_global.c @@ -0,0 +1,84 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +#include <sys/zcp_global.h> + +#include "lua.h" +#include "lauxlib.h" + +typedef struct zcp_errno_global { + const char *zeg_name; + int zeg_errno; +} zcp_errno_global_t; + +static const zcp_errno_global_t errno_globals[] = { + {"EPERM", EPERM}, + {"ENOENT", ENOENT}, + {"ESRCH", ESRCH}, + {"EINTR", EINTR}, + {"EIO", EIO}, + {"ENXIO", ENXIO}, + {"E2BIG", E2BIG}, + {"ENOEXEC", ENOEXEC}, + {"EBADF", EBADF}, + {"ECHILD", ECHILD}, + {"EAGAIN", EAGAIN}, + {"ENOMEM", ENOMEM}, + {"EACCES", EACCES}, + {"EFAULT", EFAULT}, + {"ENOTBLK", ENOTBLK}, + {"EBUSY", EBUSY}, + {"EEXIST", EEXIST}, + {"EXDEV", EXDEV}, + {"ENODEV", ENODEV}, + {"ENOTDIR", ENOTDIR}, + {"EISDIR", EISDIR}, + {"EINVAL", EINVAL}, + {"ENFILE", ENFILE}, + {"EMFILE", EMFILE}, + {"ENOTTY", ENOTTY}, + {"ETXTBSY", ETXTBSY}, + {"EFBIG", EFBIG}, + {"ENOSPC", ENOSPC}, + {"ESPIPE", ESPIPE}, + {"EROFS", EROFS}, + {"EMLINK", EMLINK}, + {"EPIPE", EPIPE}, + {"EDOM", EDOM}, + {"ERANGE", ERANGE}, + {"EDQUOT", EDQUOT}, + {NULL, NULL} +}; + +static void +zcp_load_errno_globals(lua_State *state) +{ + const zcp_errno_global_t *global = errno_globals; + while (global->zeg_name != NULL) { + lua_pushnumber(state, (lua_Number)global->zeg_errno); + lua_setglobal(state, global->zeg_name); + global++; + } +} + +void +zcp_load_globals(lua_State *state) +{ + zcp_load_errno_globals(state); +} diff --git a/usr/src/uts/common/fs/zfs/zcp_iter.c b/usr/src/uts/common/fs/zfs/zcp_iter.c new file mode 100644 index 0000000000..351e400fb5 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/zcp_iter.c @@ -0,0 +1,531 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +#include "lua.h" +#include "lauxlib.h" + +#include <sys/dmu.h> +#include <sys/dsl_prop.h> +#include <sys/dsl_synctask.h> +#include <sys/dsl_dataset.h> +#include <sys/dsl_pool.h> +#include <sys/dmu_tx.h> +#include <sys/dmu_objset.h> +#include <sys/zap.h> +#include <sys/dsl_dir.h> +#include <sys/zcp_prop.h> + +#include <sys/zcp.h> + +typedef int (zcp_list_func_t)(lua_State *); +typedef struct zcp_list_info { + const char *name; + zcp_list_func_t *func; + zcp_list_func_t *gc; + const zcp_arg_t pargs[4]; + const zcp_arg_t kwargs[2]; +} zcp_list_info_t; + +static int +zcp_clones_iter(lua_State *state) +{ + int err; + char clonename[ZFS_MAX_DATASET_NAME_LEN]; + uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1)); + uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); + dsl_pool_t *dp = zcp_run_info(state)->zri_pool; + dsl_dataset_t *ds, *clone; + zap_attribute_t za; + zap_cursor_t zc; + + err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); + if (err == ENOENT) { + return (0); + } else if (err != 0) { + return (luaL_error(state, + "unexpected error %d from dsl_dataset_hold_obj(dsobj)", + err)); + } + + if (dsl_dataset_phys(ds)->ds_next_clones_obj == 0) { + dsl_dataset_rele(ds, FTAG); + return (0); + } + + zap_cursor_init_serialized(&zc, dp->dp_meta_objset, + dsl_dataset_phys(ds)->ds_next_clones_obj, cursor); + dsl_dataset_rele(ds, FTAG); + + err = zap_cursor_retrieve(&zc, &za); + if (err != 0) { + zap_cursor_fini(&zc); + if (err != ENOENT) { + return (luaL_error(state, + "unexpected error %d from zap_cursor_retrieve()", + err)); + } + return (0); + } + zap_cursor_advance(&zc); + cursor = zap_cursor_serialize(&zc); + zap_cursor_fini(&zc); + + err = dsl_dataset_hold_obj(dp, za.za_first_integer, FTAG, &clone); + if (err != 0) { + return (luaL_error(state, + "unexpected error %d from " + "dsl_dataset_hold_obj(za_first_integer)", err)); + } + + dsl_dir_name(clone->ds_dir, clonename); + dsl_dataset_rele(clone, FTAG); + + lua_pushnumber(state, cursor); + lua_replace(state, lua_upvalueindex(2)); + + (void) lua_pushstring(state, clonename); + return (1); +} + +static int zcp_clones_list(lua_State *); +static zcp_list_info_t zcp_clones_list_info = { + .name = "clones", + .func = zcp_clones_list, + .gc = NULL, + .pargs = { + { .za_name = "snapshot", .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {NULL, NULL} + } +}; + +static int +zcp_clones_list(lua_State *state) +{ + const char *snapname = lua_tostring(state, 1); + dsl_pool_t *dp = zcp_run_info(state)->zri_pool; + boolean_t issnap; + uint64_t dsobj, cursor; + + /* + * zcp_dataset_hold will either successfully return the requested + * dataset or throw a lua error and longjmp out of the zfs.list.clones + * call without returning. + */ + dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG); + if (ds == NULL) + return (1); /* not reached; zcp_dataset_hold() longjmp'd */ + cursor = 0; + issnap = ds->ds_is_snapshot; + dsobj = ds->ds_object; + dsl_dataset_rele(ds, FTAG); + + if (!issnap) { + return (zcp_argerror(state, 1, "%s is not a snapshot", + snapname)); + } + + lua_pushnumber(state, dsobj); + lua_pushnumber(state, cursor); + lua_pushcclosure(state, &zcp_clones_iter, 2); + return (1); +} + +static int +zcp_snapshots_iter(lua_State *state) +{ + int err; + char snapname[ZFS_MAX_DATASET_NAME_LEN]; + uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1)); + uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); + dsl_pool_t *dp = zcp_run_info(state)->zri_pool; + dsl_dataset_t *ds; + objset_t *os; + char *p; + + err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); + if (err != 0) { + return (luaL_error(state, + "unexpected error %d from dsl_dataset_hold_obj(dsobj)", + err)); + } + + dsl_dataset_name(ds, snapname); + VERIFY3U(sizeof (snapname), >, + strlcat(snapname, "@", sizeof (snapname))); + + p = strchr(snapname, '\0'); + VERIFY0(dmu_objset_from_ds(ds, &os)); + err = dmu_snapshot_list_next(os, + sizeof (snapname) - (p - snapname), p, NULL, &cursor, NULL); + dsl_dataset_rele(ds, FTAG); + + if (err == ENOENT) { + return (0); + } else if (err != 0) { + return (luaL_error(state, + "unexpected error %d from dmu_snapshot_list_next()", err)); + } + + lua_pushnumber(state, cursor); + lua_replace(state, lua_upvalueindex(2)); + + (void) lua_pushstring(state, snapname); + return (1); +} + +static int zcp_snapshots_list(lua_State *); +static zcp_list_info_t zcp_snapshots_list_info = { + .name = "snapshots", + .func = zcp_snapshots_list, + .gc = NULL, + .pargs = { + { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {NULL, NULL} + } +}; + +static int +zcp_snapshots_list(lua_State *state) +{ + const char *fsname = lua_tostring(state, 1); + dsl_pool_t *dp = zcp_run_info(state)->zri_pool; + boolean_t issnap; + uint64_t dsobj; + + dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG); + if (ds == NULL) + return (1); /* not reached; zcp_dataset_hold() longjmp'd */ + issnap = ds->ds_is_snapshot; + dsobj = ds->ds_object; + dsl_dataset_rele(ds, FTAG); + + if (issnap) { + return (zcp_argerror(state, 1, + "argument %s cannot be a snapshot", fsname)); + } + + lua_pushnumber(state, dsobj); + lua_pushnumber(state, 0); + lua_pushcclosure(state, &zcp_snapshots_iter, 2); + return (1); +} + +/* + * Note: channel programs only run in the global zone, so all datasets + * are visible to this zone. + */ +static boolean_t +dataset_name_hidden(const char *name) +{ + if (strchr(name, '$') != NULL) + return (B_TRUE); + if (strchr(name, '%') != NULL) + return (B_TRUE); + return (B_FALSE); +} + +static int +zcp_children_iter(lua_State *state) +{ + int err; + char childname[ZFS_MAX_DATASET_NAME_LEN]; + uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1)); + uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); + zcp_run_info_t *ri = zcp_run_info(state); + dsl_pool_t *dp = ri->zri_pool; + dsl_dataset_t *ds; + objset_t *os; + char *p; + + err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); + if (err != 0) { + return (luaL_error(state, + "unexpected error %d from dsl_dataset_hold_obj(dsobj)", + err)); + } + + dsl_dataset_name(ds, childname); + VERIFY3U(sizeof (childname), >, + strlcat(childname, "/", sizeof (childname))); + p = strchr(childname, '\0'); + + VERIFY0(dmu_objset_from_ds(ds, &os)); + do { + err = dmu_dir_list_next(os, + sizeof (childname) - (p - childname), p, NULL, &cursor); + } while (err == 0 && dataset_name_hidden(childname)); + dsl_dataset_rele(ds, FTAG); + + if (err == ENOENT) { + return (0); + } else if (err != 0) { + return (luaL_error(state, + "unexpected error %d from dmu_dir_list_next()", + err)); + } + + lua_pushnumber(state, cursor); + lua_replace(state, lua_upvalueindex(2)); + + (void) lua_pushstring(state, childname); + return (1); +} + +static int zcp_children_list(lua_State *); +static zcp_list_info_t zcp_children_list_info = { + .name = "children", + .func = zcp_children_list, + .gc = NULL, + .pargs = { + { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {NULL, NULL} + } +}; + +static int +zcp_children_list(lua_State *state) +{ + const char *fsname = lua_tostring(state, 1); + dsl_pool_t *dp = zcp_run_info(state)->zri_pool; + boolean_t issnap; + uint64_t dsobj; + + dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG); + if (ds == NULL) + return (1); /* not reached; zcp_dataset_hold() longjmp'd */ + + issnap = ds->ds_is_snapshot; + dsobj = ds->ds_object; + dsl_dataset_rele(ds, FTAG); + + if (issnap) { + return (zcp_argerror(state, 1, + "argument %s cannot be a snapshot", fsname)); + } + + lua_pushnumber(state, dsobj); + lua_pushnumber(state, 0); + lua_pushcclosure(state, &zcp_children_iter, 2); + return (1); +} + +static int +zcp_props_list_gc(lua_State *state) +{ + nvlist_t **props = lua_touserdata(state, 1); + if (*props != NULL) + fnvlist_free(*props); + return (0); +} + +static int +zcp_props_iter(lua_State *state) +{ + char *source, *val; + nvlist_t *nvprop; + nvlist_t **props = lua_touserdata(state, lua_upvalueindex(1)); + nvpair_t *pair = lua_touserdata(state, lua_upvalueindex(2)); + + do { + pair = nvlist_next_nvpair(*props, pair); + if (pair == NULL) { + fnvlist_free(*props); + *props = NULL; + return (0); + } + } while (!zfs_prop_user(nvpair_name(pair))); + + lua_pushlightuserdata(state, pair); + lua_replace(state, lua_upvalueindex(2)); + + nvprop = fnvpair_value_nvlist(pair); + val = fnvlist_lookup_string(nvprop, ZPROP_VALUE); + source = fnvlist_lookup_string(nvprop, ZPROP_SOURCE); + + (void) lua_pushstring(state, nvpair_name(pair)); + (void) lua_pushstring(state, val); + (void) lua_pushstring(state, source); + return (3); +} + +static int zcp_props_list(lua_State *); +static zcp_list_info_t zcp_props_list_info = { + .name = "properties", + .func = zcp_props_list, + .gc = zcp_props_list_gc, + .pargs = { + { .za_name = "filesystem | snapshot | volume", + .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {NULL, NULL} + } +}; + +static int +zcp_props_list(lua_State *state) +{ + const char *dsname = lua_tostring(state, 1); + dsl_pool_t *dp = zcp_run_info(state)->zri_pool; + objset_t *os; + nvlist_t **props = lua_newuserdata(state, sizeof (nvlist_t *)); + + dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG); + if (ds == NULL) + return (1); /* not reached; zcp_dataset_hold() longjmp'd */ + VERIFY0(dmu_objset_from_ds(ds, &os)); + VERIFY0(dsl_prop_get_all(os, props)); + dsl_dataset_rele(ds, FTAG); + + /* + * Set the metatable for the properties list to free it on completion. + */ + luaL_getmetatable(state, zcp_props_list_info.name); + (void) lua_setmetatable(state, -2); + + lua_pushlightuserdata(state, NULL); + lua_pushcclosure(state, &zcp_props_iter, 2); + return (1); +} + + +/* + * Populate nv with all valid properties and their values for the given + * dataset. + */ +static void +zcp_dataset_props(dsl_dataset_t *ds, nvlist_t *nv) +{ + for (int prop = ZFS_PROP_TYPE; prop < ZFS_NUM_PROPS; prop++) { + /* Do not display hidden props */ + if (!zfs_prop_visible(prop)) + continue; + /* Do not display props not valid for this dataset */ + if (!prop_valid_for_ds(ds, prop)) + continue; + fnvlist_add_boolean(nv, zfs_prop_to_name(prop)); + } +} + +static int zcp_system_props_list(lua_State *); +static zcp_list_info_t zcp_system_props_list_info = { + .name = "system_properties", + .func = zcp_system_props_list, + .pargs = { + { .za_name = "dataset", .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {NULL, NULL} + } +}; + +/* + * Get a list of all visble properties and their values for a given dataset. + * Returned on the stack as a Lua table. + */ +static int +zcp_system_props_list(lua_State *state) +{ + int error; + char errbuf[128]; + const char *dataset_name; + dsl_pool_t *dp = zcp_run_info(state)->zri_pool; + zcp_list_info_t *libinfo = &zcp_system_props_list_info; + zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs); + dataset_name = lua_tostring(state, 1); + nvlist_t *nv = fnvlist_alloc(); + + dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); + if (ds == NULL) + return (1); /* not reached; zcp_dataset_hold() longjmp'd */ + + /* Get the names of all valid properties for this dataset */ + zcp_dataset_props(ds, nv); + dsl_dataset_rele(ds, FTAG); + + /* push list as lua table */ + error = zcp_nvlist_to_lua(state, nv, errbuf, sizeof (errbuf)); + nvlist_free(nv); + if (error != 0) { + return (luaL_error(state, + "Error returning nvlist: %s", errbuf)); + } + return (1); +} + +static int +zcp_list_func(lua_State *state) +{ + zcp_list_info_t *info = lua_touserdata(state, lua_upvalueindex(1)); + + zcp_parse_args(state, info->name, info->pargs, info->kwargs); + + return (info->func(state)); +} + +int +zcp_load_list_lib(lua_State *state) +{ + int i; + zcp_list_info_t *zcp_list_funcs[] = { + &zcp_children_list_info, + &zcp_snapshots_list_info, + &zcp_props_list_info, + &zcp_clones_list_info, + &zcp_system_props_list_info, + NULL + }; + + lua_newtable(state); + + for (i = 0; zcp_list_funcs[i] != NULL; i++) { + zcp_list_info_t *info = zcp_list_funcs[i]; + + if (info->gc != NULL) { + /* + * If the function requires garbage collection, create + * a metatable with its name and register the __gc + * function. + */ + (void) luaL_newmetatable(state, info->name); + (void) lua_pushstring(state, "__gc"); + lua_pushcfunction(state, info->gc); + lua_settable(state, -3); + lua_pop(state, 1); + } + + lua_pushlightuserdata(state, info); + lua_pushcclosure(state, &zcp_list_func, 1); + lua_setfield(state, -2, info->name); + info++; + } + + return (1); +} diff --git a/usr/src/uts/common/fs/zfs/zcp_synctask.c b/usr/src/uts/common/fs/zfs/zcp_synctask.c new file mode 100644 index 0000000000..2238281416 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/zcp_synctask.c @@ -0,0 +1,265 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ + +#include "lua.h" +#include "lauxlib.h" + +#include <sys/zcp.h> +#include <sys/dsl_dir.h> +#include <sys/dsl_pool.h> +#include <sys/dsl_prop.h> +#include <sys/dsl_synctask.h> +#include <sys/dsl_dataset.h> +#include <sys/dsl_bookmark.h> +#include <sys/dsl_destroy.h> +#include <sys/dmu_objset.h> +#include <sys/zfs_znode.h> +#include <sys/zfeature.h> +#include <sys/metaslab.h> + +#define DST_AVG_BLKSHIFT 14 + +typedef int (zcp_synctask_func_t)(lua_State *, boolean_t, nvlist_t *); +typedef struct zcp_synctask_info { + const char *name; + zcp_synctask_func_t *func; + zfs_space_check_t space_check; + int blocks_modified; + const zcp_arg_t pargs[4]; + const zcp_arg_t kwargs[2]; +} zcp_synctask_info_t; + +/* + * Generic synctask interface for channel program syncfuncs. + * + * To perform some action in syncing context, we'd generally call + * dsl_sync_task(), but since the Lua script is already running inside a + * synctask we need to leave out some actions (such as acquiring the config + * rwlock and performing space checks). + * + * If 'sync' is false, executes a dry run and returns the error code. + * + * This function also handles common fatal error cases for channel program + * library functions. If a fatal error occurs, err_dsname will be the dataset + * name reported in error messages, if supplied. + */ +static int +zcp_sync_task(lua_State *state, dsl_checkfunc_t *checkfunc, + dsl_syncfunc_t *syncfunc, void *arg, boolean_t sync, const char *err_dsname) +{ + int err; + zcp_run_info_t *ri = zcp_run_info(state); + + err = checkfunc(arg, ri->zri_tx); + if (!sync) + return (err); + + if (err == 0) { + syncfunc(arg, ri->zri_tx); + } else if (err == EIO) { + if (err_dsname != NULL) { + return (luaL_error(state, + "I/O error while accessing dataset '%s'", + err_dsname)); + } else { + return (luaL_error(state, + "I/O error while accessing dataset.")); + } + } + + return (err); +} + + +static int zcp_synctask_destroy(lua_State *, boolean_t, nvlist_t *); +static zcp_synctask_info_t zcp_synctask_destroy_info = { + .name = "destroy", + .func = zcp_synctask_destroy, + .space_check = ZFS_SPACE_CHECK_NONE, + .blocks_modified = 0, + .pargs = { + {.za_name = "filesystem | snapshot", .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {.za_name = "defer", .za_lua_type = LUA_TBOOLEAN}, + {NULL, NULL} + } +}; + +/* ARGSUSED */ +static int +zcp_synctask_destroy(lua_State *state, boolean_t sync, nvlist_t *err_details) +{ + int err; + const char *dsname = lua_tostring(state, 1); + + boolean_t issnap = (strchr(dsname, '@') != NULL); + + if (!issnap && !lua_isnil(state, 2)) { + return (luaL_error(state, + "'deferred' kwarg only supported for snapshots: %s", + dsname)); + } + + if (issnap) { + dsl_destroy_snapshot_arg_t ddsa = { 0 }; + ddsa.ddsa_name = dsname; + if (!lua_isnil(state, 2)) { + ddsa.ddsa_defer = lua_toboolean(state, 2); + } else { + ddsa.ddsa_defer = B_FALSE; + } + + err = zcp_sync_task(state, dsl_destroy_snapshot_check, + dsl_destroy_snapshot_sync, &ddsa, sync, dsname); + } else { + dsl_destroy_head_arg_t ddha = { 0 }; + ddha.ddha_name = dsname; + + err = zcp_sync_task(state, dsl_destroy_head_check, + dsl_destroy_head_sync, &ddha, sync, dsname); + } + + return (err); +} + +static int zcp_synctask_promote(lua_State *, boolean_t, nvlist_t *err_details); +static zcp_synctask_info_t zcp_synctask_promote_info = { + .name = "promote", + .func = zcp_synctask_promote, + .space_check = ZFS_SPACE_CHECK_RESERVED, + .blocks_modified = 3, + .pargs = { + {.za_name = "clone", .za_lua_type = LUA_TSTRING}, + {NULL, NULL} + }, + .kwargs = { + {NULL, NULL} + } +}; + +static int +zcp_synctask_promote(lua_State *state, boolean_t sync, nvlist_t *err_details) +{ + int err; + dsl_dataset_promote_arg_t ddpa = { 0 }; + const char *dsname = lua_tostring(state, 1); + zcp_run_info_t *ri = zcp_run_info(state); + + ddpa.ddpa_clonename = dsname; + ddpa.err_ds = err_details; + ddpa.cr = ri->zri_cred; + + /* + * If there was a snapshot name conflict, then err_ds will be filled + * with a list of conflicting snapshot names. + */ + err = zcp_sync_task(state, dsl_dataset_promote_check, + dsl_dataset_promote_sync, &ddpa, sync, dsname); + + return (err); +} + +void +zcp_synctask_wrapper_cleanup(void *arg) +{ + fnvlist_free(arg); +} + +static int +zcp_synctask_wrapper(lua_State *state) +{ + int err; + int num_ret = 1; + nvlist_t *err_details = fnvlist_alloc(); + + /* + * Make sure err_details is properly freed, even if a fatal error is + * thrown during the synctask. + */ + zcp_register_cleanup(state, &zcp_synctask_wrapper_cleanup, err_details); + + zcp_synctask_info_t *info = lua_touserdata(state, lua_upvalueindex(1)); + boolean_t sync = lua_toboolean(state, lua_upvalueindex(2)); + + zcp_run_info_t *ri = zcp_run_info(state); + dsl_pool_t *dp = ri->zri_pool; + + /* MOS space is triple-dittoed, so we multiply by 3. */ + uint64_t funcspace = (info->blocks_modified << DST_AVG_BLKSHIFT) * 3; + + zcp_parse_args(state, info->name, info->pargs, info->kwargs); + + err = 0; + if (info->space_check != ZFS_SPACE_CHECK_NONE && funcspace > 0) { + uint64_t quota = dsl_pool_adjustedsize(dp, + info->space_check == ZFS_SPACE_CHECK_RESERVED) - + metaslab_class_get_deferred(spa_normal_class(dp->dp_spa)); + uint64_t used = dsl_dir_phys(dp->dp_root_dir)->dd_used_bytes + + ri->zri_space_used; + + if (used + funcspace > quota) { + err = SET_ERROR(ENOSPC); + } + } + + if (err == 0) { + err = info->func(state, sync, err_details); + } + + if (err == 0) { + ri->zri_space_used += funcspace; + } + + lua_pushnumber(state, (lua_Number)err); + if (fnvlist_num_pairs(err_details) > 0) { + (void) zcp_nvlist_to_lua(state, err_details, NULL, 0); + num_ret++; + } + + zcp_clear_cleanup(state); + fnvlist_free(err_details); + + return (num_ret); +} + +int +zcp_load_synctask_lib(lua_State *state, boolean_t sync) +{ + int i; + zcp_synctask_info_t *zcp_synctask_funcs[] = { + &zcp_synctask_destroy_info, + &zcp_synctask_promote_info, + NULL + }; + + lua_newtable(state); + + for (i = 0; zcp_synctask_funcs[i] != NULL; i++) { + zcp_synctask_info_t *info = zcp_synctask_funcs[i]; + lua_pushlightuserdata(state, info); + lua_pushboolean(state, sync); + lua_pushcclosure(state, &zcp_synctask_wrapper, 2); + lua_setfield(state, -2, info->name); + info++; + } + + return (1); +} diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c index 014ddd28fe..4f2a563f92 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c @@ -26,7 +26,7 @@ * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved. - * Copyright (c) 2011, 2015 by Delphix. All rights reserved. + * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] @@ -185,6 +185,7 @@ #include <sys/dsl_bookmark.h> #include <sys/dsl_userhold.h> #include <sys/zfeature.h> +#include <sys/zcp.h> #include <sys/zio_checksum.h> #include "zfs_namecheck.h" @@ -192,6 +193,9 @@ #include "zfs_deleg.h" #include "zfs_comutil.h" +#include "lua.h" +#include "lauxlib.h" + extern struct modlfs zfs_modlfs; extern void zfs_init(void); @@ -1397,17 +1401,11 @@ put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) return (error); } -static int -getzfsvfs(const char *dsname, zfsvfs_t **zfvp) +int +getzfsvfs_impl(objset_t *os, zfsvfs_t **zfvp) { - objset_t *os; - int error; - - error = dmu_objset_hold(dsname, FTAG, &os); - if (error != 0) - return (error); + int error = 0; if (dmu_objset_type(os) != DMU_OST_ZFS) { - dmu_objset_rele(os, FTAG); return (SET_ERROR(EINVAL)); } @@ -1419,6 +1417,20 @@ getzfsvfs(const char *dsname, zfsvfs_t **zfvp) error = SET_ERROR(ESRCH); } mutex_exit(&os->os_user_ptr_lock); + return (error); +} + +static int +getzfsvfs(const char *dsname, zfsvfs_t **zfvp) +{ + objset_t *os; + int error; + + error = dmu_objset_hold(dsname, FTAG, &os); + if (error != 0) + return (error); + + error = getzfsvfs_impl(os, zfvp); dmu_objset_rele(os, FTAG); return (error); } @@ -3643,6 +3655,36 @@ zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, return (error); } +static int +zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, + nvlist_t *outnvl) +{ + char *program; + uint64_t instrlimit, memlimit; + nvpair_t *nvarg = NULL; + + if (0 != nvlist_lookup_string(innvl, ZCP_ARG_PROGRAM, &program)) { + return (EINVAL); + } + if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_INSTRLIMIT, &instrlimit)) { + instrlimit = ZCP_DEFAULT_INSTRLIMIT; + } + if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_MEMLIMIT, &memlimit)) { + memlimit = ZCP_DEFAULT_MEMLIMIT; + } + if (0 != nvlist_lookup_nvpair(innvl, ZCP_ARG_ARGLIST, &nvarg)) { + return (EINVAL); + } + + if (instrlimit == 0 || instrlimit > zfs_lua_max_instrlimit) + return (EINVAL); + if (memlimit == 0 || memlimit > ZCP_MAX_MEMLIMIT) + return (EINVAL); + + return (zcp_eval(poolname, program, instrlimit, memlimit, + nvarg, outnvl)); +} + /* * inputs: * zc_name name of dataset to destroy @@ -5795,6 +5837,11 @@ zfs_ioctl_init(void) POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM, + zfs_ioc_channel_program, zfs_secpolicy_config, + POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, + B_TRUE); + /* IOCTLS that use the legacy function signature */ zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, @@ -6161,12 +6208,23 @@ zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) outnvl = fnvlist_alloc(); error = vec->zvec_func(zc->zc_name, innvl, outnvl); - if (error == 0 && vec->zvec_allow_log && + /* + * Some commands can partially execute, modfiy state, and still + * return an error. In these cases, attempt to record what + * was modified. + */ + if ((error == 0 || + (cmd == ZFS_IOC_CHANNEL_PROGRAM && error != EINVAL)) && + vec->zvec_allow_log && spa_open(zc->zc_name, &spa, FTAG) == 0) { if (!nvlist_empty(outnvl)) { fnvlist_add_nvlist(lognv, ZPOOL_HIST_OUTPUT_NVL, outnvl); } + if (error != 0) { + fnvlist_add_int64(lognv, ZPOOL_HIST_ERRNO, + error); + } (void) spa_history_log_nvl(spa, lognv); spa_close(spa, FTAG); } diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c index b73ccdcb25..7a5cfa7fb9 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c @@ -975,12 +975,26 @@ zfsvfs_create(const char *osname, zfsvfs_t **zfvp) * We claim to always be readonly so we can open snapshots; * other ZPL code will prevent us from writing to snapshots. */ + error = dmu_objset_own(osname, DMU_OST_ZFS, B_TRUE, zfsvfs, &os); - if (error) { + if (error != 0) { kmem_free(zfsvfs, sizeof (zfsvfs_t)); return (error); } + error = zfsvfs_create_impl(zfvp, zfsvfs, os); + if (error != 0) { + dmu_objset_disown(os, zfsvfs); + } + return (error); +} + + +int +zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os) +{ + int error; + zfsvfs->z_vfs = NULL; zfsvfs->z_parent = zfsvfs; @@ -996,7 +1010,6 @@ zfsvfs_create(const char *osname, zfsvfs_t **zfvp) error = zfsvfs_init(zfsvfs, os); if (error != 0) { - dmu_objset_disown(os, zfsvfs); *zfvp = NULL; kmem_free(zfsvfs, sizeof (zfsvfs_t)); return (error); diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h index 354c250bb7..389e9b59fa 100644 --- a/usr/src/uts/common/sys/fs/zfs.h +++ b/usr/src/uts/common/sys/fs/zfs.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 Joyent, Inc. @@ -254,11 +254,17 @@ typedef int (*zprop_func)(int, void *); #define ZPOOL_ROOTFS_PROPS "root-props-nvl" /* + * Length of 'written@' and 'written#' + */ +#define ZFS_WRITTEN_PROP_PREFIX_LEN 8 + +/* * Dataset property functions shared between libzfs and kernel. */ const char *zfs_prop_default_string(zfs_prop_t); uint64_t zfs_prop_default_numeric(zfs_prop_t); boolean_t zfs_prop_readonly(zfs_prop_t); +boolean_t zfs_prop_visible(zfs_prop_t prop); boolean_t zfs_prop_inheritable(zfs_prop_t); boolean_t zfs_prop_setonce(zfs_prop_t); const char *zfs_prop_to_name(zfs_prop_t); @@ -874,6 +880,7 @@ typedef enum zfs_ioc { ZFS_IOC_BOOKMARK, ZFS_IOC_GET_BOOKMARKS, ZFS_IOC_DESTROY_BOOKMARKS, + ZFS_IOC_CHANNEL_PROGRAM, ZFS_IOC_LAST } zfs_ioc_t; @@ -918,6 +925,7 @@ typedef enum { #define ZPOOL_HIST_OUTPUT_NVL "out_nvl" #define ZPOOL_HIST_DSNAME "dsname" #define ZPOOL_HIST_DSID "dsid" +#define ZPOOL_HIST_ERRNO "errno" /* * Flags for ZFS_IOC_VDEV_SET_STATE @@ -938,6 +946,24 @@ typedef enum { #define ZFS_IMPORT_ONLY 0x8 /* + * Channel program argument/return nvlist keys and defaults. + */ +#define ZCP_ARG_PROGRAM "program" +#define ZCP_ARG_ARGLIST "arg" +#define ZCP_ARG_INSTRLIMIT "instrlimit" +#define ZCP_ARG_MEMLIMIT "memlimit" + +#define ZCP_ARG_CLIARGV "argv" + +#define ZCP_RET_ERROR "error" +#define ZCP_RET_RETURN "return" + +#define ZCP_DEFAULT_INSTRLIMIT (10 * 1000 * 1000) +#define ZCP_MAX_INSTRLIMIT (10 * ZCP_DEFAULT_INSTRLIMIT) +#define ZCP_DEFAULT_MEMLIMIT (10 * 1024 * 1024) +#define ZCP_MAX_MEMLIMIT (10 * ZCP_DEFAULT_MEMLIMIT) + +/* * Sysevent payload members. ZFS will generate the following sysevents with the * given payloads: * diff --git a/usr/src/uts/intel/zfs/Makefile b/usr/src/uts/intel/zfs/Makefile index ea5e947b76..1d3da0ec8a 100644 --- a/usr/src/uts/intel/zfs/Makefile +++ b/usr/src/uts/intel/zfs/Makefile @@ -27,6 +27,8 @@ # # Copyright 2013 Saso Kiselkov. All rights reserved. # +# Copyright (c) 2016 by Delphix. All rights reserved. +# # # Path to the base of the uts directory tree (usually /usr/src/uts). @@ -39,7 +41,8 @@ ARCHDIR:sh = cd ..; basename `pwd` # Define the module and object file sets. # MODULE = zfs -OBJECTS = $(ZFS_OBJS:%=$(OBJS_DIR)/%) +OBJECTS = $(ZFS_OBJS:%=$(OBJS_DIR)/%) $(LUA_OBJS:%=$(OBJS_DIR)/%) +# LUA_OBJS are intentionally omitted from LINTS LINTS = $(ZFS_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) ROOTLINK = $(ROOT_FS_DIR)/$(MODULE) @@ -65,6 +68,7 @@ LDFLAGS += -dy -Nfs/specfs -Ncrypto/swrand -Nmisc/idmap -Nmisc/sha2 \ -Nmisc/skein -Nmisc/edonr INC_PATH += -I$(UTSBASE)/common/fs/zfs +INC_PATH += -I$(UTSBASE)/common/fs/zfs/lua INC_PATH += -I$(SRC)/common INC_PATH += -I$(COMMONBASE)/zfs diff --git a/usr/src/uts/sparc/zfs/Makefile b/usr/src/uts/sparc/zfs/Makefile index 8d6d25df5e..f9d5d42165 100644 --- a/usr/src/uts/sparc/zfs/Makefile +++ b/usr/src/uts/sparc/zfs/Makefile @@ -27,6 +27,8 @@ # # Copyright 2013 Saso Kiselkov. All rights reserved. # +# Copyright (c) 2016 by Delphix. All rights reserved. +# # # Path to the base of the uts directory tree (usually /usr/src/uts). @@ -39,7 +41,8 @@ ARCHDIR:sh = cd ..; basename `pwd` # Define the module and object file sets. # MODULE = zfs -OBJECTS = $(ZFS_OBJS:%=$(OBJS_DIR)/%) +OBJECTS = $(ZFS_OBJS:%=$(OBJS_DIR)/%) $(LUA_OBJS:%=$(OBJS_DIR)/%) +# LUA_OBJS are intentionally omitted from LINTS LINTS = $(ZFS_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) ROOTLINK = $(ROOT_FS_DIR)/$(MODULE) @@ -68,6 +71,7 @@ LDFLAGS += -dy -Nfs/specfs -Ncrypto/swrand -Nmisc/idmap \ -Nsched/SDC -Nmisc/sha2 -Nmisc/skein -Nmisc/edonr INC_PATH += -I$(UTSBASE)/common/fs/zfs +INC_PATH += -I$(UTSBASE)/common/fs/zfs/lua INC_PATH += -I$(SRC)/common INC_PATH += -I$(COMMONBASE)/zfs |