diff:
@@ -42,6 +42,9 @@ int do_setregid(proc_t *p, gid_t rgid, gid_t egid);
*/
void cred_fork(proc_t *to, proc_t *from);
+/* \note Must be called with p::p_lock held */
+void cred_exec_setid(proc_t *p, uid_t uid, gid_t gid);
+
/* \note Must be called with p::p_lock. Returns p::p_lock held. */
int cred_cansignal(proc_t *p, cred_t *cred);

diff:
@@ -7,3 +7,15 @@ void cred_fork(proc_t *to, proc_t *from) {
assert(mtx_owned(&from->p_lock));
memcpy(&to->p_cred, &from->p_cred, sizeof(cred_t));
}
+
+void cred_exec_setid(proc_t *p, uid_t uid, gid_t gid) {
+ assert(mtx_owned(&p->p_lock));
+ if (uid != (uid_t)-1) {
+ p->p_cred.cr_suid = p->p_cred.cr_euid;
+ p->p_cred.cr_euid = uid;
+ }
+ if (gid != (gid_t)-1) {
+ p->p_cred.cr_sgid = p->p_cred.cr_egid;
+ p->p_cred.cr_egid = gid;
+ }
+}

diff:
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/malloc.h>
+#include <sys/stat.h>
typedef int (*copy_ptr_t)(exec_args_t *args, char *const *ptr_p);
typedef int (*copy_str_t)(exec_args_t *args, const char *str, size_t *copied_p);
@@ -302,6 +303,22 @@ static void destroy_vmspace(exec_vmspace_t *saved) {
vm_map_delete(saved->uspace);
}
+static bool check_setid(vnode_t *vn, uid_t *uid, gid_t *gid) {
+ vattr_t attr;
+ *uid = *gid = -1;
+
+ if (VOP_GETATTR(vn, &attr))
+ return false;
+
+ if (attr.va_mode & S_ISUID)
+ *uid = attr.va_uid;
+
+ if (attr.va_mode & S_ISGID)
+ *gid = attr.va_gid;
+
+ return (*uid != (uid_t)-1) || (*gid != (gid_t)-1);
+}
+
/* XXX We assume process may only have a single thread. But if there were more
* than one thread in the process that called exec, all other threads must be
* forcefully terminated. */
@@ -339,6 +356,12 @@ static int _do_execve(exec_args_t *args) {
use_interpreter = true;
}
+ uid_t uid;
+ gid_t gid;
+ /* XXX: This solution is prone to TOCTTOU. File (content or owners) may be
+ * changed between check of identity and load of content. */
+ bool setid = check_setid(vn, &uid, &gid);
+
Elf_Ehdr eh;
if ((error = exec_elf_inspect(vn, &eh)))
return error;
@@ -361,6 +384,13 @@ static int _do_execve(exec_args_t *args) {
/* Set up user context. */
user_ctx_init(td->td_uctx, (void *)eh.e_entry, (void *)stack_top);
+ /* Set new credentials if needed */
+ if (setid) {
+ WITH_PROC_LOCK(p) {
+ cred_exec_setid(p, uid, gid);
+ }
+ }
+
/* At this point we are certain that exec succeeds. We can safely destroy the
* previous vm_map, and permanently assign this one to the current process. */
destroy_vmspace(&saved);

original_message: execve: change process identity according to set-uid and set-gid bits (#821)


change_type: MODIFY

diff:
@@ -145,7 +145,8 @@ int do_setuid(proc_t *p, uid_t uid) {
uid_t ruid = uid, euid = uid, suid = uid;
- if (p->p_cred.cr_euid == 0)
+ /* Only root sets all ids to given value. */
+ if (p->p_cred.cr_euid != 0)
ruid = suid = -1;
int error = change_resuid(&p->p_cred, ruid, euid, suid);
@@ -198,6 +199,7 @@ int do_setgid(proc_t *p, gid_t gid) {
gid_t rgid = gid, egid = gid, sgid = gid;
+ /* Only root sets all ids to given value. */
if (p->p_cred.cr_euid != 0)
rgid = sgid = -1;

license: BSD 3-Clause New or Revised License

repo: cahirwpz/mimiker

original_message: setuid syscall fix - only root can change ruid and suid (#827)


diff:
@@ -47,6 +47,8 @@ typedef struct tmpfs_node {
nlink_t tfn_links; /* number of file hard links */
ino_t tfn_ino; /* node identifier */
size_t tfn_size; /* file size in bytes */
+ uid_t tfn_uid; /* owner of file */
+ gid_t tfn_gid; /* group of file */
/* Data that is only applicable to a particular type. */
union {
@@ -276,6 +278,8 @@ static int tmpfs_vop_getattr(vnode_t *v, vattr_t *va) {
va->va_mode = node->tfn_mode;
va->va_nlink = node->tfn_links;
va->va_ino = node->tfn_ino;
+ va->va_uid = node->tfn_uid;
+ va->va_gid = node->tfn_gid;
va->va_size = node->tfn_size;
return 0;
}
@@ -434,6 +438,8 @@ static tmpfs_node_t *tmpfs_new_node(tmpfs_mount_t *tfm, vattr_t *va,
node->tfn_mode = va->va_mode;
node->tfn_type = ntype;
node->tfn_links = 0;
+ node->tfn_uid = va->va_uid;
+ node->tfn_gid = va->va_gid;
node->tfn_size = 0;
mtx_lock(&tfm->tfm_lock);

diff:
@@ -65,6 +65,8 @@ static int vfs_create(proc_t *p, int fdat, char *pathname, int flags, int mode,
vattr_t va;
vattr_null(&va);
va.va_mode = S_IFREG | (mode & ALLPERMS);
+ va.va_uid = p->p_cred.cr_ruid;
+ va.va_gid = p->p_cred.cr_rgid;
error = VOP_CREATE(vs.vs_dvp, &vs.vs_lastcn, &va, &vs.vs_vp);
vnode_put(vs.vs_dvp);
} else {
@@ -257,6 +259,8 @@ int do_mkdirat(proc_t *p, int fd, char *path, mode_t mode) {
* implementation-defined.
* https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html */
va.va_mode = S_IFDIR | ((mode & ACCESSPERMS) & ~p->p_cmask);
+ va.va_uid = p->p_cred.cr_ruid;
+ va.va_gid = p->p_cred.cr_rgid;
error = VOP_MKDIR(vs.vs_dvp, &vs.vs_lastcn, &va, &vs.vs_vp);
if (!error)
@@ -458,6 +462,8 @@ int do_symlinkat(proc_t *p, char *target, int newdirfd, char *linkpath) {
memset(&va, 0, sizeof(vattr_t));
va.va_mode = S_IFLNK | (ACCESSPERMS & ~p->p_cmask);
+ va.va_uid = p->p_cred.cr_ruid;
+ va.va_gid = p->p_cred.cr_rgid;
error = VOP_SYMLINK(vs.vs_dvp, &vs.vs_lastcn, &va, target, &vs.vs_vp);
if (!error)

original_message: tmpfs & vfs: pass file's owner and group around (#846)


message: tmpfs: fix root directory uid and gid

diff:
@@ -966,6 +966,8 @@ static int tmpfs_mount(mount_t *mp) {
vattr_t va;
vattr_null(&va);
va.va_mode = S_IFDIR | (ACCESSPERMS & ~CMASK);
+ va.va_uid = 0;
+ va.va_gid = 0;
tmpfs_node_t *root = tmpfs_new_node(tfm, &va, V_DIR);
tmpfs_attach_vnode(root, mp);
root->tfn_dir.parent = root; /* Parent of the root node is itself. */

original_message: tmpfs: fix root directory uid and gid (#881)


message: Port chown program from NetBSD

old_path: bin/Makefile

new_path: bin/Makefile

diff:
TOPDIR = $(realpath ..)
-SUBDIR = cat chmod date echo kill ksh ln ls lua mandelbrot mish mkdir pwd \
+SUBDIR = cat chmod chown date echo kill ksh ln ls lua mandelbrot mish mkdir pwd\
rm rmdir sandbox stty test_kbd test_rtc utest
all: build

change_type: ADD

new_path: bin/chown/Makefile

diff:
+TOPDIR = $(realpath ../..)
+
+PROGRAM = chown
+FORMAT-EXCLUDE = $(SOURCES)
+
+include $(TOPDIR)/build/build.prog.mk

diff:
+/* $NetBSD: chown.c,v 1.8 2012/10/24 01:12:51 enami Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993, 1994, 2003
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+
+static void a_gid(const char *);
+static void a_uid(const char *);
+static id_t id(const char *, const char *);
+static void usage(void);
+
+static uid_t uid;
+static gid_t gid;
+static int ischown;
+static const char *myname;
+
+struct option chown_longopts[] = {
+ { "reference", required_argument, 0,
+ 1 },
+ { NULL, 0, 0,
+ 0 },
+};
+
+int
+main(int argc, char **argv)
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, vflag;
+ char *cp, *reference;
+ int (*change_owner)(const char *, uid_t, gid_t);
+
+ setprogname(*argv);
+
+ (void)setlocale(LC_ALL, "");
+
+ myname = getprogname();
+ ischown = (myname[2] == 'o');
+ reference = NULL;
+
+ Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
+ while ((ch = getopt_long(argc, argv, "HLPRfhv",
+ chown_longopts, NULL)) != -1)
+ switch (ch) {
+ case 1:
+ reference = optarg;
+ break;
+ case 'H':
+ Hflag = 1;
+ Lflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = 0;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ /*
+ * In System V the -h option causes chown/chgrp to
+ * change the owner/group of the symbolic link.
+ * 4.4BSD's symbolic links didn't have owners/groups,
+ * so it was an undocumented noop.
+ * In NetBSD 1.3, lchown(2) is introduced.
+ */
+ hflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc == 0 || (argc == 1 && reference == NULL))
+ usage();
+
+ fts_options = FTS_PHYSICAL;
+ if (Rflag) {
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ if (Lflag) {
+ if (hflag)
+ errx(EXIT_FAILURE,
+ "the -L and -h options "
+ "may not be specified together.");
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ } else if (!hflag)
+ fts_options |= FTS_COMFOLLOW;
+
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ if (reference == NULL) {
+ if (ischown) {
+ if ((cp = strchr(*argv, ':')) != NULL) {
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#ifdef SUPPORT_DOT
+ else if ((cp = strrchr(*argv, '.')) != NULL) {
+ if (uid_from_user(*argv, &uid) == -1) {
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+ }
+#endif
+ a_uid(*argv);
+ } else
+ a_gid(*argv);
+ argv++;
+ } else {
+ struct stat st;
+
+ if (stat(reference, &st) == -1)
+ err(EXIT_FAILURE, "Cannot stat `%s'", reference);
+ if (ischown)
+ uid = st.st_uid;
+ gid = st.st_gid;
+ }
+
+ if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
+ err(EXIT_FAILURE, "fts_open");
+
+ for (rval = EXIT_SUCCESS; (p = fts_read(ftsp)) != NULL;) {
+ change_owner = chown;
+ switch (p->fts_info) {
+ case FTS_D:
+ if (!Rflag) /* Change it at FTS_DP. */
+ fts_set(ftsp, p, FTS_SKIP);
+ continue;
+ case FTS_DNR: /* Warn, chown, continue. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = EXIT_FAILURE;
+ break;
+ case FTS_ERR: /* Warn, continue. */
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = EXIT_FAILURE;
+ continue;
+ case FTS_SL: /* Ignore unless -h. */
+ /*
+ * All symlinks we found while doing a physical
+ * walk end up here.
+ */
+ if (!hflag)
+ continue;
+ /*
+ * Note that if we follow a symlink, fts_info is
+ * not FTS_SL but FTS_F or whatever. And we should
+ * use lchown only for FTS_SL and should use chown
+ * for others.
+ */
+ change_owner = lchown;
+ break;
+ case FTS_SLNONE: /* Ignore. */
+ /*
+ * The only symlinks that end up here are ones that
+ * don't point to anything. Note that if we are
+ * doing a phisycal walk, we never reach here unless
+ * we asked to follow explicitly.
+ */
+ continue;
+ default:
+ break;
+ }
+
+ if ((*change_owner)(p->fts_accpath, uid, gid) && !fflag) {
+ warn("%s", p->fts_path);
+ rval = EXIT_FAILURE;
+ } else {
+ if (vflag)
+ printf("%s\n", p->fts_path);
+ }
+ }
+ if (errno)
+ err(EXIT_FAILURE, "fts_read");
+ exit(rval);
+ /* NOTREACHED */
+}
+
+static void
+a_gid(const char *s)
+{
+ struct group *gr;
+
+ if (*s == '\0') /* Argument was "uid[:.]". */
+ return;
+ gr = *s == '#' ? NULL : getgrnam(s);
+ if (gr == NULL)
+ gid = id(s, "group");
+ else
+ gid = gr->gr_gid;
+ return;
+}
+
+static void
+a_uid(const char *s)
+{
+ if (*s == '\0') /* Argument was "[:.]gid". */
+ return;
+ if (*s == '#' || uid_from_user(s, &uid) == -1) {
+ uid = id(s, "user");
+ }
+ return;
+}
+
+static id_t
+id(const char *name, const char *type)
+{
+ id_t val;
+ char *ep;
+
+ errno = 0;
+ if (*name == '#')
+ name++;
+ val = (id_t)strtoul(name, &ep, 10);
+ if (errno)
+ err(EXIT_FAILURE, "%s", name);
+ if (*ep != '\0')
+ errx(EXIT_FAILURE, "%s: invalid %s name", name, type);
+ return (val);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr,
+ "Usage: %s [-R [-H | -L | -P]] [-fhv] %s file ...\n"
+ "\t%s [-R [-H | -L | -P]] [-fhv] --reference=rfile file ...\n",
+ myname, ischown ? "owner:group|owner|:group" : "group",
+ myname);
+ exit(EXIT_FAILURE);
+}

diff:
@@ -66,6 +66,7 @@ void setgrfile(const char *);
int setgroupent(int);
int getgrent_r(struct group *, char *, size_t, struct group **);
const char *group_from_gid(gid_t, int);
+int gid_from_group(const char *, gid_t *);
__END_DECLS
#endif /* !_GRP_H_ */

diff:
@@ -97,6 +97,7 @@ void endpwent(void);
int setpassent(int);
const char *user_from_uid(uid_t, int);
+int uid_from_user(const char *, uid_t *);
__END_DECLS
#endif /* !_PWD_H_ */

diff:
@@ -8,3 +8,23 @@ const char *user_from_uid(uid_t uid, int noname) {
const char *group_from_gid(gid_t gid, int noname) {
return NULL;
}
+
+int uid_from_user(const char *name, uid_t *uid) {
+ struct passwd *pw;
+ pw = getpwnam(name);
+ if (pw == NULL)
+ return -1;
+
+ *uid = pw->pw_uid;
+ return 0;
+}
+
+int gid_from_group(const char *name, gid_t *gid) {
+ struct group *gr;
+ gr = getgrnam(name);
+ if (gr == NULL)
+ return -1;
+
+ *gid = gr->gr_gid;
+ return 0;
+}

original_message: Port chown program from NetBSD (#904)


message: Port pwcache.c from NetBSD

diff:
-#include <pwd.h>
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * 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.
+ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <assert.h>
#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pwcache.h"
+
+/*
+ * routines that control user, group, uid and gid caches (for the archive
+ * member print routine).
+ * IMPORTANT:
+ * these routines cache BOTH hits and misses, a major performance improvement
+ */
+
+/*
+ * function pointers to various name lookup routines.
+ * these may be changed as necessary.
+ */
+static int (*_pwcache_setgroupent)(int) = setgroupent;
+static void (*_pwcache_endgrent)(void) = endgrent;
+static struct group *(*_pwcache_getgrnam)(const char *) = getgrnam;
+static struct group *(*_pwcache_getgrgid)(gid_t) = getgrgid;
+static int (*_pwcache_setpassent)(int) = setpassent;
+static void (*_pwcache_endpwent)(void) = endpwent;
+static struct passwd *(*_pwcache_getpwnam)(const char *) = getpwnam;
+static struct passwd *(*_pwcache_getpwuid)(uid_t) = getpwuid;
+
+/*
+ * internal state
+ */
+static int pwopn; /* is password file open */
+static int gropn; /* is group file open */
+static UIDC **uidtb; /* uid to name cache */
+static GIDC **gidtb; /* gid to name cache */
+static UIDC **usrtb; /* user name to uid cache */
+static GIDC **grptb; /* group name to gid cache */
+
+static int uidtb_fail; /* uidtb_start() failed ? */
+static int gidtb_fail; /* gidtb_start() failed ? */
+static int usrtb_fail; /* usrtb_start() failed ? */
+static int grptb_fail; /* grptb_start() failed ? */
+
+static u_int st_hash(const char *, size_t, int);
+static int uidtb_start(void);
+static int gidtb_start(void);
+static int usrtb_start(void);
+static int grptb_start(void);
+
+static u_int st_hash(const char *name, size_t len, int tabsz) {
+ u_int key = 0;
+
+ _DIAGASSERT(name != NULL);
+
+ while (len--) {
+ key += *name++;
+ key = (key << 8) | (key >> 24);
+ }
+
+ return (key % tabsz);
+}
+
+/*
+ * uidtb_start
+ * creates an empty uidtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int uidtb_start(void) {
+
+ if (uidtb != NULL)
+ return (0);
+ if (uidtb_fail)
+ return (-1);
+ if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
+ ++uidtb_fail;
+ return (-1);
+ }
+ return (0);
+}
+/*
+ * gidtb_start
+ * creates an empty gidtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int gidtb_start(void) {
+
+ if (gidtb != NULL)
+ return (0);
+ if (gidtb_fail)
+ return (-1);
+ if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
+ ++gidtb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * usrtb_start
+ * creates an empty usrtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int usrtb_start(void) {
+
+ if (usrtb != NULL)
+ return (0);
+ if (usrtb_fail)
+ return (-1);
+ if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
+ ++usrtb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * grptb_start
+ * creates an empty grptb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int grptb_start(void) {
+
+ if (grptb != NULL)
+ return (0);
+ if (grptb_fail)
+ return (-1);
+ if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
+ ++grptb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * user_from_uid()
+ * caches the name (if any) for the uid. If noname clear, we always
+ * return the stored name (if valid or invalid match).
+ * We use a simple hash table.
+ * Return
+ * Pointer to stored name (or a empty string)
+ */
const char *user_from_uid(uid_t uid, int noname) {
- return NULL;
+ struct passwd *pw;
+ UIDC *ptr, **pptr;
+
+ if ((uidtb == NULL) && (uidtb_start() < 0))
+ return (NULL);
+
+ /*
+ * see if we have this uid cached
+ */
+ pptr = uidtb + (uid % UID_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
+ /*
+ * have an entry for this uid
+ */
+ if (!noname || (ptr->valid == VALID))
+ return (ptr->name);
+ return (NULL);
}
+ /*
+ * No entry for this uid, we will add it
+ */
+ if (!pwopn) {
+ if (_pwcache_setpassent != NULL)
+ (*_pwcache_setpassent)(1);
+ ++pwopn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
+
+ if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) {
+ /*
+ * no match for this uid in the local password file
+ * a string that is the uid in numeric format
+ */
+ if (ptr == NULL)
+ return (NULL);
+ ptr->uid = uid;
+ (void)snprintf(ptr->name, UNMLEN, "%lu", (long)uid);
+ ptr->valid = INVALID;
+ if (noname)
+ return (NULL);
+ } else {
+ /*
+ * there is an entry for this uid in the password file
+ */
+ if (ptr == NULL)
+ return (pw->pw_name);
+ ptr->uid = uid;
+ (void)strlcpy(ptr->name, pw->pw_name, UNMLEN);
+ ptr->valid = VALID;
+ }
+ return (ptr->name);
+}
+
+/*
+ * group_from_gid()
+ * caches the name (if any) for the gid. If noname clear, we always
+ * return the stored name (if valid or invalid match).
+ * We use a simple hash table.
+ * Return
+ * Pointer to stored name (or a empty string)
+ */
const char *group_from_gid(gid_t gid, int noname) {
- return NULL;
+ struct group *gr;
+ GIDC *ptr, **pptr;
+
+ if ((gidtb == NULL) && (gidtb_start() < 0))
+ return (NULL);
+
+ /*
+ * see if we have this gid cached
+ */
+ pptr = gidtb + (gid % GID_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
+ /*
+ * have an entry for this gid
+ */
+ if (!noname || (ptr->valid == VALID))
+ return (ptr->name);
+ return (NULL);
+ }
+
+ /*
+ * No entry for this gid, we will add it
+ */
+ if (!gropn) {
+ if (_pwcache_setgroupent != NULL)
+ (*_pwcache_setgroupent)(1);
+ ++gropn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
+
+ if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) {
+ /*
+ * no match for this gid in the local group file, put in
+ * a string that is the gid in numberic format
+ */
+ if (ptr == NULL)
+ return (NULL);
+ ptr->gid = gid;
+ (void)snprintf(ptr->name, GNMLEN, "%lu", (long)gid);
+ ptr->valid = INVALID;
+ if (noname)
+ return (NULL);
+ } else {
+ /*
+ * there is an entry for this group in the group file
+ */
+ if (ptr == NULL)
+ return (gr->gr_name);
+ ptr->gid = gid;
+ (void)strlcpy(ptr->name, gr->gr_name, GNMLEN);
+ ptr->valid = VALID;
+ }
+ return (ptr->name);
}
+/*
+ * uid_from_user()
+ * caches the uid for a given user name. We use a simple hash table.
+ * Return
+ * the uid (if any) for a user name, or a -1 if no match can be found
+ */
int uid_from_user(const char *name, uid_t *uid) {
struct passwd *pw;
- pw = getpwnam(name);
- if (pw == NULL)
- return -1;
+ UIDC *ptr, **pptr;
+ size_t namelen;
+ /*
+ * return -1 for mangled names
+ */
+ if (name == NULL || ((namelen = strlen(name)) == 0))
+ return (-1);
+ if ((usrtb == NULL) && (usrtb_start() < 0))
+ return (-1);
+
+ /*
+ * look up in hash table, if found and valid return the uid,
+ * if found and invalid, return a -1
+ */
+ pptr = usrtb + st_hash(name, namelen, UNM_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+ if (ptr->valid == INVALID)
+ return (-1);
+ *uid = ptr->uid;
+ return (0);
+ }
+
+ if (!pwopn) {
+ if (_pwcache_setpassent != NULL)
+ (*_pwcache_setpassent)(1);
+ ++pwopn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
+
+ /*
+ * no match, look it up, if no match store it as an invalid entry,
+ * or store the matching uid
+ */
+ if (ptr == NULL) {
+ if ((pw = (*_pwcache_getpwnam)(name)) == NULL)
+ return (-1);
*uid = pw->pw_uid;
- return 0;
+ return (0);
+ }
+ (void)strlcpy(ptr->name, name, UNMLEN);
+ if ((pw = (*_pwcache_getpwnam)(name)) == NULL) {
+ ptr->valid = INVALID;
+ return (-1);
+ }
+ ptr->valid = VALID;
+ *uid = ptr->uid = pw->pw_uid;
+ return (0);
}
+/*
+ * gid_from_group()
+ * caches the gid for a given group name. We use a simple hash table.
+ * Return
+ * the gid (if any) for a group name, or a -1 if no match can be found
+ */
int gid_from_group(const char *name, gid_t *gid) {
struct group *gr;
- gr = getgrnam(name);
- if (gr == NULL)
- return -1;
+ GIDC *ptr, **pptr;
+ size_t namelen;
+
+ /*
+ * return -1 for mangled names
+ */
+ if (name == NULL || ((namelen = strlen(name)) == 0))
+ return (-1);
+ if ((grptb == NULL) && (grptb_start() < 0))
+ return (-1);
+ /*
+ * look up in hash table, if found and valid return the uid,
+ * if found and invalid, return a -1
+ */
+ pptr = grptb + st_hash(name, namelen, GID_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+ if (ptr->valid == INVALID)
+ return (-1);
+ *gid = ptr->gid;
+ return (0);
+ }
+
+ if (!gropn) {
+ if (_pwcache_setgroupent != NULL)
+ (*_pwcache_setgroupent)(1);
+ ++gropn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
+
+ /*
+ * no match, look it up, if no match store it as an invalid entry,
+ * or store the matching gid
+ */
+ if (ptr == NULL) {
+ if ((gr = (*_pwcache_getgrnam)(name)) == NULL)
+ return (-1);
*gid = gr->gr_gid;
- return 0;
+ return (0);
+ }
+
+ (void)strlcpy(ptr->name, name, GNMLEN);
+ if ((gr = (*_pwcache_getgrnam)(name)) == NULL) {
+ ptr->valid = INVALID;
+ return (-1);
+ }
+ ptr->valid = VALID;
+ *gid = ptr->gid = gr->gr_gid;
+ return (0);
+}
+
+#define FLUSHTB(arr, len, fail) \
+ do { \
+ if (arr != NULL) { \
+ for (i = 0; i < len; i++) \
+ if (arr[i] != NULL) \
+ free(arr[i]); \
+ arr = NULL; \
+ } \
+ fail = 0; \
+ } while (/* CONSTCOND */ 0);
+
+int pwcache_userdb(int (*a_setpassent)(int), void (*a_endpwent)(void),
+ struct passwd *(*a_getpwnam)(const char *),
+ struct passwd *(*a_getpwuid)(uid_t)) {
+ int i;
+
+ /* a_setpassent and a_endpwent may be NULL */
+ if (a_getpwnam == NULL || a_getpwuid == NULL)
+ return (-1);
+
+ if (_pwcache_endpwent != NULL)
+ (*_pwcache_endpwent)();
+ FLUSHTB(uidtb, UID_SZ, uidtb_fail);
+ FLUSHTB(usrtb, UNM_SZ, usrtb_fail);
+ pwopn = 0;
+ _pwcache_setpassent = a_setpassent;
+ _pwcache_endpwent = a_endpwent;
+ _pwcache_getpwnam = a_getpwnam;
+ _pwcache_getpwuid = a_getpwuid;
+
+ return (0);
+}
+
+int pwcache_groupdb(int (*a_setgroupent)(int), void (*a_endgrent)(void),
+ struct group *(*a_getgrnam)(const char *),
+ struct group *(*a_getgrgid)(gid_t)) {
+ int i;
+
+ /* a_setgroupent and a_endgrent may be NULL */
+ if (a_getgrnam == NULL || a_getgrgid == NULL)
+ return (-1);
+
+ if (_pwcache_endgrent != NULL)
+ (*_pwcache_endgrent)();
+ FLUSHTB(gidtb, GID_SZ, gidtb_fail);
+ FLUSHTB(grptb, GNM_SZ, grptb_fail);
+ gropn = 0;
+ _pwcache_setgroupent = a_setgroupent;
+ _pwcache_endgrent = a_endgrent;
+ _pwcache_getgrnam = a_getgrnam;
+ _pwcache_getgrgid = a_getgrgid;
+
+ return (0);
}

diff:
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * 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.
+ *
+ * @(#)cache.h 8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Constants and data structures used to implement group and password file
+ * caches. Traditional passwd/group cache routines perform quite poorly with
+ * archives. The chances of hitting a valid lookup with an archive is quite a
+ * bit worse than with files already resident on the file system. These misses
+ * create a MAJOR performance cost. To address this problem, these routines
+ * cache both hits and misses.
+ *
+ * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and
+ * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME
+ */
+#define UNMLEN 32 /* >= user name found in any protocol */
+#define GNMLEN 32 /* >= group name found in any protocol */
+#define UID_SZ 317 /* size of uid to user_name cache */
+#define UNM_SZ 317 /* size of user_name to uid cache */
+#define GID_SZ 251 /* size of gid to group_name cache */
+#define GNM_SZ 251 /* size of group_name to gid cache */
+#define VALID 1 /* entry and name are valid */
+#define INVALID 2 /* entry valid, name NOT valid */
+
+/*
+ * Node structures used in the user, group, uid, and gid caches.
+ */
+
+typedef struct uidc {
+ int valid; /* is this a valid or a miss entry */
+ char name[UNMLEN]; /* uid name */
+ uid_t uid; /* cached uid */
+} UIDC;
+
+typedef struct gidc {
+ int valid; /* is this a valid or a miss entry */
+ char name[GNMLEN]; /* gid name */
+ gid_t gid; /* cached gid */
+} GIDC;

original_message: Port pwcache.c from NetBSD (#915)


diff:
@@ -69,11 +69,12 @@ static int vfs_create(proc_t *p, int fdat, char *pathname, int *flags, int mode,
vnode_put(vs.vs_dvp);
goto fail;
}
- vattr_t va;
+ vattr_t va, dva;
+ VOP_GETATTR(vs.vs_dvp, &dva);
vattr_null(&va);
va.va_mode = S_IFREG | (mode & ALLPERMS);
- va.va_uid = p->p_cred.cr_ruid;
- va.va_gid = p->p_cred.cr_rgid;
+ va.va_uid = p->p_cred.cr_euid;
+ va.va_gid = dva.va_mode & S_ISGID ? dva.va_gid : p->p_cred.cr_egid;
error = VOP_CREATE(vs.vs_dvp, &vs.vs_lastcn, &va, &vs.vs_vp);
vnode_put(vs.vs_dvp);
} else {
@@ -271,7 +272,7 @@ fail:
int do_mkdirat(proc_t *p, int fd, char *path, mode_t mode) {
vnrstate_t vs;
- vattr_t va;
+ vattr_t va, dva;
int error;
if ((error = vnrstate_init(&vs, VNR_CREATE, VNR_FOLLOW, path, &p->p_cred)))
@@ -291,13 +292,20 @@ int do_mkdirat(proc_t *p, int fd, char *path, mode_t mode) {
goto fail;
}
+ VOP_GETATTR(vs.vs_dvp, &dva);
memset(&va, 0, sizeof(vattr_t));
/* We discard all bits but permission bits, since it is
* implementation-defined.
* https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html */
va.va_mode = S_IFDIR | ((mode & ACCESSPERMS) & ~p->p_cmask);
- va.va_uid = p->p_cred.cr_ruid;
- va.va_gid = p->p_cred.cr_rgid;
+ va.va_uid = p->p_cred.cr_euid;
+ if (dva.va_mode & S_ISGID) {
+ /* We propagate set-group-id down */
+ va.va_mode |= S_ISGID;
+ va.va_gid = dva.va_gid;
+ } else {
+ va.va_gid = p->p_cred.cr_egid;
+ }
error = VOP_MKDIR(vs.vs_dvp, &vs.vs_lastcn, &va, &vs.vs_vp);
if (!error)

original_message: Give correct ownership when file is begin created (#926)


message: libc: add code needed to compile login

diff:
#define _PATH_SHELLS "/etc/shells"
#define _PATH_TTY "/dev/tty"
#define _PATH_URANDOM "/dev/urandom"
+#define _PATH_NOLOGIN "/etc/nologin"
#endif /* !_PATHS_H_ */

diff:
#define _PATH_PASSWD "/etc/passwd"
#define _PATH_MASTERPASSWD "/etc/master.passwd"
#define _PASSWORD_LEN 128
+#define _PASSWORD_WARNDAYS 14
+/* special day to force password change at next login */
+#define _PASSWORD_CHGNOW -1
struct passwd {
const char *pw_name; /* user name */

diff:
#define _SYS_FILE_H_
#include <sys/cdefs.h>
-#include <sys/mutex.h>
#include <sys/fcntl.h>
+
+#ifdef _KERNEL
+#include <sys/mutex.h>
#include <sys/refcnt.h>
typedef struct file file_t;
@@ -95,4 +97,6 @@ int do_fcntl(proc_t *p, int fd, int cmd, int arg, int *resp);
int do_ioctl(proc_t *p, int fd, u_long cmd, void *data);
int do_umask(proc_t *p, int newmask, int *oldmaskp);
+#endif /* !_KERNEL */
+
#endif /* !_SYS_FILE_H_ */

diff:
#include <sys/inttypes.h>
#define NGROUPS NGROUPS_MAX /* max number groups */
+#define MAXLOGNAME (LOGIN_NAME_MAX - 1) /* max login name length */
/* Macros for min/max. */
#define MIN(a, b) (((a) < (b)) ? (a) : (b))

diff:
+/* $NetBSD: sysexits.h,v 1.7 2005/09/30 20:56:19 rpaulo Exp $ */
+
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * @(#)sysexits.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _SYSEXITS_H_
+#define _SYSEXITS_H_
+
+/*
+ * SYSEXITS.H -- Exit status codes for system programs.
+ *
+ * This include file attempts to categorize possible error
+ * exit statuses for system programs, notably delivermail
+ * and the Berkeley network.
+ *
+ * Error numbers begin at EX__BASE to reduce the possibility of
+ * clashing with other exit statuses that random programs may
+ * already return. The meaning of the codes is approximately
+ * as follows:
+ *
+ * EX_USAGE -- The command was used incorrectly, e.g., with
+ * the wrong number of arguments, a bad flag, a bad
+ * syntax in a parameter, or whatever.
+ * EX_DATAERR -- The input data was incorrect in some way.
+ * This should only be used for user's data & not
+ * system files.
+ * EX_NOINPUT -- An input file (not a system file) did not
+ * exist or was not readable. This could also include
+ * errors like "No message" to a mailer (if it cared
+ * to catch it).
+ * EX_NOUSER -- The user specified did not exist. This might
+ * be used for mail addresses or remote logins.
+ * EX_NOHOST -- The host specified did not exist. This is used
+ * in mail addresses or network requests.
+ * EX_UNAVAILABLE -- A service is unavailable. This can occur
+ * if a support program or file does not exist. This
+ * can also be used as a catchall message when something
+ * you wanted to do doesn't work, but you don't know
+ * why.
+ * EX_SOFTWARE -- An internal software error has been detected.
+ * This should be limited to non-operating system related
+ * errors as possible.
+ * EX_OSERR -- An operating system error has been detected.
+ * This is intended to be used for such things as "cannot
+ * fork", "cannot create pipe", or the like. It includes
+ * things like getuid returning a user that does not
+ * exist in the passwd file.
+ * EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp,
+ * etc.) does not exist, cannot be opened, or has some
+ * sort of error (e.g., syntax error).
+ * EX_CANTCREAT -- A (user specified) output file cannot be
+ * created.
+ * EX_IOERR -- An error occurred while doing I/O on some file.
+ * EX_TEMPFAIL -- temporary failure, indicating something that
+ * is not really an error. In sendmail, this means
+ * that a mailer (e.g.) could not create a connection,
+ * and the request should be reattempted later.
+ * EX_PROTOCOL -- the remote system returned something that
+ * was "not possible" during a protocol exchange.
+ * EX_NOPERM -- You did not have sufficient permission to
+ * perform the operation. This is not intended for
+ * file system problems, which should use NOINPUT or
+ * CANTCREAT, but rather for higher level permissions.
+ *
+ * Please update the sysexits(3) man page after adding more entries.
+ */
+
+#define EX_OK 0 /* successful termination */
+
+#define EX__BASE 64 /* base value for error messages */
+
+#define EX_USAGE 64 /* command line usage error */
+#define EX_DATAERR 65 /* data format error */
+#define EX_NOINPUT 66 /* cannot open input */
+#define EX_NOUSER 67 /* addressee unknown */
+#define EX_NOHOST 68 /* host name unknown */
+#define EX_UNAVAILABLE 69 /* service unavailable */
+#define EX_SOFTWARE 70 /* internal software error */
+#define EX_OSERR 71 /* system error (e.g., can't fork) */
+#define EX_OSFILE 72 /* critical OS file missing */
+#define EX_CANTCREAT 73 /* can't create (user) output file */
+#define EX_IOERR 74 /* input/output error */
+#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
+#define EX_PROTOCOL 76 /* remote error in protocol */
+#define EX_NOPERM 77 /* permission denied */
+#define EX_CONFIG 78 /* configuration error */
+
+#define EX__MAX 78 /* maximum listed value */
+
+#endif /* !_SYSEXITS_H_ */

diff:
@@ -26,6 +26,7 @@ int execlp(const char *, const char *, ...);
int execv(const char *, char *const *);
int execve(const char *, char *const *, char *const *);
int execvp(const char *, char *const *);
+int execvpe(const char *, char *const *, char *const *);
pid_t fork(void);
long fpathconf(int, int);
char *getcwd(char *, size_t);
@@ -59,7 +60,7 @@ unsigned int sleep(unsigned int);
long sysconf(int);
pid_t tcgetpgrp(int);
int tcsetpgrp(int, pid_t);
-const char *ttyname(int);
+char *ttyname(int);
int unlink(const char *);
ssize_t write(int, const void *, size_t);

diff:
+/* $NetBSD: execlp.c,v 1.13 2014/09/26 19:28:03 christos Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+int execlp(const char *name, const char *arg, ...) {
+ va_list ap;
+ char **argv;
+ size_t i;
+
+ va_start(ap, arg);
+ for (i = 2; va_arg(ap, char *) != NULL; i++)
+ continue;
+ va_end(ap);
+
+ argv = alloca(i * sizeof(char *));
+
+ va_start(ap, arg);
+ argv[0] = __UNCONST(arg);
+ for (i = 1; (argv[i] = va_arg(ap, char *)) != NULL; i++)
+ continue;
+ va_end(ap);
+
+ return execvp(name, argv);
+}
+
+int execlpe(const char *name, const char *arg, ...) {
+ va_list ap;
+ char **argv, **envp;
+ size_t i;
+
+ va_start(ap, arg);
+ for (i = 2; va_arg(ap, char *) != NULL; i++)
+ continue;
+ va_end(ap);
+
+ argv = alloca(i * sizeof(char *));
+
+ va_start(ap, arg);
+ argv[0] = __UNCONST(arg);
+ for (i = 1; (argv[i] = va_arg(ap, char *)) != NULL; i++)
+ continue;
+ envp = va_arg(ap, char **);
+ va_end(ap);
+
+ return execvpe(name, argv, envp);
+}

diff:
#include <errno.h>
#include <unistd.h>
-const char *ttyname(int fd) {
+char *ttyname(int fd) {
/* TODO(fzdob): to implement */
errno = ENOTTY;
return NULL;

original_message: libc: add code needed to compile login (#949)


diff:
@@ -70,7 +70,11 @@ static int vfs_create(proc_t *p, int fdat, char *pathname, int *flags, int mode,
goto fail;
}
vattr_t va, dva;
- VOP_GETATTR(vs.vs_dvp, &dva);
+ if ((error = VOP_GETATTR(vs.vs_dvp, &dva))) {
+ vnode_put(vs.vs_dvp);
+ goto fail;
+ }
+
vattr_null(&va);
va.va_mode = S_IFREG | (mode & ALLPERMS);
va.va_uid = p->p_cred.cr_euid;
@@ -292,7 +296,11 @@ int do_mkdirat(proc_t *p, int fd, char *path, mode_t mode) {
goto fail;
}
- VOP_GETATTR(vs.vs_dvp, &dva);
+ if ((error = VOP_GETATTR(vs.vs_dvp, &dva))) {
+ vnode_put(vs.vs_dvp);
+ goto fail;
+ }
+
memset(&va, 0, sizeof(vattr_t));
/* We discard all bits but permission bits, since it is
* implementation-defined.

original_message: mkdir, creat: return errors from VOP_GETATTR (#960)


diff:
TOPDIR = $(realpath ..)
-SUBDIR = cat chmod chown date echo kill ksh ln ls mandelbrot mkdir pwd \
+SUBDIR = cat chmod chown date echo kill ksh ln ls mandelbrot mkdir ps pwd \
rm rmdir sandbox stty test_kbd test_rtc utest
all: build

new_path: bin/ps/Makefile

diff:
+TOPDIR = $(realpath ../..)
+
+SCRIPT = ps
+
+include $(TOPDIR)/build/build.script.mk

new_path: bin/ps/ps

diff:
+#!/bin/ksh
+echo "euid pid ppid pgrp session state elfpath"
+cat /dev/procstat

diff:
+# Common makefile for installing userspace scripts.
+#
+# This template assumes following make variables are set:
+# SCRIPT: The name of script.
+
+ifndef SCRIPT
+$(error SCRIPT is not set)
+endif
+
+BINDIR ?= $(shell echo $(DIR) | cut -f 1 -d / | tr . /)
+
+INSTALL-FILES += $(SYSROOT)/$(BINDIR)/$(SCRIPT)
+BINMODE ?= 0755
+
+
+include $(TOPDIR)/build/common.mk
+
+$(SYSROOT)/$(BINDIR)/$(SCRIPT): $(SCRIPT)
+ @echo "[INSTALL] $(DIR)$< -> /$(BINDIR)/$(SCRIPT)"
+ $(INSTALL) -D $(SCRIPT) --mode=$(BINMODE) $(SYSROOT)/$(BINDIR)/$(SCRIPT)

diff:
@@ -23,6 +23,7 @@ typedef TAILQ_HEAD(, pgrp) pgrp_list_t;
typedef TAILQ_HEAD(, session) session_list_t;
extern mtx_t *all_proc_mtx;
+extern proc_list_t proc_list, zombie_list;
/*! \brief Called during kernel initialization. */
void init_proc(void);

diff:
@@ -234,9 +234,10 @@ void vnode_put(vnode_t *v);
bool vnode_is_mounted(vnode_t *v);
/* Convenience function with default vnode operation implementation. */
-int vnode_open_generic(vnode_t *v, int mode, file_t *fp);
int vnode_seek_generic(vnode_t *v, off_t oldoff, off_t newoff);
int vnode_access_generic(vnode_t *v, accmode_t mode, cred_t *cred);
+/* When successful increments reference counter for given vnode.*/
+int vnode_open_generic(vnode_t *v, int mode, file_t *fp);
/* Default fileops implementations for files with v-nodes. */
int default_vnread(file_t *f, uio_t *uio);

old_path: sys/kern/Makefile

new_path: sys/kern/Makefile

diff:
@@ -16,6 +16,7 @@ SOURCES = \
device.c \
dev_cons.c \
dev_null.c \
+ dev_procstat.c \
dev_vga.c \
devfs.c \
exception.c \

diff:
+#include <sys/devfs.h>
+#include <sys/vnode.h>
+#include <sys/linker_set.h>
+#include <sys/queue.h>
+#include <sys/proc.h>
+#include <sys/thread.h>
+#include <sys/malloc.h>
+#include <stdio.h>
+
+/* Implementation of /dev/procstat
+ *
+ * When /dev/procstat is opened it takes a snapshot of info of all existing
+ * processes.
+ *
+ * During read calls to /dev/procstat this info can be read.
+ *
+ * Example:
+ * euid pid ppid pgrp session state elfpath
+ * 0 1 0 1 1 R /bin/ksh
+ */
+
+/* maximum size of output string of proc info
+ * name + path + state (1 character)+ spaces (10 characters) +
+ * + 5 * max length of uint32 (10 characters)*/
+#define MAX_P_STRING (PATH_MAX + 5 * 10 + 1 + 10)
+
+/* maximum amount of processes that procstat can handle */
+#define MAX_PROC 40
+
+/* we want to have at most 10 instances of procstat */
+#define MAX_PROCSTAT 10
+
+static char proc_state[4] = {
+ [PS_NORMAL] = 'R',
+ [PS_STOPPED] = 'S',
+ [PS_DYING] = 'D',
+ [PS_ZOMBIE] = 'Z',
+};
+
+typedef struct ps_entry {
+ pid_t pid;
+ pid_t ppid;
+ uid_t uid;
+ pgid_t pgrp;
+ sid_t sid;
+ proc_state_t proc_state;
+ char *elfpath;
+} ps_entry_t;
+
+typedef struct ps_buf {
+ mtx_t lock;
+ int nproc;
+ int cur_proc;
+ size_t offset; /* offset in buffer of current proc string */
+ size_t psize; /* size of current proc string */
+ char buf[MAX_P_STRING];
+ ps_entry_t ps[];
+} ps_buf_t;
+
+static int ps_opencnt = 0;
+static mtx_t procstat_lock;
+
+static int dev_procstat_read(file_t *f, uio_t *uio);
+static int dev_procstat_close(vnode_t *v, file_t *fp);
+static int dev_procstat_open(vnode_t *v, int mode, file_t *fp);
+
+static fileops_t dev_procstat_fileops = {
+ .fo_read = dev_procstat_read,
+ .fo_close = default_vnclose,
+ .fo_stat = default_vnstat,
+ .fo_ioctl = default_vnioctl,
+};
+
+static vnodeops_t dev_procstat_vnodeops = {
+ .v_open = dev_procstat_open,
+ .v_close = dev_procstat_close,
+};
+
+static void ps_entry_fill(ps_entry_t *pe, proc_t *p) {
+ SCOPED_MTX_LOCK(&p->p_lock);
+ pe->uid = p->p_cred.cr_euid;
+ pe->pid = p->p_pid;
+ pe->ppid = p->p_parent->p_pid;
+ pe->pgrp = p->p_pgrp->pg_id;
+ pe->sid = p->p_pgrp->pg_session->s_sid;
+ pe->proc_state = p->p_state;
+ pe->elfpath = kstrndup(M_TEMP, p->p_elfpath, PATH_MAX);
+}
+
+/* buf must be at least MAX_P_STRING long
+ * returns length of written string
+ */
+static int ps_entry_tostring(char *buf, ps_entry_t *pe) {
+ int r = snprintf(buf, MAX_P_STRING, "%d\t%d\t%d\t%d\t%d\t%c\t%s\n", pe->uid,
+ pe->pid, pe->ppid, pe->pgrp, pe->sid,
+ proc_state[pe->proc_state], pe->elfpath);
+
+ return MIN(r, MAX_P_STRING);
+}
+
+static int dev_procstat_open(vnode_t *v, int mode, file_t *fp) {
+ WITH_MTX_LOCK (&procstat_lock) {
+ if (ps_opencnt >= MAX_PROCSTAT)
+ return EMFILE;
+ ps_opencnt++;
+ assert(ps_opencnt <= MAX_PROCSTAT);
+ }
+
+ int error = 0;
+ proc_t *p;
+ ps_buf_t *ps;
+
+ if ((error = vnode_open_generic(v, mode, fp))) {
+ WITH_MTX_LOCK (&procstat_lock) {
+ ps_opencnt--;
+ assert(ps_opencnt >= 0);
+ }
+ return error;
+ }
+
+ ps = kmalloc(M_TEMP, MAX_PROC * sizeof(ps_entry_t) + sizeof(ps_buf_t), 0);
+
+ ps->nproc = 0;
+ mtx_init(&ps->lock, 0);
+
+ WITH_MTX_LOCK (all_proc_mtx) {
+ TAILQ_FOREACH (p, &proc_list, p_all) {
+ if (p->p_pid == 0)
+ continue; /* we don't want to show proc0 to user */
+ if (ps->nproc >= MAX_PROC)
+ goto out;
+ ps_entry_fill(&ps->ps[ps->nproc++], p);
+ }
+ TAILQ_FOREACH (p, &zombie_list, p_zombie) {
+ if (ps->nproc >= MAX_PROC)
+ goto out;
+ ps_entry_fill(&ps->ps[ps->nproc++], p);
+ }
+ }
+
+out:
+ /* get first proc into buffer */
+ ps->psize = ps_entry_tostring(ps->buf, &ps->ps[0]);
+ ps->offset = 0;
+ ps->cur_proc = 0;
+
+ fp->f_ops = &dev_procstat_fileops;
+ fp->f_data = ps;
+ return 0;
+}
+
+static int dev_procstat_read(file_t *f, uio_t *uio) {
+ ps_buf_t *ps = f->f_data;
+ int error = 0;
+
+ SCOPED_MTX_LOCK(&ps->lock);
+
+ if (ps->cur_proc >= ps->nproc)
+ return 0;
+
+ uio->uio_offset = f->f_offset;
+ while (uio->uio_resid > 0) {
+ size_t len = MIN(uio->uio_resid, ps->psize - ps->offset);
+ if ((error = uiomove(ps->buf, len, uio)))
+ break;
+
+ ps->offset += len;
+
+ /* have to jump to next process info string */
+ if (ps->offset >= ps->psize) {
+ if (++ps->cur_proc >= ps->nproc)
+ break;
+ ps->psize = ps_entry_tostring(ps->buf, &ps->ps[ps->cur_proc]);
+ ps->offset = 0;
+ }
+ }
+
+ f->f_offset = uio->uio_offset;
+ return error;
+}
+
+static int dev_procstat_close(vnode_t *v, file_t *fp) {
+ ps_buf_t *ps = fp->f_data;
+ for (int i = 0; i < ps->nproc; ++i) {
+ kfree(M_TEMP, ps->ps[i].elfpath);
+ }
+ kfree(M_TEMP, ps);
+
+ WITH_MTX_LOCK (&procstat_lock) {
+ ps_opencnt--;
+ assert(ps_opencnt >= 0);
+ }
+ return 0;
+}
+
+static void init_dev_procstat(void) {
+ mtx_init(&procstat_lock, 0);
+ devfs_makedev(NULL, "procstat", &dev_procstat_vnodeops, NULL, NULL);
+}
+
+SET_ENTRY(devfs_init, init_dev_procstat);

diff:
@@ -42,8 +42,8 @@ static POOL_DEFINE(P_SESSION, "session", sizeof(session_t));
mtx_t *all_proc_mtx = &MTX_INITIALIZER(0);
/* all_proc_mtx protects following data: */
-static proc_list_t proc_list = TAILQ_HEAD_INITIALIZER(proc_list);
-static proc_list_t zombie_list = TAILQ_HEAD_INITIALIZER(zombie_list);
+proc_list_t proc_list = TAILQ_HEAD_INITIALIZER(proc_list);
+proc_list_t zombie_list = TAILQ_HEAD_INITIALIZER(zombie_list);
static pgrp_list_t pgrp_list = TAILQ_HEAD_INITIALIZER(pgrp_list);
static proc_t *proc_find_raw(pid_t pid);

original_message: procstat device: reports list of all processes (#974)


old_path: bin/ps/ps

diff:
#!/bin/ksh
-echo "euid pid ppid pgrp session state elfpath"
+echo "euid pid ppid pgrp session state command"
cat /dev/procstat

old_path: bin/utest/Makefile

new_path: bin/utest/Makefile

diff:
@@ -13,6 +13,7 @@ SOURCES = \
misbehave.c \
mmap.c \
pgrp.c \
+ procstat.c \
pty.c \
sbrk.c \
signal.c \

diff:
@@ -122,6 +122,9 @@ int main(int argc, char **argv) {
CHECKRUN_TEST(tty_canon);
CHECKRUN_TEST(tty_echo);
CHECKRUN_TEST(tty_signals);
+
+ CHECKRUN_TEST(procstat);
+
printf("No user test \"%s\" available.\n", test_name);
return 1;
}

diff:
+#include <stdio.h>
+#include <assert.h>
+
+int test_procstat(void) {
+ int euid, pid, ppid, pgrp, session, got;
+ char state;
+#define PROC_COMM_MAX 128
+ char command[PROC_COMM_MAX];
+
+ FILE *pstat = fopen("/dev/procstat", "r");
+ assert(pstat != NULL);
+
+ for (;;) {
+ if ((got = fscanf(pstat, "%d\t%d\t%d\t%d\t%d\t%c\t", &euid, &pid, &ppid,
+ &pgrp, &session, &state)) == -1)
+ break;
+
+ if (got < 6)
+ return 1;
+
+ if ((fgets(command, PROC_COMM_MAX, pstat)) == NULL)
+ return 1;
+ }
+ return 0;
+}

diff:
@@ -117,4 +117,6 @@ int test_tty_canon(void);
int test_tty_echo(void);
int test_tty_signals(void);
+int test_procstat(void);
+
#endif /* __UTEST_H__ */

diff:
@@ -101,6 +101,7 @@ struct proc {
pid_t p_pid; /* (!) Process ID */
cred_t p_cred; /* (@, *) Process credentials */
char *p_elfpath; /* (!) path of loaded elf file */
+ char *p_args; /* (!) prefix of process arguments */
TAILQ_ENTRY(proc) p_pglist; /* (g + a) link on pg_members list */
pgrp_t *p_pgrp; /* (@ + a) process group */
volatile proc_state_t p_state; /* (@) process state */

diff:
#define PATH_MAX 1024 /* max bytes in pathname */
#define PIPE_BUF 512 /* max bytes for atomic pipe writes */
#define IOV_MAX 1024 /* max elements in i/o vector */
+#define PARGS_MAX 100 /* max length of stored process arguments */
#define BC_BASE_MAX 99 /* max ibase/obase values in bc(1) */
#define BC_DIM_MAX 2048 /* max array elements in bc(1) */

diff:
* During read calls to /dev/procstat this info can be read.
*
* Example:
- * euid pid ppid pgrp session state elfpath
- * 0 1 0 1 1 R /bin/ksh
+ * euid pid ppid pgrp session state command
+ * 0 1 0 1 1 R ls -l
*/
+/* length of displayed command of process (includes terminating null byte) */
+#define PROC_COMM_MAX 128
+
/* maximum size of output string of proc info
- * name + path + state (1 character)+ spaces (10 characters) +
+ * command + state (1 character)+ spaces (10 characters) +
* + 5 * max length of uint32 (10 characters)*/
-#define MAX_P_STRING (PATH_MAX + 5 * 10 + 1 + 10)
+#define MAX_P_STRING (PROC_COMM_MAX + 1 + 10 + 5 * 10)
/* maximum amount of processes that procstat can handle */
#define MAX_PROC 40
/* we want to have at most 10 instances of procstat */
#define MAX_PROCSTAT 10
+/* string used when process has no p_elfpath or p_args */
+#define NONAME "-"
+
static char proc_state[4] = {
[PS_NORMAL] = 'R',
[PS_STOPPED] = 'S',
@@ -44,7 +50,7 @@ typedef struct ps_entry {
pgid_t pgrp;
sid_t sid;
proc_state_t proc_state;
- char *elfpath;
+ char *command;
} ps_entry_t;
typedef struct ps_buf {
@@ -76,6 +82,39 @@ static vnodeops_t dev_procstat_vnodeops = {
.v_close = dev_procstat_close,
};
+static char *get_command(proc_t *p) {
+ char *buf = kmalloc(M_TEMP, PROC_COMM_MAX, M_ZERO);
+ ssize_t left = PROC_COMM_MAX, used = 0;
+ char *command;
+
+ /* copy program's name from elfpath */
+ if (p->p_elfpath) {
+ command = strrchr(p->p_elfpath, '/');
+ if (command == NULL)
+ command = p->p_elfpath;
+ else
+ /* omit slash */
+ command += 1;
+ } else {
+ command = NONAME;
+ }
+
+ ssize_t wanted = strlcpy(buf, command, left);
+ used += min(wanted, left - 1);
+ left = PROC_COMM_MAX - used;
+
+ /* if there is enough space append space after program name */
+ if (left > 1) {
+ buf[used++] = ' ';
+ left--;
+ }
+
+ char *args = p->p_args ? p->p_args : NONAME;
+ strlcpy(buf + used, args, left);
+
+ return buf;
+}
+
static void ps_entry_fill(ps_entry_t *pe, proc_t *p) {
SCOPED_MTX_LOCK(&p->p_lock);
pe->uid = p->p_cred.cr_euid;
@@ -84,7 +123,7 @@ static void ps_entry_fill(ps_entry_t *pe, proc_t *p) {
pe->pgrp = p->p_pgrp->pg_id;
pe->sid = p->p_pgrp->pg_session->s_sid;
pe->proc_state = p->p_state;
- pe->elfpath = kstrndup(M_TEMP, p->p_elfpath, PATH_MAX);
+ pe->command = get_command(p);
}
/* buf must be at least MAX_P_STRING long
@@ -93,7 +132,7 @@ static void ps_entry_fill(ps_entry_t *pe, proc_t *p) {
static int ps_entry_tostring(char *buf, ps_entry_t *pe) {
int r = snprintf(buf, MAX_P_STRING, "%d\t%d\t%d\t%d\t%d\t%c\t%s\n", pe->uid,
pe->pid, pe->ppid, pe->pgrp, pe->sid,
- proc_state[pe->proc_state], pe->elfpath);
+ proc_state[pe->proc_state], pe->command);
return min(r, MAX_P_STRING);
}
@@ -182,7 +221,7 @@ static int dev_procstat_read(file_t *f, uio_t *uio) {
static int dev_procstat_close(vnode_t *v, file_t *fp) {
ps_buf_t *ps = fp->f_data;
for (int i = 0; i < ps->nproc; ++i) {
- kfree(M_TEMP, ps->ps[i].elfpath);
+ kfree(M_TEMP, ps->ps[i].command);
}
kfree(M_TEMP, ps);

diff:
@@ -323,6 +323,28 @@ static bool check_setid(vnode_t *vn, uid_t *uid, gid_t *gid) {
return (*uid != (uid_t)-1) || (*gid != (gid_t)-1);
}
+static char *pargs_create(exec_args_t *args) {
+ char *pargs = kmalloc(M_STR, PARGS_MAX, M_ZERO);
+ size_t used = 0;
+ size_t left = PARGS_MAX;
+
+ for (size_t i = 1; used + 1 < PARGS_MAX && i < args->argc; i++) {
+ size_t wanted = strlcpy(pargs + used, args->argv[i], left);
+
+ /* calculate how much we have copied (without terminating null byte) */
+ used += min(wanted, left - 1);
+ left = PARGS_MAX - used;
+
+ /* add space between args if there is space and it is not last argument*/
+ if (left > 1 && i + 1 < args->argc) {
+ pargs[used++] = ' ';
+ left--;
+ }
+ }
+
+ return pargs;
+}
+
/* XXX We assume process may only have a single thread. But if there were more
* than one thread in the process that called exec, all other threads must be
* forcefully terminated. */
@@ -383,6 +405,9 @@ static int _do_execve(exec_args_t *args) {
if ((error = exec_args_copyout(args, &stack_top)))
goto fail;
+ kfree(M_STR, p->p_args);
+ p->p_args = pargs_create(args);
+
fdtab_onexec(p->p_fdtable);
/* Set up user context. */

diff:
@@ -83,6 +83,7 @@ proc_t proc0 = {
.p_pgrp = &pgrp0,
.p_state = PS_NORMAL,
.p_children = TAILQ_HEAD_INITIALIZER(proc0.p_children),
+ .p_args = NULL,
};
void init_proc0(void) {
@@ -452,8 +453,10 @@ proc_t *proc_create(thread_t *td, proc_t *parent) {
p->p_thread = td;
p->p_parent = parent;
- if (parent && parent->p_elfpath)
+ if (parent->p_elfpath)
p->p_elfpath = kstrndup(M_STR, parent->p_elfpath, PATH_MAX);
+ if (parent->p_args)
+ p->p_args = kstrndup(M_STR, parent->p_args, PARGS_MAX);
TAILQ_INIT(CHILDREN(p));
kitimer_init(p);
@@ -528,6 +531,7 @@ static void proc_reap(proc_t *p) {
TAILQ_REMOVE(CHILDREN(p->p_parent), p, p_child);
TAILQ_REMOVE(&zombie_list, p, p_zombie);
kfree(M_STR, p->p_elfpath);
+ kfree(M_TEMP, p->p_args);
TAILQ_REMOVE(PROC_HASH_CHAIN(p->p_pid), p, p_hash);
pool_free(P_PROC, p);
}

diff:
@@ -179,3 +179,5 @@ UTEST_ADD_SIMPLE(pty_simple);
UTEST_ADD_SIMPLE(tty_canon);
UTEST_ADD_SIMPLE(tty_echo);
UTEST_ADD_SIMPLE(tty_signals);
+
+UTEST_ADD_SIMPLE(procstat);

original_message: procstat: construct shortened process cmdline during exec (#1043)


diff:
@@ -178,16 +178,17 @@ int do_setreuid(proc_t *p, uid_t ruid, uid_t euid) {
/* if ruid is set
* or euid is set to value other than current suid or ruid(and of course euid)
- * then suid is set to current euid
+ * then suid is set to new euid
*
* when we don't set ruid and euid we also don't set suid
* (the last condition in or)
*/
+ uid_t new_suid = euid == (uid_t)-1 ? cur_euid : euid;
if (ruid == (uid_t)-1 && (euid == cur_ruid || euid == cur_euid ||
euid == cur_suid || euid == (uid_t)-1))
- cur_euid = -1;
+ new_suid = -1;
- error = change_resuid(&p->p_cred, ruid, euid, cur_euid);
+ error = change_resuid(&p->p_cred, ruid, euid, new_suid);
end:
proc_unlock(p);
@@ -233,16 +234,17 @@ int do_setregid(proc_t *p, gid_t rgid, gid_t egid) {
/* if rgid is set
* or egid is set to value other than current sgid or rgid(and of course egid)
- * then sgid is set to current egid
+ * then sgid is set to new egid
*
* when we don't set rgid and egid we also don't set sgid
* (the last condition in or)
*/
+ gid_t new_sgid = egid == (gid_t)-1 ? cur_egid : egid;
if (rgid == (gid_t)-1 && (egid == cur_rgid || egid == cur_egid ||
egid == cur_sgid || egid == (gid_t)-1))
- cur_egid = -1;
+ new_sgid = -1;
- error = change_resgid(&p->p_cred, rgid, egid, cur_egid);
+ error = change_resgid(&p->p_cred, rgid, egid, new_sgid);
end:
proc_unlock(p);

original_message: setreuid: adjust semantics to match NetBSD (#1140)


diff:
@@ -66,8 +66,9 @@ bool cred_groupmember(gid_t gid, cred_t *cred);
/* VFS checks */
bool cred_can_chmod(uid_t f_owner, gid_t f_group, cred_t *cred, mode_t mode);
bool cred_can_chown(uid_t f_owner, cred_t *cred, uid_t new_uid, gid_t new_gid);
+bool cred_can_utime(vnode_t *vn, uid_t f_owner, cred_t *cred,
+ va_flags_t vaflags);
int cred_can_access(vattr_t *va, cred_t *cred, accmode_t mode);
-int cred_can_utime(vnode_t *vn, uid_t f_owner, cred_t *cred);
/* setlogin() check */
bool cred_can_setlogin(cred_t *cred);

diff:
@@ -24,6 +24,11 @@ typedef struct cred cred_t;
* vnodeops should not modify attributes set to VNOVAL. */
#define VNOVAL (-1)
+/* Flags for vattr:va_flags. */
+typedef enum {
+ VA_UTIMES_NULL = 1, /* Utimes argument was null. */
+} va_flags_t;
+
/* vnode access modes
* VADMIN - owner of file (root has VADMIN to all files) */
typedef enum { VEXEC = 1, VWRITE = 2, VREAD = 4, VADMIN = 8 } accmode_t;
@@ -113,6 +118,7 @@ typedef struct vattr {
uid_t va_uid; /* owner user id */
gid_t va_gid; /* owner group id */
size_t va_size; /* file size in bytes */
+ va_flags_t va_flags; /* additional flags */
timespec_t va_atime; /* time of last access */
timespec_t va_mtime; /* time of last data modification */
timespec_t va_ctime; /* time of last file status change */

diff:
@@ -123,9 +123,16 @@ check:
return (mode & granted) == mode ? 0 : EACCES;
}
-int cred_can_utime(vnode_t *vn, uid_t f_owner, cred_t *cred) {
- if (f_owner == cred->cr_euid)
- return 0;
+bool cred_can_utime(vnode_t *vn, uid_t f_owner, cred_t *cred,
+ va_flags_t vaflags) {
+ /* owner and root can change times */
+ if (f_owner == cred->cr_euid || cred->cr_euid == 0)
+ return true;
+
+ /* any other user can change times only to current time... */
+ if ((vaflags & VA_UTIMES_NULL) == 0)
+ return false;
- return VOP_ACCESS(vn, VWRITE, cred);
+ /* and only when he has write permission */
+ return VOP_ACCESS(vn, VWRITE, cred) == 0;
}

diff:
@@ -310,7 +310,7 @@ static void tmpfs_dir_detach(tmpfs_node_t *dv, tmpfs_dirent_t *de);
static blkptr_t *tmpfs_get_blk(tmpfs_node_t *v, size_t blkno);
static int tmpfs_resize(tmpfs_mount_t *tfm, tmpfs_node_t *v, size_t newsize);
static int tmpfs_chtimes(tmpfs_node_t *v, timespec_t *atime, timespec_t *mtime,
- cred_t *cred);
+ cred_t *cred, va_flags_t vaflags);
static void tmpfs_update_time(tmpfs_node_t *v, tmpfs_time_type_t type);
/* tmpfs readdir operations */
@@ -510,7 +510,8 @@ static int tmpfs_vop_setattr(vnode_t *v, vattr_t *va, cred_t *cred) {
}
if (va->va_atime.tv_sec != VNOVAL || va->va_mtime.tv_sec != VNOVAL) {
- if ((error = tmpfs_chtimes(node, &va->va_atime, &va->va_mtime, cred)))
+ if ((error = tmpfs_chtimes(node, &va->va_atime, &va->va_mtime, cred,
+ va->va_flags)))
return error;
}
@@ -997,10 +998,9 @@ static int tmpfs_resize(tmpfs_mount_t *tfm, tmpfs_node_t *v, size_t newsize) {
}
static int tmpfs_chtimes(tmpfs_node_t *v, timespec_t *atime, timespec_t *mtime,
- cred_t *cred) {
- int err;
- if ((err = cred_can_utime(v->tfn_vnode, v->tfn_uid, cred)))
- return err;
+ cred_t *cred, va_flags_t vaflags) {
+ if (!cred_can_utime(v->tfn_vnode, v->tfn_uid, cred, vaflags))
+ return EPERM;
mtx_lock(&v->tfn_timelock);
if (atime->tv_sec != VNOVAL)

diff:
@@ -699,6 +699,7 @@ static int vfs_utimens(vnode_t *v, timespec_t times[2], cred_t *cred) {
if (times == NULL) {
va.va_atime = va.va_mtime = nanotime();
+ va.va_flags = VA_UTIMES_NULL;
} else {
va.va_atime = times[0];
va.va_mtime = times[1];

diff:
@@ -174,6 +174,7 @@ void vattr_null(vattr_t *va) {
va->va_uid = VNOVAL;
va->va_gid = VNOVAL;
va->va_size = VNOVAL;
+ va->va_flags = 0;
va->va_atime.tv_sec = va->va_mtime.tv_sec = va->va_ctime.tv_sec = VNOVAL;
va->va_atime.tv_nsec = va->va_mtime.tv_nsec = va->va_ctime.tv_nsec = VNOVAL;
}

original_message: utimens: fix authorization during change of file timestamps (#1104)


message: [wiki] Update toolchain description

old_path: .gitignore

new_path: .gitignore

diff:
@@ -70,3 +70,6 @@ sysroot
# pcap files
*.pcap
+
+# python env
+mimiker-env/

old_path: Dockerfile

new_path: Dockerfile

diff:
@@ -2,28 +2,20 @@ FROM debian:bullseye-backports
WORKDIR /root
-RUN apt-get -q update && apt-get upgrade -y
-RUN apt-get install -y --no-install-recommends -t bullseye-backports \
- git make ccache cpio curl gnupg universal-ctags cscope socat patch \
- gperf quilt bmake byacc python3-pip device-tree-compiler tmux \
- libmpfr6 libfdt1 libpython3.9 libsdl2-2.0-0 libglib2.0-0 libpixman-1-0
-RUN apt-key adv --fetch-keys https://apt.llvm.org/llvm-snapshot.gpg.key
-RUN echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-14 main" > \
- /etc/apt/sources.list.d/llvm-14.list && apt-get update
-RUN apt-get install -y --no-install-recommends -t bullseye-backports \
+COPY packages.txt .
+RUN apt-get -q update && apt-get upgrade -y && \
+ apt-get install -y --no-install-recommends -t bullseye-backports $(cat packages.txt) && \
+ apt-key adv --fetch-keys https://apt.llvm.org/llvm-snapshot.gpg.key && \
+ echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-14 main" > \
+ /etc/apt/sources.list.d/llvm-14.list && apt-get update && \
+ apt-get install -y --no-install-recommends -t bullseye-backports \
clang-14 clang-format-14 llvm-14 lld-14
-# patch & quilt required by lua and programs in contrib/
-# gperf required by libterminfo
-# socat & tmux required by launch
+
COPY requirements.txt .
-RUN pip3 install setuptools wheel && pip3 install -r requirements.txt
-RUN curl -O https://mimiker.ii.uni.wroc.pl/download/mipsel-mimiker-elf_latest_amd64.deb && \
- dpkg -i mipsel-mimiker-elf_latest_amd64.deb && rm -f mipsel-mimiker-elf_latest_amd64.deb
-RUN curl -O https://mimiker.ii.uni.wroc.pl/download/aarch64-mimiker-elf_latest_amd64.deb && \
- dpkg -i aarch64-mimiker-elf_latest_amd64.deb && rm -f aarch64-mimiker-elf_latest_amd64.deb
-RUN curl -O https://mimiker.ii.uni.wroc.pl/download/riscv32-mimiker-elf_latest_amd64.deb && \
- dpkg -i riscv32-mimiker-elf_latest_amd64.deb && rm -f riscv32-mimiker-elf_latest_amd64.deb
-RUN curl -O https://mimiker.ii.uni.wroc.pl/download/riscv64-mimiker-elf_latest_amd64.deb && \
- dpkg -i riscv64-mimiker-elf_latest_amd64.deb && rm -f riscv64-mimiker-elf_latest_amd64.deb
-RUN curl -O https://mimiker.ii.uni.wroc.pl/download/qemu-mimiker_latest_amd64.deb && \
- dpkg -i qemu-mimiker_latest_amd64.deb && rm -f qemu-mimiker_latest_amd64.deb
+RUN pip3 install setuptools wheel && pip3 install -r requirements.txt && \
+ curl -O https://mimiker.ii.uni.wroc.pl/download/mipsel-mimiker-elf_latest_amd64.deb && \
+ curl -O https://mimiker.ii.uni.wroc.pl/download/aarch64-mimiker-elf_latest_amd64.deb && \
+ curl -O https://mimiker.ii.uni.wroc.pl/download/riscv32-mimiker-elf_latest_amd64.deb && \
+ curl -O https://mimiker.ii.uni.wroc.pl/download/riscv64-mimiker-elf_latest_amd64.deb && \
+ curl -O https://mimiker.ii.uni.wroc.pl/download/qemu-mimiker_latest_amd64.deb && \
+ apt-get install -y ./*.deb && rm -f *.deb

diff:
+#!/bin/bash
+nocolor='\033[0m'
+col_red='\033[0;31m'
+col_orange='\033[0;33m'
+col_blue='\033[0;34m'
+
+function fatal() {
+ echo -e "${col_red}ERROR${nocolor}: "$1
+ exit 1
+}
+
+function warn() {
+ echo -e "${col_orange}WARN${nocolor}: "$1
+}
+
+function info() {
+ echo -e "${col_blue}INFO${nocolor}: "$1
+}
+
+function ask() {
+ read -p "Do you want to proceed? (Y/n) " choice
+ case $choice in
+ [yY] ) return;;
+ [nN] ) echo exiting...;
+ exit 1;;
+ * ) echo invalid response. Assuming NO.;
+ exit 1;;
+ esac
+}
+
+# all checks before script is run
+[ "`id -u`" -eq 0 ] && fatal "You are running as root... it is not supported"
+[ -f /etc/apt/sources.list.d/llvm.list ] && warn 'You already have llvm sources.list. The script will override it.' && ask
+[ -f requirements.txt ] || fatal 'Python requirements not found. Are you in the Mimiker directory?'
+[ -f /usr/bin/apt ] || fatal '/usr/bin/apt is missing; is your distro debian based?'
+[ -f /usr/bin/sudo ] || fatal 'sudo is missing; (run `su -c "apt install sudo"`)'
+
+DEBS=packages.txt
+
+info "Install required packages"
+info "Upgrade privileges to root user"
+info "Install packages from base repository"
+
+sudo -u root sh -c "apt-get update -q && apt install $(< ${DEBS})"
+
+info "Drop privileges to $USER"
+
+read -p "Do you want to create python virtual env for Mimiker? (Y/n) " choice
+case $choice in
+ [yY] )
+ python3 -m venv mimiker-env
+ source mimiker-env/bin/activate
+ info "Python venv 'mimiker-env' created"
+ ;;
+esac
+
+info "Install python dependencies"
+pip3 install -r requirements.txt
+
+CODENAME=$(lsb_release -sc 2>/dev/null)
+
+if ! [ "$CODENAME" == "bullseye" ]; then
+ fatal "Your distribution '$CODENAME' is not supported"
+fi
+
+LLVM_VERSION=14
+DEBS_LLVM="clang-${LLVM_VERSION} clang-format-${LLVM_VERSION} llvm-${LLVM_VERSION} lld-${LLVM_VERSION}"
+
+info "Prepare sources.list for llvm && install llvm-${LLVM_VERSION}"
+LLVM_REPO="deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain-${CODENAME}-${LLVM_VERSION} main"
+sudo -u root sh -c "apt-key adv --fetch-keys https://apt.llvm.org/llvm-snapshot.gpg.key && echo ${LLVM_REPO} > /etc/apt/sources.list.d/llvm.list && apt-get update -q && apt-get install ${DEBS_LLVM}"
+
+WORKDIR=$(mktemp -d)
+info "${WORKDIR} created"
+info "Download prebuild toolchain"
+
+curl -o $WORKDIR/mipsel-mimiker-elf_latest_amd64.deb https://mimiker.ii.uni.wroc.pl/download/mipsel-mimiker-elf_latest_amd64.deb
+curl -o $WORKDIR/aarch64-mimiker-elf_latest_amd64.deb https://mimiker.ii.uni.wroc.pl/download/aarch64-mimiker-elf_latest_amd64.deb
+curl -o $WORKDIR/riscv32-mimiker-elf_latest_amd64.deb https://mimiker.ii.uni.wroc.pl/download/riscv32-mimiker-elf_latest_amd64.deb
+curl -o $WORKDIR/riscv64-mimiker-elf_latest_amd64.deb https://mimiker.ii.uni.wroc.pl/download/riscv64-mimiker-elf_latest_amd64.deb
+curl -o $WORKDIR/qemu-mimiker_latest_amd64.deb https://mimiker.ii.uni.wroc.pl/download/qemu-mimiker_latest_amd64.deb
+
+info "Install toolchain"
+sudo -u root sh -c "apt-get install $WORKDIR/*.deb"
+
+info "Cleanup $WORKDIR"
+rm -r $WORKDIR
+
+exit 0

diff: +git make ccache cpio curl gnupg universal-ctags cscope socat patch gperf quilt byacc python3 python3-pip python3-virtualenv device-tree-compiler tmux lsb-release

diff:
@@ -22,4 +22,7 @@ they are run, add `VERBOSE=1` to the command line.
**Important!** It's recommended to issue `make distclean` command after each
modification to `config.mk` file.
+**Important!** Before building Mimker for different board you have to run
+`make distclean` with `BOARD` argument set to previous board.
+
[1]: toolchain.md

original_message: [wiki] Update toolchain description (#1343)


diff:
@@ -84,8 +84,7 @@ TEST_ADD(get_set_groups) {
uid_t euid;
/* check if we are a root at start */
- getresuid(NULL, &euid, NULL);
- // TODO(fzdob): this fails with EFAULT
+ syscall_ok(getresuid(NULL, &euid, NULL));
assert(euid == 0);
syscall_ok(getgroups(0, NULL));

diff:
@@ -663,14 +663,22 @@ static int sys_getresuid(proc_t *p, getresuid_args_t *args, register_t *res) {
uid_t *usr_euid = SCARG(args, euid);
uid_t *usr_suid = SCARG(args, suid);
- klog("getresuid()");
+ klog("getresuid(%p, %p, %p)", usr_ruid, usr_euid, usr_suid);
uid_t ruid, euid, suid;
do_getresuid(p, &ruid, &euid, &suid);
- int err1 = copyout(&ruid, usr_ruid, sizeof(uid_t));
- int err2 = copyout(&euid, usr_euid, sizeof(uid_t));
- int err3 = copyout(&suid, usr_suid, sizeof(uid_t));
+ int err1, err2, err3;
+ err1 = err2 = err3 = 0;
+
+ if (usr_ruid != NULL)
+ err1 = copyout(&ruid, usr_ruid, sizeof(uid_t));
+
+ if (usr_euid != NULL)
+ err2 = copyout(&euid, usr_euid, sizeof(uid_t));
+
+ if (usr_suid != NULL)
+ err3 = copyout(&suid, usr_suid, sizeof(uid_t));
return err1 ? err1 : (err2 ? err2 : err3);
}
@@ -680,14 +688,22 @@ static int sys_getresgid(proc_t *p, getresgid_args_t *args, register_t *res) {
gid_t *usr_egid = SCARG(args, egid);
gid_t *usr_sgid = SCARG(args, sgid);
- klog("getresgid()");
+ klog("getresgid(%p, %p, %p)", usr_rgid, usr_egid, usr_sgid);
gid_t rgid, egid, sgid;
do_getresgid(p, &rgid, &egid, &sgid);
- int err1 = copyout(&rgid, usr_rgid, sizeof(gid_t));
- int err2 = copyout(&egid, usr_egid, sizeof(gid_t));
- int err3 = copyout(&sgid, usr_sgid, sizeof(gid_t));
+ int err1, err2, err3;
+ err1 = err2 = err3 = 0;
+
+ if (usr_rgid != NULL)
+ err1 = copyout(&rgid, usr_rgid, sizeof(gid_t));
+
+ if (usr_egid != NULL)
+ err2 = copyout(&egid, usr_egid, sizeof(gid_t));
+
+ if (usr_sgid != NULL)
+ err3 = copyout(&sgid, usr_sgid, sizeof(gid_t));
return err1 ? err1 : (err2 ? err2 : err3);
}

original_message: getresuid: support correctly NULL arguments (#1376)


message:
[BUG] Initialize variable to prevent error when not set
And do refactoring

diff:
@@ -511,6 +511,7 @@ class EmailAddress extends AbstractValidator
}
// Match hostname part
+ $hostname = false;
if ($this->options['useDomainCheck']) {
$hostname = $this->validateHostnamePart();
}
@@ -518,13 +519,7 @@ class EmailAddress extends AbstractValidator
$local = $this->validateLocalPart();
// If both parts valid, return true
- if ($local && $length) {
- if (($this->options['useDomainCheck'] && $hostname) || ! $this->options['useDomainCheck']) {
- return true;
- }
- }
-
- return false;
+ return ($local && $length) && (! $this->options['useDomainCheck'] || $hostname);
}
/**

diff:
@@ -685,6 +685,11 @@ class EmailAddressTest extends TestCase
$this->assertInstanceOf(Hostname::class, $hostname);
}
+ public function testIsMxSupported()
+ {
+ $validator = new EmailAddress(['useMxCheck' => true, 'allow' => Hostname::ALLOW_ALL]);
+ $this->assertInternalType('bool', $validator->isMxSupported());
+ }
/**
* Test getMXRecord
*/
@@ -692,7 +697,6 @@ class EmailAddressTest extends TestCase
{
$this->skipIfOnlineTestsDisabled();
- $validator = new EmailAddress(['useMxCheck' => true, 'allow' => Hostname::ALLOW_ALL]);
$validator = new EmailAddress(['useMxCheck' => true, 'allow' => Hostname::ALLOW_ALL]);
if (! $validator->isMxSupported()) {

repo: laminas/laminas-validator


message:
Fix Between validator behavior
return false when validating a string with a numeric range
return false when validating a numeric value with a string range

diff:
@@ -17,6 +17,13 @@ class Between extends AbstractValidator
const NOT_BETWEEN = 'notBetween';
const NOT_BETWEEN_STRICT = 'notBetweenStrict';
+ /**
+ * Retain if min and max are numeric values. Allow to not compare string and numeric types
+ *
+ * @var boolean
+ */
+ private $numeric;
+
/**
* Validation failure message template definitions
*
@@ -81,7 +88,17 @@ class Between extends AbstractValidator
if (count($options) !== 2
&& (! array_key_exists('min', $options) || ! array_key_exists('max', $options))
) {
- throw new Exception\InvalidArgumentException("Missing option. 'min' and 'max' have to be given");
+ throw new Exception\InvalidArgumentException("Missing option : 'min' and 'max' have to be given");
+ }
+
+ if (is_numeric($options['min']) && is_numeric($options['max'])) {
+ $this->numeric = true;
+ } elseif (is_string($options['min']) && is_string($options['max'])) {
+ $this->numeric = false;
+ } else {
+ throw new Exception\InvalidArgumentException(
+ "Invalid options : 'min' and 'max' should be of the same scalar type"
+ );
}
parent::__construct($options);
@@ -162,6 +179,13 @@ class Between extends AbstractValidator
*/
public function isValid($value)
{
+ if ($this->numeric && ! is_numeric($value)) {
+ return false;
+ }
+ if (! $this->numeric && ! is_string($value)) {
+ return false;
+ }
+
$this->setValue($value);
if ($this->getInclusive()) {

diff:
@@ -18,39 +18,78 @@ use Zend\Validator\Exception\InvalidArgumentException;
*/
class BetweenTest extends TestCase
{
+ public function providerBasic()
+ {
+ return [
+ [
+ 'min' => 1,
+ 'max' => 100,
+ 'inclusive' => true,
+ 'expected' => true,
+ 'values' => [1, 10, 100],
+ ],
+ [
+ 'min' => 1,
+ 'max' => 100,
+ 'inclusive' => true,
+ 'expected' => false,
+ 'values' => [0, 0.99, 100.01, 101],
+ ],
+ [
+ 'min' => 1,
+ 'max' => 100,
+ 'inclusive' => false,
+ 'expected' => false,
+ 'values' => [0, 1, 100, 101],
+ ],
+ [
+ 'min' => 'a',
+ 'max' => 'z',
+ 'inclusive' => true,
+ 'expected' => true,
+ 'values' => ['a', 'b', 'y', 'z'],
+ ],
+ [
+ 'min' => 'a',
+ 'max' => 'z',
+ 'inclusive' => false,
+ 'expected' => false,
+ 'values' => ['!', 'a', 'z'],
+ ],
+ [
+ 'min' => 0,
+ 'max' => 99999999,
+ 'inclusive' => true,
+ 'expected' => false,
+ 'values' => ['asdasd', 'q'],
+ ],
+ [
+ 'min' => 'a',
+ 'max' => 'zzzzz',
+ 'inclusive' => true,
+ 'expected' => false,
+ 'values' => [0, 10, 548],
+ ],
+ ];
+ }
/**
* Ensures that the validator follows expected behavior
*
+ * @dataProvider providerBasic
* @return void
*/
- public function testBasic()
+ public function testBasic($min, $max, $inclusive, $expected, $values)
{
- /**
- * The elements of each array are, in order:
- * - minimum
- * - maximum
- * - inclusive
- * - expected validation result
- * - array of test input values
- */
- $valuesExpected = [
- [1, 100, true, true, [1, 10, 100]],
- [1, 100, true, false, [0, 0.99, 100.01, 101]],
- [1, 100, false, false, [0, 1, 100, 101]],
- ['a', 'z', true, true, ['a', 'b', 'y', 'z']],
- ['a', 'z', false, false, ['!', 'a', 'z']]
- ];
- foreach ($valuesExpected as $element) {
- $validator = new Between(['min' => $element[0], 'max' => $element[1], 'inclusive' => $element[2]]);
- foreach ($element[4] as $input) {
+ $validator = new Between(['min' => $min, 'max' => $max, 'inclusive' => $inclusive]);
+
+ foreach ($values as $input) {
$this->assertEquals(
- $element[3],
+ $expected,
$validator->isValid($input),
'Failed values: ' . $input . ":" . implode("\n", $validator->getMessages())
);
}
}
- }
/**
* Ensures that getMessages() returns expected default value
@@ -117,7 +156,7 @@ class BetweenTest extends TestCase
public function testMissingMinOrMax(array $args)
{
$this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage("Missing option. 'min' and 'max' have to be given");
+ $this->expectExceptionMessage("Missing option : 'min' and 'max' have to be given");
new Between($args);
}


message: issue-125 - add unit test proving undefined index error


message: issue-201 - add tow countries for SEPA validation (croatia and san marino)


message: issue-#215 - fix the step validator caused by a precision error when substracting

diff:
@@ -122,7 +122,9 @@ class Step extends AbstractValidator
$this->setValue($value);
- $fmod = $this->fmod($value - $this->baseValue, $this->step);
+ $substract = $this->sub($value, $this->baseValue);
+
+ $fmod = $this->fmod($substract, $this->step);
if ($fmod !== 0.0 && $fmod !== $this->step) {
$this->error(self::NOT_STEP);
@@ -146,10 +148,27 @@ class Step extends AbstractValidator
}
//find the maximum precision from both input params to give accurate results
- $xFloatSegment = substr($x, strpos($x, '.') + 1) ?: '';
- $yFloatSegment = substr($y, strpos($y, '.') + 1) ?: '';
- $precision = strlen($xFloatSegment) + strlen($yFloatSegment);
+ $precision = $this->getPrecision($x) + $this->getPrecision($y);
return round($x - $y * floor($x / $y), $precision);
}
+
+ /**
+ * replaces the internal substraction operation which give wrong results on some cases
+ *
+ * @param float $x
+ * @param float $y
+ * @return float
+ */
+ private function sub($x, $y)
+ {
+ $precision = $this->getPrecision($x) + $this->getPrecision($y);
+ return round($x - $y, $precision);
+ }
+
+ private function getPrecision($float)
+ {
+ $segment = substr($float, strpos($float, '.') + 1);
+ return $segment ? strlen($segment) : 0;
+ }
}

diff:
@@ -124,6 +124,29 @@ class StepTest extends TestCase
$this->assertSame($expected, $validator->isValid($value));
}
+
+ public function decimalStepSubstractionBugValues()
+ {
+ return [
+ 'base-value-20' => [20, 20.06, true],
+ 'base-value-40' => [40, 40.09, true],
+ 'base-value-50' => [50, 50.09, true],
+ ];
+ }
+
+ /**
+ * @dataProvider decimalStepSubstractionBugValues
+ */
+ public function testDecimalStepSubstractionBug($baseValue, $value, $expected)
+ {
+ $validator = new Validator\Step([
+ 'baseValue' => $baseValue,
+ 'step' => 0.01
+ ]);
+
+ $this->assertSame($expected, $validator->isValid($value));
+ }
+
public function decimalHundredthStepValues()
{
return [
@@ -151,7 +174,7 @@ class StepTest extends TestCase
/**
* @dataProvider decimalHundredthStepValues
*/
- public function testDecimalStep2($value, $expected)
+ public function testdecimalHundredthStep($value, $expected)
{
$validator = new Validator\Step([
'baseValue' => 0,


message: issue#27 - Tidy some misplaced unit tests

diff:
@@ -65,11 +65,6 @@ class AbstractTest extends TestCase
$this->assertNull($this->validator->getTranslator());
}
- public function testGlobalDefaultTranslatorNullByDefault()
- {
- $this->assertNull(AbstractValidator::getDefaultTranslator());
- }
-
public function testErrorMessagesAreTranslatedWhenTranslatorPresent()
{
if (! extension_loaded('intl')) {

diff:
namespace ZendTest\Validator;
use PHPUnit\Framework\TestCase;
-use Zend\Validator\AbstractValidator;
use Zend\I18n\Validator\Alpha;
+use Zend\ServiceManager\Exception\ServiceNotFoundException;
+use Zend\ServiceManager\ServiceManager;
+use Zend\Validator\AbstractValidator;
use Zend\Validator\Between;
-use Zend\Validator\StaticValidator;
use Zend\Validator\ValidatorPluginManager;
-use Zend\ServiceManager\ServiceManager;
+use Zend\Validator\StaticValidator;
/**
* @group Zend_Validator
@@ -167,4 +168,19 @@ class StaticValidatorTest extends TestCase
$this->expectExceptionMessage('options');
StaticValidator::execute($value, $validator, $options);
}
+
+ /**
+ * Ensures that if we specify a validator class basename that doesn't
+ * exist in the namespace, is() throws an exception.
+ *
+ * Refactored to conform with ZF-2724.
+ *
+ * @group ZF-2724
+ * @return void
+ */
+ public function testStaticFactoryClassNotFound()
+ {
+ $this->expectException(ServiceNotFoundException::class);
+ StaticValidator::execute('1234', 'UnknownValidator');
+ }
}

diff:
@@ -15,7 +15,6 @@ use Zend\Validator\Between;
use Zend\Validator\NotEmpty;
use Zend\Validator\StaticValidator;
use Zend\Validator\ValidatorChain;
-use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\Validator\ValidatorInterface;
use Zend\Validator\GreaterThan;
@@ -100,39 +99,6 @@ class ValidatorChainTest extends TestCase
$this->assertEquals(['error' => 'validation failed'], $this->validator->getMessages());
}
- /**
- * Ensures that if we specify a validator class basename that doesn't
- * exist in the namespace, is() throws an exception.
- *
- * Refactored to conform with ZF-2724.
- *
- * @group ZF-2724
- * @return void
- */
- public function testStaticFactoryClassNotFound()
- {
- $this->expectException(ServiceNotFoundException::class);
- StaticValidator::execute('1234', 'UnknownValidator');
- }
-
- public function testSetGetMessageLengthLimitation()
- {
- AbstractValidator::setMessageLength(5);
- $this->assertEquals(5, AbstractValidator::getMessageLength());
-
- $valid = new Between(1, 10);
- $this->assertFalse($valid->isValid(24));
- $message = current($valid->getMessages());
- $this->assertLessThanOrEqual(5, strlen($message));
- }
-
- public function testSetGetDefaultTranslator()
- {
- $translator = new TestAsset\Translator();
- AbstractValidator::setDefaultTranslator($translator);
- $this->assertSame($translator, AbstractValidator::getDefaultTranslator());
- }
-
public function testAllowsPrependingValidators()
{
$this->validator->attach($this->getValidatorTrue())


message:
Fix some minors typos
replace cannont by cannot
use only lowcase cannot
use only uppercase constants

diff:
@@ -12,16 +12,16 @@ use Laminas\Validator\Hostname;
require __DIR__ . '/../vendor/autoload.php';
define('IANA_URL', 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt');
-define('Laminas_HOSTNAME_VALIDATOR_FILE', __DIR__.'/../src/Hostname.php');
+define('LAMINAS_HOSTNAME_VALIDATOR_FILE', __DIR__.'/../src/Hostname.php');
-if (! file_exists(Laminas_HOSTNAME_VALIDATOR_FILE) || ! is_readable(Laminas_HOSTNAME_VALIDATOR_FILE)) {
- printf("Error: cannont read file '%s'%s", Laminas_HOSTNAME_VALIDATOR_FILE, PHP_EOL);
+if (! file_exists(LAMINAS_HOSTNAME_VALIDATOR_FILE) || ! is_readable(LAMINAS_HOSTNAME_VALIDATOR_FILE)) {
+ printf("Error: cannot read file '%s'%s", LAMINAS_HOSTNAME_VALIDATOR_FILE, PHP_EOL);
exit(1);
}
-if (! is_writable(Laminas_HOSTNAME_VALIDATOR_FILE)) {
- printf("Error: Cannot update file '%s'%s", Laminas_HOSTNAME_VALIDATOR_FILE, PHP_EOL);
+if (! is_writable(LAMINAS_HOSTNAME_VALIDATOR_FILE)) {
+ printf("Error: cannot update file '%s'%s", LAMINAS_HOSTNAME_VALIDATOR_FILE, PHP_EOL);
exit(1);
}
@@ -31,7 +31,7 @@ $insertFinish = false; // becomes 'true' when we find end of $validTlds decl
$checkOnly = isset($argv[1]) ? $argv[1] === '--check-only' : false;
$response = getOfficialTLDs();
$ianaVersion = getVersionFromString('Version', strtok($response->getBody(), "\n"));
-$validatorVersion = getVersionFromString('IanaVersion', file_get_contents(Laminas_HOSTNAME_VALIDATOR_FILE));
+$validatorVersion = getVersionFromString('IanaVersion', file_get_contents(LAMINAS_HOSTNAME_VALIDATOR_FILE));
if ($checkOnly && $ianaVersion > $validatorVersion) {
printf(
@@ -46,7 +46,7 @@ if ($checkOnly) {
exit(0);
}
-foreach (file(Laminas_HOSTNAME_VALIDATOR_FILE) as $line) {
+foreach (file(LAMINAS_HOSTNAME_VALIDATOR_FILE) as $line) {
// Replace old version number with new one
if (preg_match('/\*\s+IanaVersion\s+\d+/', $line, $matches)) {
$newFileContent[] = sprintf(" * IanaVersion %s\n", $ianaVersion);
@@ -89,8 +89,8 @@ if (!$insertFinish) {
exit(1);
}
-if (false === @file_put_contents(Laminas_HOSTNAME_VALIDATOR_FILE, $newFileContent)) {
- printf('Error: cannot write info file "%s"%s', Laminas_HOSTNAME_VALIDATOR_FILE, PHP_EOL);
+if (false === @file_put_contents(LAMINAS_HOSTNAME_VALIDATOR_FILE, $newFileContent)) {
+ printf('Error: cannot write info file "%s"%s', LAMINAS_HOSTNAME_VALIDATOR_FILE, PHP_EOL);
exit(1);
}


message:
Refactoring: reduce email validation complexity
rise readibility by using early returns
remove condition depth

diff:
@@ -335,32 +335,30 @@ class EmailAddress extends AbstractValidator
protected function validateLocalPart()
{
// First try to match the local part on the common dot-atom format
- $result = false;
// Dot-atom characters are: 1*atext *("." 1*atext)
// atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*",
// "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~"
$atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e';
if (preg_match('/^[' . $atext . ']+(\x2e+[' . $atext . ']+)*$/', $this->localPart)) {
- $result = true;
- } elseif ($this->validateInternationalizedLocalPart($this->localPart)) {
- $result = true;
- } else {
+ return true;
+ }
+ if ($this->validateInternationalizedLocalPart($this->localPart)) {
+ return true;
+ }
+
// Try quoted string format (RFC 5321 Chapter 4.1.2)
// Quoted-string characters are: DQUOTE *(qtext/quoted-pair) DQUOTE
$qtext = '\x20-\x21\x23-\x5b\x5d-\x7e'; // %d32-33 / %d35-91 / %d93-126
$quotedPair = '\x20-\x7e'; // %d92 %d32-126
if (preg_match('/^"(['. $qtext .']|\x5c[' . $quotedPair . '])*"$/', $this->localPart)) {
- $result = true;
- } else {
+ return true;
+ }
$this->error(self::DOT_ATOM);
$this->error(self::QUOTED_STRING);
$this->error(self::INVALID_LOCAL_PART);
- }
- }
-
- return $result;
+ return false;
}
/**


diff:
@@ -89,22 +89,17 @@ class Uri extends AbstractValidator
// Instantiate string Uri handler that references a class
$this->uriHandler = new $this->uriHandler;
}
-
- if (! $this->uriHandler instanceof UriHandler) {
- throw new InvalidArgumentException('URI handler is expected to be a Laminas\Uri\Uri object');
- }
-
return $this->uriHandler;
}
/**
- * @param UriHandler $uriHandler
+ * @param UriHandler|string $uriHandler
* @throws InvalidArgumentException
* @return Uri
*/
public function setUriHandler($uriHandler)
{
- if (! is_subclass_of($uriHandler, 'Laminas\Uri\Uri')) {
+ if (! is_a($uriHandler, UriHandler::class, true)) {
throw new InvalidArgumentException(sprintf(
'Expecting a subclass name or instance of %s as $uriHandler',
UriHandler::class

diff:
+<?php
+
+namespace LaminasTest\Validator\TestAsset;
+
+use Iterator;
+
+class CustomTraversable implements Iterator
+{
+ /** @var array */
+ private $data;
+
+ public function __construct(array $data)
+ {
+ $this->data = $data;
+ }
+
+ public function current()
+ {
+ return current($this->data);
+ }
+
+ public function next()
+ {
+ return next($this->data);
+ }
+
+ public function key()
+ {
+ return key($this->data);
+ }
+
+ public function valid()
+ {
+ return $this->key() !== null;
+ }
+
+ public function rewind()
+ {
+ return reset($this->data);
+ }
+}

diff:
@@ -10,8 +10,9 @@ namespace LaminasTest\Validator;
use Laminas\Uri\Exception\InvalidArgumentException;
use Laminas\Uri\Http;
-use Laminas\Uri\Uri;
+use Laminas\Uri\Uri as UriHandler;
use Laminas\Validator;
+use LaminasTest\Validator\TestAsset;
use PHPUnit\Framework\TestCase;
/**
@@ -38,31 +39,55 @@ class UriTest extends TestCase
{
$validator = $this->validator;
$uriHandler = $validator->getUriHandler();
- $this->assertInstanceOf(Uri::class, $uriHandler);
- $this->assertTrue($validator->getAllowRelative());
- $this->assertTrue($validator->getAllowAbsolute());
+ self::assertInstanceOf(UriHandler::class, $uriHandler);
+ self::assertTrue($validator->getAllowRelative());
+ self::assertTrue($validator->getAllowAbsolute());
+ }
+
+ public function testWithProperUriHandler()
+ {
+ $uriHandler = new UriHandler();
+ $this->validator->setUriHandler($uriHandler);
+ self::assertInstanceOf(UriHandler::class, $this->validator->getUriHandler());
+
+ $this->validator->setUriHandler(UriHandler::class);
+ self::assertInstanceOf(UriHandler::class, $this->validator->getUriHandler());
}
public function testConstructorWithArraySetsOptions()
{
- $uriMock = $this->createMock(Uri::class);
+ $uriMock = $this->createMock(UriHandler::class);
$validator = new Validator\Uri([
'uriHandler' => $uriMock,
'allowRelative' => false,
'allowAbsolute' => false,
]);
- $this->assertEquals($uriMock, $validator->getUriHandler());
- $this->assertFalse($validator->getAllowRelative());
- $this->assertFalse($validator->getAllowAbsolute());
+ self::assertSame($uriMock, $validator->getUriHandler());
+ self::assertFalse($validator->getAllowRelative());
+ self::assertFalse($validator->getAllowAbsolute());
}
public function testConstructorWithArgsSetsOptions()
{
- $uriMock = $this->createMock(Uri::class);
+ $uriMock = $this->createMock(UriHandler::class);
$validator = new Validator\Uri($uriMock, false, false);
- $this->assertEquals($uriMock, $validator->getUriHandler());
- $this->assertFalse($validator->getAllowRelative());
- $this->assertFalse($validator->getAllowAbsolute());
+ self::assertSame($uriMock, $validator->getUriHandler());
+ self::assertFalse($validator->getAllowRelative());
+ self::assertFalse($validator->getAllowAbsolute());
+ }
+
+ public function testConstructWithTraversableSetsOptions()
+ {
+ $uriMock = $this->createMock(UriHandler::class);
+ $options = new TestAsset\CustomTraversable([
+ 'uriHandler' => $uriMock,
+ 'allowRelative' => false,
+ 'allowAbsolute' => false,
+ ]);
+ $validator = new Validator\Uri($options);
+ self::assertSame($uriMock, $validator->getUriHandler());
+ self::assertFalse($validator->getAllowRelative());
+ self::assertFalse($validator->getAllowAbsolute());
}
public function allowOptionsDataProvider()
@@ -85,14 +110,14 @@ class UriTest extends TestCase
* @dataProvider allowOptionsDataProvider
*/
public function testUriHandlerBehaviorWithAllowSettings(
- $allowAbsolute,
- $allowRelative,
- $isAbsolute,
- $isRelative,
- $isValid,
- $expects
+ bool $allowAbsolute,
+ bool $allowRelative,
+ bool $isAbsolute,
+ bool $isRelative,
+ bool $isValid,
+ bool $expects
) {
- $uriMock = $this->getMockBuilder(Uri::class)
+ $uriMock = $this->getMockBuilder(UriHandler::class)
->setConstructorArgs(['parse', 'isValid', 'isAbsolute', 'isValidRelative'])
->getMock();
$uriMock
@@ -111,18 +136,18 @@ class UriTest extends TestCase
->setAllowAbsolute($allowAbsolute)
->setAllowRelative($allowRelative);
- $this->assertEquals($expects, $this->validator->isValid('uri'));
+ self::assertSame($expects, $this->validator->isValid('uri'));
}
public function testUriHandlerThrowsExceptionInParseMethodNotValid()
{
- $uriMock = $this->createMock(Uri::class);
+ $uriMock = $this->createMock(UriHandler::class);
$uriMock->expects($this->once())
->method('parse')
->will($this->throwException(new InvalidArgumentException()));
$this->validator->setUriHandler($uriMock);
- $this->assertFalse($this->validator->isValid('uri'));
+ self::assertFalse($this->validator->isValid('uri'));
}
/**
@@ -132,20 +157,20 @@ class UriTest extends TestCase
*/
public function testGetMessages()
{
- $this->assertEquals([], $this->validator->getMessages());
+ self::assertSame([], $this->validator->getMessages());
}
public function testEqualsMessageTemplates()
{
$validator = $this->validator;
- $this->assertObjectHasAttribute('messageTemplates', $validator);
- $this->assertAttributeEquals($validator->getOption('messageTemplates'), 'messageTemplates', $validator);
+ self::assertObjectHasAttribute('messageTemplates', $validator);
+ self::assertAttributeEquals($validator->getOption('messageTemplates'), 'messageTemplates', $validator);
}
public function testUriHandlerCanBeSpecifiedAsString()
{
$this->validator->setUriHandler(Http::class);
- $this->assertInstanceOf(Http::class, $this->validator->getUriHandler());
+ self::assertInstanceOf(Http::class, $this->validator->getUriHandler());
}
public function testUriHandlerStringInvalidClassThrowsException()
@@ -160,6 +185,20 @@ class UriTest extends TestCase
$this->validator->setUriHandler(new \stdClass());
}
+ public function testConstructUriHandlerStringInvalidClassThrowsException()
+ {
+ $this->expectException(Validator\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Expecting a subclass name or instance of Laminas\Uri\Uri as $uriHandler');
+ new Validator\Uri(\stdClass::class, false, false);
+ }
+
+ public function testConstructUriHandlerInvalidTypeThrowsException()
+ {
+ $this->expectException(Validator\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Expecting a subclass name or instance of Laminas\Uri\Uri as $uriHandler');
+ new Validator\Uri(new \stdClass(), false, false);
+ }
+
public function invalidValueTypes()
{
return [
@@ -180,6 +219,6 @@ class UriTest extends TestCase
*/
public function testIsValidReturnsFalseWhenProvidedUnsupportedType($value)
{
- $this->assertFalse($this->validator->isValid($value));
+ self::assertFalse($this->validator->isValid($value));
}
}

original_message:
qa: add uri validator test

Add constructor test for traversable object
Replace assertion calls by static calls

Signed-off-by: codisart <louis.celier@gmail.com>