summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/addrinfo.h180
-rw-r--r--lib/compat.c272
-rw-r--r--lib/dummy.in2
-rw-r--r--lib/getaddrinfo.c504
-rw-r--r--lib/getpass.c72
-rw-r--r--lib/inet_ntop.c186
-rw-r--r--lib/inet_pton.c212
-rw-r--r--lib/md-defines.h37
-rw-r--r--lib/md5-asm-x86_64.S701
-rw-r--r--lib/md5.c321
-rw-r--r--lib/mdfour.c247
-rw-r--r--lib/mdigest.h22
-rw-r--r--lib/permstring.c65
-rw-r--r--lib/permstring.h3
-rw-r--r--lib/pool_alloc.3268
-rw-r--r--lib/pool_alloc.c375
-rw-r--r--lib/pool_alloc.h21
-rw-r--r--lib/snprintf.c1512
-rw-r--r--lib/sysacls.c2802
-rw-r--r--lib/sysacls.h307
-rw-r--r--lib/sysxattrs.c301
-rw-r--r--lib/sysxattrs.h26
-rw-r--r--lib/wildmatch.c368
-rw-r--r--lib/wildmatch.h6
24 files changed, 8810 insertions, 0 deletions
diff --git a/lib/addrinfo.h b/lib/addrinfo.h
new file mode 100644
index 0000000..ee9f672
--- /dev/null
+++ b/lib/addrinfo.h
@@ -0,0 +1,180 @@
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this paragraph
+and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+*/
+
+/*-------------------------------------------------------------------------
+ *
+ * getaddrinfo.h
+ * Support getaddrinfo() on platforms that don't have it.
+ *
+ * Note: we use our own routines on platforms that don't HAVE_STRUCT_ADDRINFO,
+ * whether or not the library routine getaddrinfo() can be found. This
+ * policy is needed because on some platforms a manually installed libbind.a
+ * may provide getaddrinfo(), yet the system headers may not provide the
+ * struct definitions needed to call it. To avoid conflict with the libbind
+ * definition in such cases, we rename our routines to pg_xxx() via macros.
+ *
+ * This code will also work on platforms where struct addrinfo is defined
+ * in the system headers but no getaddrinfo() can be located.
+ *
+ * Copyright (c) 2003-2007, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ADDRINFO_H
+#define ADDRINFO_H
+
+
+/* Various macros that ought to be in <netdb.h>, but might not be */
+
+#ifndef EAI_FAIL
+#define EAI_BADFLAGS (-1)
+#define EAI_NONAME (-2)
+#define EAI_AGAIN (-3)
+#define EAI_FAIL (-4)
+#define EAI_FAMILY (-6)
+#define EAI_SOCKTYPE (-7)
+#define EAI_SERVICE (-8)
+#define EAI_MEMORY (-10)
+#define EAI_SYSTEM (-11)
+#endif /* !EAI_FAIL */
+
+#ifndef AI_PASSIVE
+#define AI_PASSIVE 0x0001
+#endif
+
+#ifndef AI_NUMERICHOST
+/*
+ * some platforms don't support AI_NUMERICHOST; define as zero if using
+ * the system version of getaddrinfo...
+ */
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_NUMERICHOST 0
+#else
+#define AI_NUMERICHOST 0x0004
+#endif
+#endif
+
+#ifndef AI_CANONNAME
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_CANONNAME 0
+#else
+#define AI_CANONNAME 0x0008
+#endif
+#endif
+
+#ifndef AI_NUMERICSERV
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_NUMERICSERV 0
+#else
+#define AI_NUMERICSERV 0x0010
+#endif
+#endif
+
+#ifndef NI_NUMERICHOST
+#define NI_NUMERICHOST 1
+#endif
+
+#ifndef NI_NUMERICSERV
+#define NI_NUMERICSERV 2
+#endif
+
+#ifndef NI_NOFQDN
+#define NI_NOFQDN 4
+#endif
+
+#ifndef NI_NAMEREQD
+#define NI_NAMEREQD 8
+#endif
+
+#ifndef NI_DGRAM
+#define NI_DGRAM 16
+#endif
+
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+
+#ifndef NI_MAXSERV
+#define NI_MAXSERV 32
+#endif
+
+#ifndef HAVE_STRUCT_ADDRINFO
+struct addrinfo
+{
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ struct sockaddr *ai_addr;
+ char *ai_canonname;
+ struct addrinfo *ai_next;
+};
+#endif /* !HAVE_STRUCT_ADDRINFO */
+
+#ifndef HAVE_STRUCT_SOCKADDR_STORAGE
+struct sockaddr_storage {
+ unsigned short ss_family;
+ unsigned long ss_align;
+ char ss_padding[128 - sizeof (unsigned long)];
+};
+#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */
+
+#ifndef HAVE_GETADDRINFO
+
+/* Rename private copies per comments above */
+#ifdef getaddrinfo
+#undef getaddrinfo
+#endif
+#define getaddrinfo pg_getaddrinfo
+
+#ifdef freeaddrinfo
+#undef freeaddrinfo
+#endif
+#define freeaddrinfo pg_freeaddrinfo
+
+#ifdef gai_strerror
+#undef gai_strerror
+#endif
+#define gai_strerror pg_gai_strerror
+
+#ifdef getnameinfo
+#undef getnameinfo
+#endif
+#define getnameinfo pg_getnameinfo
+
+extern int getaddrinfo(const char *node, const char *service,
+ const struct addrinfo * hints, struct addrinfo ** res);
+extern void freeaddrinfo(struct addrinfo * res);
+extern const char *gai_strerror(int errcode);
+extern int getnameinfo(const struct sockaddr * sa, socklen_t salen,
+ char *node, size_t nodelen,
+ char *service, size_t servicelen, int flags);
+#endif /* !HAVE_GETADDRINFO */
+
+#endif /* ADDRINFO_H */
diff --git a/lib/compat.c b/lib/compat.c
new file mode 100644
index 0000000..513d79b
--- /dev/null
+++ b/lib/compat.c
@@ -0,0 +1,272 @@
+/*
+ * Reimplementations of standard functions for platforms that don't have them.
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2004-2020 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+
+static char number_separator;
+
+char get_number_separator(void)
+{
+ if (!number_separator) {
+ char buf[32];
+ snprintf(buf, sizeof buf, "%f", 3.14);
+ if (strchr(buf, '.') != NULL)
+ number_separator = ',';
+ else
+ number_separator = '.';
+ }
+
+ return number_separator;
+}
+
+char get_decimal_point(void)
+{
+ return get_number_separator() == ',' ? '.' : ',';
+}
+
+#ifndef HAVE_GETCWD
+ char *getcwd(char *buf, int size)
+{
+ return getwd(buf);
+}
+#endif
+
+
+#ifndef HAVE_WAITPID
+ pid_t waitpid(pid_t pid, int *statptr, int options)
+{
+#ifdef HAVE_WAIT4
+ return wait4(pid, statptr, options, NULL);
+#else
+ /* If wait4 is also not available, try wait3 for SVR3 variants */
+ /* Less ideal because can't actually request a specific pid */
+ /* At least the WNOHANG option is supported */
+ /* Code borrowed from apache fragment written by dwd@bell-labs.com */
+ int tmp_pid, dummystat;;
+ if (kill(pid, 0) == -1) {
+ errno = ECHILD;
+ return -1;
+ }
+ if (statptr == NULL)
+ statptr = &dummystat;
+ while (((tmp_pid = wait3(statptr, options, 0)) != pid) &&
+ (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1))
+ ;
+ return tmp_pid;
+#endif
+}
+#endif
+
+
+#ifndef HAVE_MEMMOVE
+ void *memmove(void *dest, const void *src, size_t n)
+{
+ bcopy((char *) src, (char *) dest, n);
+ return dest;
+}
+#endif
+
+#ifndef HAVE_STRPBRK
+/**
+ * Find the first occurrence in @p s of any character in @p accept.
+ *
+ * Derived from glibc
+ **/
+ char *strpbrk(const char *s, const char *accept)
+{
+ while (*s != '\0') {
+ const char *a = accept;
+ while (*a != '\0') {
+ if (*a++ == *s) return (char *)s;
+ }
+ ++s;
+ }
+
+ return NULL;
+}
+#endif
+
+
+#ifndef HAVE_STRLCPY
+/**
+ * Like strncpy but does not 0 fill the buffer and always null
+ * terminates.
+ *
+ * @param bufsize is the size of the destination buffer.
+ *
+ * @return index of the terminating byte.
+ **/
+ size_t strlcpy(char *d, const char *s, size_t bufsize)
+{
+ size_t len = strlen(s);
+ size_t ret = len;
+ if (bufsize > 0) {
+ if (len >= bufsize)
+ len = bufsize-1;
+ memcpy(d, s, len);
+ d[len] = 0;
+ }
+ return ret;
+}
+#endif
+
+#ifndef HAVE_STRLCAT
+/**
+ * Like strncat() but does not 0 fill the buffer and always null
+ * terminates.
+ *
+ * @param bufsize length of the buffer, which should be one more than
+ * the maximum resulting string length.
+ **/
+ size_t strlcat(char *d, const char *s, size_t bufsize)
+{
+ size_t len1 = strlen(d);
+ size_t len2 = strlen(s);
+ size_t ret = len1 + len2;
+
+ if (len1 < bufsize - 1) {
+ if (len2 >= bufsize - len1)
+ len2 = bufsize - len1 - 1;
+ memcpy(d+len1, s, len2);
+ d[len1+len2] = 0;
+ }
+ return ret;
+}
+#endif
+
+/* some systems don't take the 2nd argument */
+int sys_gettimeofday(struct timeval *tv)
+{
+#ifdef HAVE_GETTIMEOFDAY_TZ
+ return gettimeofday(tv, NULL);
+#else
+ return gettimeofday(tv);
+#endif
+}
+
+/* Return the int64 number as a string. If the human_flag arg is non-zero,
+ * we may output the number in K, M, G, or T units. If we don't add a unit
+ * suffix, we will append the fract string, if it is non-NULL. We can
+ * return up to 4 buffers at a time. */
+char *do_big_num(int64 num, int human_flag, const char *fract)
+{
+ static char bufs[4][128]; /* more than enough room */
+ static unsigned int n;
+ char *s;
+ int len, negated;
+
+ if (human_flag && !number_separator)
+ (void)get_number_separator();
+
+ n = (n + 1) % (sizeof bufs / sizeof bufs[0]);
+
+ if (human_flag > 1) {
+ int mult = human_flag == 2 ? 1000 : 1024;
+ if (num >= mult || num <= -mult) {
+ double dnum = (double)num / mult;
+ char units;
+ if (num < 0)
+ dnum = -dnum;
+ if (dnum < mult)
+ units = 'K';
+ else if ((dnum /= mult) < mult)
+ units = 'M';
+ else if ((dnum /= mult) < mult)
+ units = 'G';
+ else if ((dnum /= mult) < mult)
+ units = 'T';
+ else {
+ dnum /= mult;
+ units = 'P';
+ }
+ if (num < 0)
+ dnum = -dnum;
+ snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units);
+ return bufs[n];
+ }
+ }
+
+ s = bufs[n] + sizeof bufs[0] - 1;
+ if (fract) {
+ len = strlen(fract);
+ s -= len;
+ strlcpy(s, fract, len + 1);
+ } else
+ *s = '\0';
+
+ len = 0;
+
+ if (!num)
+ *--s = '0';
+ if (num < 0) {
+ /* A maximum-size negated number can't fit as a positive,
+ * so do one digit in negated form to start us off. */
+ *--s = (char)(-(num % 10)) + '0';
+ num = -(num / 10);
+ len++;
+ negated = 1;
+ } else
+ negated = 0;
+
+ while (num) {
+ if (human_flag) {
+ if (len == 3) {
+ *--s = number_separator;
+ len = 1;
+ } else
+ len++;
+ }
+ *--s = (char)(num % 10) + '0';
+ num /= 10;
+ }
+
+ if (negated)
+ *--s = '-';
+
+ return s;
+}
+
+/* Return the double number as a string. If the human_flag option is > 1,
+ * we may output the number in K, M, G, or T units. The buffer we use for
+ * our result is either a single static buffer defined here, or a buffer
+ * we get from do_big_num(). */
+char *do_big_dnum(double dnum, int human_flag, int decimal_digits)
+{
+ static char tmp_buf[128];
+#if SIZEOF_INT64 >= 8
+ char *fract;
+
+ snprintf(tmp_buf, sizeof tmp_buf, "%.*f", decimal_digits, dnum);
+
+ if (!human_flag || (dnum < 1000.0 && dnum > -1000.0))
+ return tmp_buf;
+
+ for (fract = tmp_buf+1; isDigit(fract); fract++) {}
+
+ return do_big_num((int64)dnum, human_flag, fract);
+#else
+ /* A big number might lose digits converting to a too-short int64,
+ * so let's just return the raw double conversion. */
+ snprintf(tmp_buf, sizeof tmp_buf, "%.*f", decimal_digits, dnum);
+ return tmp_buf;
+#endif
+}
diff --git a/lib/dummy.in b/lib/dummy.in
new file mode 100644
index 0000000..3b26a20
--- /dev/null
+++ b/lib/dummy.in
@@ -0,0 +1,2 @@
+This is a dummy file to ensure that the lib directory gets created
+by configure when a VPATH is used.
diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c
new file mode 100644
index 0000000..96d7a2b
--- /dev/null
+++ b/lib/getaddrinfo.c
@@ -0,0 +1,504 @@
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this paragraph
+and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+*/
+
+/*-------------------------------------------------------------------------
+ *
+ * getaddrinfo.c
+ * Support getaddrinfo() on platforms that don't have it.
+ *
+ * We also supply getnameinfo() here, assuming that the platform will have
+ * it if and only if it has getaddrinfo(). If this proves false on some
+ * platform, we'll need to split this file and provide a separate configure
+ * test for getnameinfo().
+ *
+ * Copyright (c) 2003-2007, PostgreSQL Global Development Group
+ *
+ * Copyright (C) 2007 Jeremy Allison.
+ * Modified to return multiple IPv4 addresses for Samba.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "rsync.h"
+
+#ifndef SMB_MALLOC
+#define SMB_MALLOC(s) malloc(s)
+#endif
+
+#ifndef SMB_STRDUP
+#define SMB_STRDUP(s) strdup(s)
+#endif
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
+static int check_hostent_err(struct hostent *hp)
+{
+#ifndef INET6
+ extern int h_errno;
+#endif
+ if (!hp) {
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ return EAI_NONAME;
+ case TRY_AGAIN:
+ return EAI_AGAIN;
+ case NO_RECOVERY:
+ default:
+ return EAI_FAIL;
+ }
+ }
+ if (!hp->h_name || hp->h_addrtype != AF_INET) {
+ return EAI_FAIL;
+ }
+ return 0;
+}
+
+static char *canon_name_from_hostent(struct hostent *hp,
+ int *perr)
+{
+ char *ret = NULL;
+
+ *perr = check_hostent_err(hp);
+ if (*perr) {
+ return NULL;
+ }
+ ret = SMB_STRDUP(hp->h_name);
+ if (!ret) {
+ *perr = EAI_MEMORY;
+ }
+ return ret;
+}
+
+static char *get_my_canon_name(int *perr)
+{
+ char name[HOST_NAME_MAX+1];
+
+ if (gethostname(name, HOST_NAME_MAX) == -1) {
+ *perr = EAI_FAIL;
+ return NULL;
+ }
+ /* Ensure null termination. */
+ name[HOST_NAME_MAX] = '\0';
+ return canon_name_from_hostent(gethostbyname(name), perr);
+}
+
+static char *get_canon_name_from_addr(struct in_addr ip,
+ int *perr)
+{
+ return canon_name_from_hostent(
+ gethostbyaddr((void *)&ip, sizeof ip, AF_INET),
+ perr);
+}
+
+static struct addrinfo *alloc_entry(const struct addrinfo *hints,
+ struct in_addr ip,
+ unsigned short port)
+{
+ struct sockaddr_in *psin = NULL;
+ struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
+
+ if (!ai) {
+ return NULL;
+ }
+ memset(ai, '\0', sizeof(*ai));
+
+ psin = SMB_MALLOC(sizeof(*psin));
+ if (!psin) {
+ free(ai);
+ return NULL;
+ }
+
+ memset(psin, '\0', sizeof(*psin));
+
+ psin->sin_family = AF_INET;
+ psin->sin_port = htons(port);
+ psin->sin_addr = ip;
+
+ ai->ai_flags = 0;
+ ai->ai_family = AF_INET;
+ ai->ai_socktype = hints->ai_socktype;
+ ai->ai_protocol = hints->ai_protocol;
+ ai->ai_addrlen = sizeof(*psin);
+ ai->ai_addr = (struct sockaddr *) psin;
+ ai->ai_canonname = NULL;
+ ai->ai_next = NULL;
+
+ return ai;
+}
+
+/*
+ * get address info for a single ipv4 address.
+ *
+ * Bugs: - servname can only be a number, not text.
+ */
+
+static int getaddr_info_single_addr(const char *service,
+ uint32 addr,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+
+ struct addrinfo *ai = NULL;
+ struct in_addr ip;
+ unsigned short port = 0;
+
+ if (service) {
+ port = (unsigned short)atoi(service);
+ }
+ ip.s_addr = htonl(addr);
+
+ ai = alloc_entry(hints, ip, port);
+ if (!ai) {
+ return EAI_MEMORY;
+ }
+
+ /* If we're asked for the canonical name,
+ * make sure it returns correctly. */
+ if (!(hints->ai_flags & AI_NUMERICSERV) &&
+ hints->ai_flags & AI_CANONNAME) {
+ int err;
+ if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
+ ai->ai_canonname = get_my_canon_name(&err);
+ } else {
+ ai->ai_canonname =
+ get_canon_name_from_addr(ip,&err);
+ }
+ if (ai->ai_canonname == NULL) {
+ freeaddrinfo(ai);
+ return err;
+ }
+ }
+
+ *res = ai;
+ return 0;
+}
+
+/*
+ * get address info for multiple ipv4 addresses.
+ *
+ * Bugs: - servname can only be a number, not text.
+ */
+
+static int getaddr_info_name(const char *node,
+ const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+ struct addrinfo *listp = NULL, *prevp = NULL;
+ char **pptr = NULL;
+ int err;
+ struct hostent *hp = NULL;
+ unsigned short port = 0;
+
+ if (service) {
+ port = (unsigned short)atoi(service);
+ }
+
+ hp = gethostbyname(node);
+ err = check_hostent_err(hp);
+ if (err) {
+ return err;
+ }
+
+ for(pptr = hp->h_addr_list; *pptr; pptr++) {
+ struct in_addr ip = *(struct in_addr *)*pptr;
+ struct addrinfo *ai = alloc_entry(hints, ip, port);
+
+ if (!ai) {
+ freeaddrinfo(listp);
+ return EAI_MEMORY;
+ }
+
+ if (!listp) {
+ listp = ai;
+ prevp = ai;
+ ai->ai_canonname = SMB_STRDUP(hp->h_name);
+ if (!ai->ai_canonname) {
+ freeaddrinfo(listp);
+ return EAI_MEMORY;
+ }
+ } else {
+ prevp->ai_next = ai;
+ prevp = ai;
+ }
+ }
+ *res = listp;
+ return 0;
+}
+
+/*
+ * get address info for ipv4 sockets.
+ *
+ * Bugs: - servname can only be a number, not text.
+ */
+
+int getaddrinfo(const char *node,
+ const char *service,
+ const struct addrinfo * hintp,
+ struct addrinfo ** res)
+{
+ struct addrinfo hints;
+
+ /* Setup the hints struct. */
+ if (hintp == NULL) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ } else {
+ memcpy(&hints, hintp, sizeof(hints));
+ }
+
+ if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
+ return EAI_FAMILY;
+ }
+
+ if (hints.ai_socktype == 0) {
+ hints.ai_socktype = SOCK_STREAM;
+ }
+
+ if (!node && !service) {
+ return EAI_NONAME;
+ }
+
+ if (node) {
+ if (node[0] == '\0') {
+ return getaddr_info_single_addr(service,
+ INADDR_ANY,
+ &hints,
+ res);
+ } else if (hints.ai_flags & AI_NUMERICHOST) {
+ struct in_addr ip;
+ if (inet_pton(AF_INET, node, &ip) <= 0)
+ return EAI_FAIL;
+ return getaddr_info_single_addr(service,
+ ntohl(ip.s_addr),
+ &hints,
+ res);
+ } else {
+ return getaddr_info_name(node,
+ service,
+ &hints,
+ res);
+ }
+ } else if (hints.ai_flags & AI_PASSIVE) {
+ return getaddr_info_single_addr(service,
+ INADDR_ANY,
+ &hints,
+ res);
+ }
+ return getaddr_info_single_addr(service,
+ INADDR_LOOPBACK,
+ &hints,
+ res);
+}
+
+
+void freeaddrinfo(struct addrinfo *res)
+{
+ struct addrinfo *next = NULL;
+
+ for (;res; res = next) {
+ next = res->ai_next;
+ if (res->ai_canonname) {
+ free(res->ai_canonname);
+ }
+ if (res->ai_addr) {
+ free(res->ai_addr);
+ }
+ free(res);
+ }
+}
+
+
+const char *gai_strerror(int errcode)
+{
+#ifdef HAVE_HSTRERROR
+ int hcode;
+
+ switch (errcode)
+ {
+ case EAI_NONAME:
+ hcode = HOST_NOT_FOUND;
+ break;
+ case EAI_AGAIN:
+ hcode = TRY_AGAIN;
+ break;
+ case EAI_FAIL:
+ default:
+ hcode = NO_RECOVERY;
+ break;
+ }
+
+ return hstrerror(hcode);
+#else /* !HAVE_HSTRERROR */
+
+ switch (errcode)
+ {
+ case EAI_NONAME:
+ return "Unknown host";
+ case EAI_AGAIN:
+ return "Host name lookup failure";
+#ifdef EAI_BADFLAGS
+ case EAI_BADFLAGS:
+ return "Invalid argument";
+#endif
+#ifdef EAI_FAMILY
+ case EAI_FAMILY:
+ return "Address family not supported";
+#endif
+#ifdef EAI_MEMORY
+ case EAI_MEMORY:
+ return "Not enough memory";
+#endif
+#ifdef EAI_NODATA
+ case EAI_NODATA:
+ return "No host data of that type was found";
+#endif
+#ifdef EAI_SERVICE
+ case EAI_SERVICE:
+ return "Class type not found";
+#endif
+#ifdef EAI_SOCKTYPE
+ case EAI_SOCKTYPE:
+ return "Socket type not supported";
+#endif
+ default:
+ return "Unknown server error";
+ }
+#endif /* HAVE_HSTRERROR */
+}
+
+static int gethostnameinfo(const struct sockaddr *sa,
+ char *node,
+ size_t nodelen,
+ int flags)
+{
+ int ret = -1;
+ char *p = NULL;
+
+ if (!(flags & NI_NUMERICHOST)) {
+ struct hostent *hp = gethostbyaddr(
+ (void *)&((struct sockaddr_in *)sa)->sin_addr,
+ sizeof (struct in_addr),
+ sa->sa_family);
+ ret = check_hostent_err(hp);
+ if (ret == 0) {
+ /* Name looked up successfully. */
+ ret = snprintf(node, nodelen, "%s", hp->h_name);
+ if (ret < 0 || (size_t)ret >= nodelen) {
+ return EAI_MEMORY;
+ }
+ if (flags & NI_NOFQDN) {
+ p = strchr(node,'.');
+ if (p) {
+ *p = '\0';
+ }
+ }
+ return 0;
+ }
+
+ if (flags & NI_NAMEREQD) {
+ /* If we require a name and didn't get one,
+ * automatically fail. */
+ return ret;
+ }
+ /* Otherwise just fall into the numeric host code... */
+ }
+ p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
+ ret = snprintf(node, nodelen, "%s", p);
+ if (ret < 0 || (size_t)ret >= nodelen) {
+ return EAI_MEMORY;
+ }
+ return 0;
+}
+
+static int getservicenameinfo(const struct sockaddr *sa,
+ char *service,
+ size_t servicelen,
+ int flags)
+{
+ int ret = -1;
+ int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+
+ if (!(flags & NI_NUMERICSERV)) {
+ struct servent *se = getservbyport(
+ port,
+ (flags & NI_DGRAM) ? "udp" : "tcp");
+ if (se && se->s_name) {
+ /* Service name looked up successfully. */
+ ret = snprintf(service, servicelen, "%s", se->s_name);
+ if (ret < 0 || (size_t)ret >= servicelen) {
+ return EAI_MEMORY;
+ }
+ return 0;
+ }
+ /* Otherwise just fall into the numeric service code... */
+ }
+ ret = snprintf(service, servicelen, "%d", port);
+ if (ret < 0 || (size_t)ret >= servicelen) {
+ return EAI_MEMORY;
+ }
+ return 0;
+}
+
+/*
+ * Convert an ipv4 address to a hostname.
+ *
+ * Bugs: - No IPv6 support.
+ */
+int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *node, size_t nodelen,
+ char *service, size_t servicelen, int flags)
+{
+
+ /* Invalid arguments. */
+ if (sa == NULL || (node == NULL && service == NULL)) {
+ return EAI_FAIL;
+ }
+
+ if (sa->sa_family != AF_INET) {
+ return EAI_FAIL;
+ }
+
+ if (salen < (socklen_t)sizeof (struct sockaddr_in)) {
+ return EAI_FAIL;
+ }
+
+ if (node) {
+ int ret = gethostnameinfo(sa, node, nodelen, flags);
+ if (ret)
+ return ret;
+ }
+
+ if (service) {
+ return getservicenameinfo(sa, service, servicelen, flags);
+ }
+ return 0;
+}
diff --git a/lib/getpass.c b/lib/getpass.c
new file mode 100644
index 0000000..dea3126
--- /dev/null
+++ b/lib/getpass.c
@@ -0,0 +1,72 @@
+/*
+ * An implementation of getpass for systems that lack one.
+ *
+ * Copyright (C) 2013 Roman Donchenko
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "rsync.h"
+
+char *getpass(const char *prompt)
+{
+ static char password[256];
+
+ BOOL tty_changed = False, read_success;
+ struct termios tty_old, tty_new;
+ FILE *in = stdin, *out = stderr;
+ FILE *tty = fopen("/dev/tty", "w+");
+
+ if (tty)
+ in = out = tty;
+
+ if (tcgetattr(fileno(in), &tty_old) == 0) {
+ tty_new = tty_old;
+ tty_new.c_lflag &= ~(ECHO | ISIG);
+
+ if (tcsetattr(fileno(in), TCSAFLUSH, &tty_new) == 0)
+ tty_changed = True;
+ }
+
+ if (!tty_changed)
+ fputs("(WARNING: will be visible) ", out);
+ fputs(prompt, out);
+ fflush(out);
+
+ read_success = fgets(password, sizeof password, in) != NULL;
+
+ /* Print the newline that hasn't been echoed. */
+ fputc('\n', out);
+
+ if (tty_changed)
+ tcsetattr(fileno(in), TCSAFLUSH, &tty_old);
+
+ if (tty)
+ fclose(tty);
+
+ if (read_success) {
+ /* Remove the trailing newline. */
+ size_t password_len = strlen(password);
+ if (password_len && password[password_len - 1] == '\n')
+ password[password_len - 1] = '\0';
+
+ return password;
+ }
+
+ return NULL;
+}
diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c
new file mode 100644
index 0000000..7be7368
--- /dev/null
+++ b/lib/inet_ntop.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 1996-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include "rsync.h"
+
+#define NS_INT16SZ 2
+#define NS_IN6ADDRSZ 16
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static const char *inet_ntop4(const unsigned char *src, char *dst,
+ size_t size);
+
+#ifdef AF_INET6
+static const char *inet_ntop6(const unsigned char *src, char *dst,
+ size_t size);
+#endif
+
+/* char *
+ * isc_net_ntop(af, src, dst, size)
+ * convert a network format address to presentation format.
+ * return:
+ * pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ * Paul Vixie, 1996.
+ */
+const char *
+inet_ntop(int af, const void *src, char *dst, size_t size)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_ntop4(src, dst, size));
+#ifdef AF_INET6
+ case AF_INET6:
+ return (inet_ntop6(src, dst, size));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (NULL);
+ }
+ /* NOTREACHED */
+}
+
+/* const char *
+ * inet_ntop4(src, dst, size)
+ * format an IPv4 address
+ * return:
+ * `dst' (as a const)
+ * notes:
+ * (1) uses no statics
+ * (2) takes a unsigned char* not an in_addr as input
+ * author:
+ * Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop4(const unsigned char *src, char *dst, size_t size)
+{
+ static const char *fmt = "%u.%u.%u.%u";
+ char tmp[sizeof "255.255.255.255"];
+ size_t len;
+
+ len = snprintf(tmp, sizeof tmp, fmt, src[0], src[1], src[2], src[3]);
+ if (len >= size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ memcpy(dst, tmp, len + 1);
+
+ return (dst);
+}
+
+/* const char *
+ * isc_inet_ntop6(src, dst, size)
+ * convert IPv6 binary address into presentation (printable) format
+ * author:
+ * Paul Vixie, 1996.
+ */
+#ifdef AF_INET6
+static const char *
+inet_ntop6(const unsigned char *src, char *dst, size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+ struct { int base, len; } best, cur;
+ unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ];
+ int i, inc;
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof words);
+ for (i = 0; i < NS_IN6ADDRSZ; i++)
+ words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+ best.base = -1;
+ cur.base = -1;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ if (words[i] == 0) {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ }
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ }
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ /* Are we inside the best run of 0x00's? */
+ if (best.base != -1 && i >= best.base &&
+ i < (best.base + best.len)) {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0)
+ *tp++ = ':';
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
+ if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
+ return (NULL);
+ tp += strlen(tp);
+ break;
+ }
+ inc = snprintf(tp, 5, "%x", words[i]);
+ assert(inc < 5);
+ tp += inc;
+ }
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) ==
+ (NS_IN6ADDRSZ / NS_INT16SZ))
+ *tp++ = ':';
+ *tp++ = '\0';
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((size_t)(tp - tmp) > size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ memcpy(dst, tmp, tp - tmp);
+ return (dst);
+}
+#endif /* AF_INET6 */
diff --git a/lib/inet_pton.c b/lib/inet_pton.c
new file mode 100644
index 0000000..4a0be88
--- /dev/null
+++ b/lib/inet_pton.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 1996-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "rsync.h"
+
+#define NS_INT16SZ 2
+#define NS_INADDRSZ 4
+#define NS_IN6ADDRSZ 16
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int inet_pton4(const char *src, unsigned char *dst);
+#ifdef INET6
+static int inet_pton6(const char *src, unsigned char *dst);
+#endif
+
+/* int
+ * inet_pton(af, src, dst)
+ * convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+inet_pton(int af,
+ const char *src,
+ void *dst)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_pton4(src, dst));
+#ifdef INET6
+ case AF_INET6:
+ return (inet_pton6(src, dst));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ /* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ * like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton4(src, dst)
+ const char *src;
+ unsigned char *dst;
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[NS_INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ *(tp = tmp) = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ unsigned int new = *tp * 10 + (pch - digits);
+
+ if (new > 255)
+ return (0);
+ *tp = new;
+ if (! saw_digit) {
+ if (++octets > 4)
+ return (0);
+ saw_digit = 1;
+ }
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4)
+ return (0);
+ *++tp = 0;
+ saw_digit = 0;
+ } else
+ return (0);
+ }
+ if (octets < 4)
+ return (0);
+ memcpy(dst, tmp, NS_INADDRSZ);
+ return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ * convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+#ifdef INET6
+static int
+inet_pton6(src, dst)
+ const char *src;
+ unsigned char *dst;
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ unsigned int val;
+
+ memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+ endp = tp + NS_IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return (0);
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ }
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += NS_INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if (saw_xdigit) {
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return (0);
+ memcpy(dst, tmp, NS_IN6ADDRSZ);
+ return (1);
+}
+#endif
diff --git a/lib/md-defines.h b/lib/md-defines.h
new file mode 100644
index 0000000..6ef6a68
--- /dev/null
+++ b/lib/md-defines.h
@@ -0,0 +1,37 @@
+/* Keep this simple so both C and ASM can use it */
+
+/* These allow something like CFLAGS=-DDISABLE_SHA512_DIGEST */
+#ifdef DISABLE_SHA256_DIGEST
+#undef SHA256_DIGEST_LENGTH
+#endif
+#ifdef DISABLE_SHA512_DIGEST
+#undef SHA512_DIGEST_LENGTH
+#endif
+
+#define MD4_DIGEST_LEN 16
+#define MD5_DIGEST_LEN 16
+#if defined SHA512_DIGEST_LENGTH
+#define MAX_DIGEST_LEN SHA512_DIGEST_LENGTH
+#elif defined SHA256_DIGEST_LENGTH
+#define MAX_DIGEST_LEN SHA256_DIGEST_LENGTH
+#elif defined SHA_DIGEST_LENGTH
+#define MAX_DIGEST_LEN SHA_DIGEST_LENGTH
+#else
+#define MAX_DIGEST_LEN MD5_DIGEST_LEN
+#endif
+
+#define CSUM_CHUNK 64
+
+#define CSUM_gone -1
+#define CSUM_NONE 0
+#define CSUM_MD4_ARCHAIC 1
+#define CSUM_MD4_BUSTED 2
+#define CSUM_MD4_OLD 3
+#define CSUM_MD4 4
+#define CSUM_MD5 5
+#define CSUM_XXH64 6
+#define CSUM_XXH3_64 7
+#define CSUM_XXH3_128 8
+#define CSUM_SHA1 9
+#define CSUM_SHA256 10
+#define CSUM_SHA512 11
diff --git a/lib/md5-asm-x86_64.S b/lib/md5-asm-x86_64.S
new file mode 100644
index 0000000..3737058
--- /dev/null
+++ b/lib/md5-asm-x86_64.S
@@ -0,0 +1,701 @@
+/*
+ * x86-64 optimized assembler MD5 implementation
+ *
+ * Author: Marc Bevand, 2004
+ *
+ * This code was placed in the public domain by the author. The original
+ * publication can be found at:
+ *
+ * https://www.zorinaq.com/papers/md5-amd64.html
+ */
+/*
+ * No modifications were made aside from changing the function and file names.
+ * The MD5_CTX structure as expected here (from OpenSSL) is binary compatible
+ * with the md_context used by rsync, for the fields accessed.
+ *
+ * Benchmarks (in MB/s) C ASM
+ * - Intel Atom D2700 302 334
+ * - Intel i7-7700hq 351 376
+ * - AMD ThreadRipper 2950x 728 784
+ *
+ * The original code was also incorporated into OpenSSL. It has since been
+ * modified there. Those changes have not been made here due to licensing
+ * incompatibilities. Benchmarks of those changes on the above CPUs did not
+ * show any significant difference in performance, though.
+ */
+
+#include "config.h"
+#include "md-defines.h"
+
+#ifdef USE_MD5_ASM /* { */
+
+#ifdef __APPLE__
+#define md5_process_asm _md5_process_asm
+#endif
+
+.text
+.align 16
+
+.globl md5_process_asm
+md5_process_asm:
+ push %rbp
+ push %rbx
+ push %r12
+ push %r13 # not really useful (r13 is unused)
+ push %r14
+ push %r15
+
+ # rdi = arg #1 (ctx, MD5_CTX pointer)
+ # rsi = arg #2 (ptr, data pointer)
+ # rdx = arg #3 (nbr, number of 16-word blocks to process)
+ mov %rdi, %rbp # rbp = ctx
+ shl $6, %rdx # rdx = nbr in bytes
+ lea (%rsi,%rdx), %rdi # rdi = end
+ mov 0*4(%rbp), %eax # eax = ctx->A
+ mov 1*4(%rbp), %ebx # ebx = ctx->B
+ mov 2*4(%rbp), %ecx # ecx = ctx->C
+ mov 3*4(%rbp), %edx # edx = ctx->D
+ # end is 'rdi'
+ # ptr is 'rsi'
+ # A is 'eax'
+ # B is 'ebx'
+ # C is 'ecx'
+ # D is 'edx'
+
+ cmp %rdi, %rsi # cmp end with ptr
+ je 1f # jmp if ptr == end
+
+ # BEGIN of loop over 16-word blocks
+2: # save old values of A, B, C, D
+ mov %eax, %r8d
+ mov %ebx, %r9d
+ mov %ecx, %r14d
+ mov %edx, %r15d
+ mov 0*4(%rsi), %r10d /* (NEXT STEP) X[0] */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ xor %ecx, %r11d /* y ^ ... */
+ lea -680876936(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r11d /* x & ... */
+ xor %edx, %r11d /* z ^ ... */
+ mov 1*4(%rsi),%r10d /* (NEXT STEP) X[1] */
+ add %r11d, %eax /* dst += ... */
+ rol $7, %eax /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %ebx, %eax /* dst += x */
+ xor %ebx, %r11d /* y ^ ... */
+ lea -389564586(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r11d /* x & ... */
+ xor %ecx, %r11d /* z ^ ... */
+ mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
+ add %r11d, %edx /* dst += ... */
+ rol $12, %edx /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %eax, %edx /* dst += x */
+ xor %eax, %r11d /* y ^ ... */
+ lea 606105819(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r11d /* x & ... */
+ xor %ebx, %r11d /* z ^ ... */
+ mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
+ add %r11d, %ecx /* dst += ... */
+ rol $17, %ecx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %edx, %ecx /* dst += x */
+ xor %edx, %r11d /* y ^ ... */
+ lea -1044525330(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r11d /* x & ... */
+ xor %eax, %r11d /* z ^ ... */
+ mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
+ add %r11d, %ebx /* dst += ... */
+ rol $22, %ebx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %ecx, %ebx /* dst += x */
+ xor %ecx, %r11d /* y ^ ... */
+ lea -176418897(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r11d /* x & ... */
+ xor %edx, %r11d /* z ^ ... */
+ mov 5*4(%rsi),%r10d /* (NEXT STEP) X[5] */
+ add %r11d, %eax /* dst += ... */
+ rol $7, %eax /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %ebx, %eax /* dst += x */
+ xor %ebx, %r11d /* y ^ ... */
+ lea 1200080426(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r11d /* x & ... */
+ xor %ecx, %r11d /* z ^ ... */
+ mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
+ add %r11d, %edx /* dst += ... */
+ rol $12, %edx /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %eax, %edx /* dst += x */
+ xor %eax, %r11d /* y ^ ... */
+ lea -1473231341(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r11d /* x & ... */
+ xor %ebx, %r11d /* z ^ ... */
+ mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
+ add %r11d, %ecx /* dst += ... */
+ rol $17, %ecx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %edx, %ecx /* dst += x */
+ xor %edx, %r11d /* y ^ ... */
+ lea -45705983(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r11d /* x & ... */
+ xor %eax, %r11d /* z ^ ... */
+ mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
+ add %r11d, %ebx /* dst += ... */
+ rol $22, %ebx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %ecx, %ebx /* dst += x */
+ xor %ecx, %r11d /* y ^ ... */
+ lea 1770035416(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r11d /* x & ... */
+ xor %edx, %r11d /* z ^ ... */
+ mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
+ add %r11d, %eax /* dst += ... */
+ rol $7, %eax /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %ebx, %eax /* dst += x */
+ xor %ebx, %r11d /* y ^ ... */
+ lea -1958414417(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r11d /* x & ... */
+ xor %ecx, %r11d /* z ^ ... */
+ mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
+ add %r11d, %edx /* dst += ... */
+ rol $12, %edx /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %eax, %edx /* dst += x */
+ xor %eax, %r11d /* y ^ ... */
+ lea -42063(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r11d /* x & ... */
+ xor %ebx, %r11d /* z ^ ... */
+ mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
+ add %r11d, %ecx /* dst += ... */
+ rol $17, %ecx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %edx, %ecx /* dst += x */
+ xor %edx, %r11d /* y ^ ... */
+ lea -1990404162(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r11d /* x & ... */
+ xor %eax, %r11d /* z ^ ... */
+ mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
+ add %r11d, %ebx /* dst += ... */
+ rol $22, %ebx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %ecx, %ebx /* dst += x */
+ xor %ecx, %r11d /* y ^ ... */
+ lea 1804603682(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r11d /* x & ... */
+ xor %edx, %r11d /* z ^ ... */
+ mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
+ add %r11d, %eax /* dst += ... */
+ rol $7, %eax /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %ebx, %eax /* dst += x */
+ xor %ebx, %r11d /* y ^ ... */
+ lea -40341101(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r11d /* x & ... */
+ xor %ecx, %r11d /* z ^ ... */
+ mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
+ add %r11d, %edx /* dst += ... */
+ rol $12, %edx /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %eax, %edx /* dst += x */
+ xor %eax, %r11d /* y ^ ... */
+ lea -1502002290(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r11d /* x & ... */
+ xor %ebx, %r11d /* z ^ ... */
+ mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
+ add %r11d, %ecx /* dst += ... */
+ rol $17, %ecx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %edx, %ecx /* dst += x */
+ xor %edx, %r11d /* y ^ ... */
+ lea 1236535329(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r11d /* x & ... */
+ xor %eax, %r11d /* z ^ ... */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ add %r11d, %ebx /* dst += ... */
+ rol $22, %ebx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %ecx, %ebx /* dst += x */
+ mov 1*4(%rsi), %r10d /* (NEXT STEP) X[1] */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ not %r11d /* not z */
+ lea -165796510(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r12d /* x & z */
+ and %ecx, %r11d /* y & (not z) */
+ mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %r12d, %eax /* dst += ... */
+ mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
+ rol $5, %eax /* dst <<< s */
+ add %ebx, %eax /* dst += x */
+ not %r11d /* not z */
+ lea -1069501632(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r12d /* x & z */
+ and %ebx, %r11d /* y & (not z) */
+ mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %r12d, %edx /* dst += ... */
+ mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
+ rol $9, %edx /* dst <<< s */
+ add %eax, %edx /* dst += x */
+ not %r11d /* not z */
+ lea 643717713(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r12d /* x & z */
+ and %eax, %r11d /* y & (not z) */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %r12d, %ecx /* dst += ... */
+ mov %eax, %r12d /* (NEXT STEP) z' = %eax */
+ rol $14, %ecx /* dst <<< s */
+ add %edx, %ecx /* dst += x */
+ not %r11d /* not z */
+ lea -373897302(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r12d /* x & z */
+ and %edx, %r11d /* y & (not z) */
+ mov 5*4(%rsi),%r10d /* (NEXT STEP) X[5] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %r12d, %ebx /* dst += ... */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ rol $20, %ebx /* dst <<< s */
+ add %ecx, %ebx /* dst += x */
+ not %r11d /* not z */
+ lea -701558691(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r12d /* x & z */
+ and %ecx, %r11d /* y & (not z) */
+ mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %r12d, %eax /* dst += ... */
+ mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
+ rol $5, %eax /* dst <<< s */
+ add %ebx, %eax /* dst += x */
+ not %r11d /* not z */
+ lea 38016083(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r12d /* x & z */
+ and %ebx, %r11d /* y & (not z) */
+ mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %r12d, %edx /* dst += ... */
+ mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
+ rol $9, %edx /* dst <<< s */
+ add %eax, %edx /* dst += x */
+ not %r11d /* not z */
+ lea -660478335(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r12d /* x & z */
+ and %eax, %r11d /* y & (not z) */
+ mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %r12d, %ecx /* dst += ... */
+ mov %eax, %r12d /* (NEXT STEP) z' = %eax */
+ rol $14, %ecx /* dst <<< s */
+ add %edx, %ecx /* dst += x */
+ not %r11d /* not z */
+ lea -405537848(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r12d /* x & z */
+ and %edx, %r11d /* y & (not z) */
+ mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %r12d, %ebx /* dst += ... */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ rol $20, %ebx /* dst <<< s */
+ add %ecx, %ebx /* dst += x */
+ not %r11d /* not z */
+ lea 568446438(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r12d /* x & z */
+ and %ecx, %r11d /* y & (not z) */
+ mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %r12d, %eax /* dst += ... */
+ mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
+ rol $5, %eax /* dst <<< s */
+ add %ebx, %eax /* dst += x */
+ not %r11d /* not z */
+ lea -1019803690(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r12d /* x & z */
+ and %ebx, %r11d /* y & (not z) */
+ mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %r12d, %edx /* dst += ... */
+ mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
+ rol $9, %edx /* dst <<< s */
+ add %eax, %edx /* dst += x */
+ not %r11d /* not z */
+ lea -187363961(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r12d /* x & z */
+ and %eax, %r11d /* y & (not z) */
+ mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %r12d, %ecx /* dst += ... */
+ mov %eax, %r12d /* (NEXT STEP) z' = %eax */
+ rol $14, %ecx /* dst <<< s */
+ add %edx, %ecx /* dst += x */
+ not %r11d /* not z */
+ lea 1163531501(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r12d /* x & z */
+ and %edx, %r11d /* y & (not z) */
+ mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %r12d, %ebx /* dst += ... */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ rol $20, %ebx /* dst <<< s */
+ add %ecx, %ebx /* dst += x */
+ not %r11d /* not z */
+ lea -1444681467(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r12d /* x & z */
+ and %ecx, %r11d /* y & (not z) */
+ mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %r12d, %eax /* dst += ... */
+ mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
+ rol $5, %eax /* dst <<< s */
+ add %ebx, %eax /* dst += x */
+ not %r11d /* not z */
+ lea -51403784(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r12d /* x & z */
+ and %ebx, %r11d /* y & (not z) */
+ mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %r12d, %edx /* dst += ... */
+ mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
+ rol $9, %edx /* dst <<< s */
+ add %eax, %edx /* dst += x */
+ not %r11d /* not z */
+ lea 1735328473(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r12d /* x & z */
+ and %eax, %r11d /* y & (not z) */
+ mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %r12d, %ecx /* dst += ... */
+ mov %eax, %r12d /* (NEXT STEP) z' = %eax */
+ rol $14, %ecx /* dst <<< s */
+ add %edx, %ecx /* dst += x */
+ not %r11d /* not z */
+ lea -1926607734(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r12d /* x & z */
+ and %edx, %r11d /* y & (not z) */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %r12d, %ebx /* dst += ... */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ rol $20, %ebx /* dst <<< s */
+ add %ecx, %ebx /* dst += x */
+ mov 5*4(%rsi), %r10d /* (NEXT STEP) X[5] */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ lea -378558(%eax,%r10d),%eax /* Const + dst + ... */
+ mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
+ xor %edx, %r11d /* z ^ ... */
+ xor %ebx, %r11d /* x ^ ... */
+ add %r11d, %eax /* dst += ... */
+ rol $4, %eax /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
+ add %ebx, %eax /* dst += x */
+ lea -2022574463(%edx,%r10d),%edx /* Const + dst + ... */
+ mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
+ xor %ecx, %r11d /* z ^ ... */
+ xor %eax, %r11d /* x ^ ... */
+ add %r11d, %edx /* dst += ... */
+ rol $11, %edx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) y' = %eax */
+ add %eax, %edx /* dst += x */
+ lea 1839030562(%ecx,%r10d),%ecx /* Const + dst + ... */
+ mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
+ xor %ebx, %r11d /* z ^ ... */
+ xor %edx, %r11d /* x ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ rol $16, %ecx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) y' = %edx */
+ add %edx, %ecx /* dst += x */
+ lea -35309556(%ebx,%r10d),%ebx /* Const + dst + ... */
+ mov 1*4(%rsi),%r10d /* (NEXT STEP) X[1] */
+ xor %eax, %r11d /* z ^ ... */
+ xor %ecx, %r11d /* x ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ rol $23, %ebx /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ add %ecx, %ebx /* dst += x */
+ lea -1530992060(%eax,%r10d),%eax /* Const + dst + ... */
+ mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
+ xor %edx, %r11d /* z ^ ... */
+ xor %ebx, %r11d /* x ^ ... */
+ add %r11d, %eax /* dst += ... */
+ rol $4, %eax /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
+ add %ebx, %eax /* dst += x */
+ lea 1272893353(%edx,%r10d),%edx /* Const + dst + ... */
+ mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
+ xor %ecx, %r11d /* z ^ ... */
+ xor %eax, %r11d /* x ^ ... */
+ add %r11d, %edx /* dst += ... */
+ rol $11, %edx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) y' = %eax */
+ add %eax, %edx /* dst += x */
+ lea -155497632(%ecx,%r10d),%ecx /* Const + dst + ... */
+ mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
+ xor %ebx, %r11d /* z ^ ... */
+ xor %edx, %r11d /* x ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ rol $16, %ecx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) y' = %edx */
+ add %edx, %ecx /* dst += x */
+ lea -1094730640(%ebx,%r10d),%ebx /* Const + dst + ... */
+ mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
+ xor %eax, %r11d /* z ^ ... */
+ xor %ecx, %r11d /* x ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ rol $23, %ebx /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ add %ecx, %ebx /* dst += x */
+ lea 681279174(%eax,%r10d),%eax /* Const + dst + ... */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ xor %edx, %r11d /* z ^ ... */
+ xor %ebx, %r11d /* x ^ ... */
+ add %r11d, %eax /* dst += ... */
+ rol $4, %eax /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
+ add %ebx, %eax /* dst += x */
+ lea -358537222(%edx,%r10d),%edx /* Const + dst + ... */
+ mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
+ xor %ecx, %r11d /* z ^ ... */
+ xor %eax, %r11d /* x ^ ... */
+ add %r11d, %edx /* dst += ... */
+ rol $11, %edx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) y' = %eax */
+ add %eax, %edx /* dst += x */
+ lea -722521979(%ecx,%r10d),%ecx /* Const + dst + ... */
+ mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
+ xor %ebx, %r11d /* z ^ ... */
+ xor %edx, %r11d /* x ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ rol $16, %ecx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) y' = %edx */
+ add %edx, %ecx /* dst += x */
+ lea 76029189(%ebx,%r10d),%ebx /* Const + dst + ... */
+ mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
+ xor %eax, %r11d /* z ^ ... */
+ xor %ecx, %r11d /* x ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ rol $23, %ebx /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ add %ecx, %ebx /* dst += x */
+ lea -640364487(%eax,%r10d),%eax /* Const + dst + ... */
+ mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
+ xor %edx, %r11d /* z ^ ... */
+ xor %ebx, %r11d /* x ^ ... */
+ add %r11d, %eax /* dst += ... */
+ rol $4, %eax /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
+ add %ebx, %eax /* dst += x */
+ lea -421815835(%edx,%r10d),%edx /* Const + dst + ... */
+ mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
+ xor %ecx, %r11d /* z ^ ... */
+ xor %eax, %r11d /* x ^ ... */
+ add %r11d, %edx /* dst += ... */
+ rol $11, %edx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) y' = %eax */
+ add %eax, %edx /* dst += x */
+ lea 530742520(%ecx,%r10d),%ecx /* Const + dst + ... */
+ mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
+ xor %ebx, %r11d /* z ^ ... */
+ xor %edx, %r11d /* x ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ rol $16, %ecx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) y' = %edx */
+ add %edx, %ecx /* dst += x */
+ lea -995338651(%ebx,%r10d),%ebx /* Const + dst + ... */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ xor %eax, %r11d /* z ^ ... */
+ xor %ecx, %r11d /* x ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ rol $23, %ebx /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ add %ecx, %ebx /* dst += x */
+ mov 0*4(%rsi), %r10d /* (NEXT STEP) X[0] */
+ mov $0xffffffff, %r11d
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx*/
+ lea -198630844(%eax,%r10d),%eax /* Const + dst + ... */
+ or %ebx, %r11d /* x | ... */
+ xor %ecx, %r11d /* y ^ ... */
+ add %r11d, %eax /* dst += ... */
+ mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
+ mov $0xffffffff, %r11d
+ rol $6, %eax /* dst <<< s */
+ xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
+ add %ebx, %eax /* dst += x */
+ lea 1126891415(%edx,%r10d),%edx /* Const + dst + ... */
+ or %eax, %r11d /* x | ... */
+ xor %ebx, %r11d /* y ^ ... */
+ add %r11d, %edx /* dst += ... */
+ mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
+ mov $0xffffffff, %r11d
+ rol $10, %edx /* dst <<< s */
+ xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
+ add %eax, %edx /* dst += x */
+ lea -1416354905(%ecx,%r10d),%ecx /* Const + dst + ... */
+ or %edx, %r11d /* x | ... */
+ xor %eax, %r11d /* y ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ mov 5*4(%rsi),%r10d /* (NEXT STEP) X[5] */
+ mov $0xffffffff, %r11d
+ rol $15, %ecx /* dst <<< s */
+ xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
+ add %edx, %ecx /* dst += x */
+ lea -57434055(%ebx,%r10d),%ebx /* Const + dst + ... */
+ or %ecx, %r11d /* x | ... */
+ xor %edx, %r11d /* y ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
+ mov $0xffffffff, %r11d
+ rol $21, %ebx /* dst <<< s */
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
+ add %ecx, %ebx /* dst += x */
+ lea 1700485571(%eax,%r10d),%eax /* Const + dst + ... */
+ or %ebx, %r11d /* x | ... */
+ xor %ecx, %r11d /* y ^ ... */
+ add %r11d, %eax /* dst += ... */
+ mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
+ mov $0xffffffff, %r11d
+ rol $6, %eax /* dst <<< s */
+ xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
+ add %ebx, %eax /* dst += x */
+ lea -1894986606(%edx,%r10d),%edx /* Const + dst + ... */
+ or %eax, %r11d /* x | ... */
+ xor %ebx, %r11d /* y ^ ... */
+ add %r11d, %edx /* dst += ... */
+ mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
+ mov $0xffffffff, %r11d
+ rol $10, %edx /* dst <<< s */
+ xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
+ add %eax, %edx /* dst += x */
+ lea -1051523(%ecx,%r10d),%ecx /* Const + dst + ... */
+ or %edx, %r11d /* x | ... */
+ xor %eax, %r11d /* y ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ mov 1*4(%rsi),%r10d /* (NEXT STEP) X[1] */
+ mov $0xffffffff, %r11d
+ rol $15, %ecx /* dst <<< s */
+ xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
+ add %edx, %ecx /* dst += x */
+ lea -2054922799(%ebx,%r10d),%ebx /* Const + dst + ... */
+ or %ecx, %r11d /* x | ... */
+ xor %edx, %r11d /* y ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
+ mov $0xffffffff, %r11d
+ rol $21, %ebx /* dst <<< s */
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
+ add %ecx, %ebx /* dst += x */
+ lea 1873313359(%eax,%r10d),%eax /* Const + dst + ... */
+ or %ebx, %r11d /* x | ... */
+ xor %ecx, %r11d /* y ^ ... */
+ add %r11d, %eax /* dst += ... */
+ mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
+ mov $0xffffffff, %r11d
+ rol $6, %eax /* dst <<< s */
+ xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
+ add %ebx, %eax /* dst += x */
+ lea -30611744(%edx,%r10d),%edx /* Const + dst + ... */
+ or %eax, %r11d /* x | ... */
+ xor %ebx, %r11d /* y ^ ... */
+ add %r11d, %edx /* dst += ... */
+ mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
+ mov $0xffffffff, %r11d
+ rol $10, %edx /* dst <<< s */
+ xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
+ add %eax, %edx /* dst += x */
+ lea -1560198380(%ecx,%r10d),%ecx /* Const + dst + ... */
+ or %edx, %r11d /* x | ... */
+ xor %eax, %r11d /* y ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
+ mov $0xffffffff, %r11d
+ rol $15, %ecx /* dst <<< s */
+ xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
+ add %edx, %ecx /* dst += x */
+ lea 1309151649(%ebx,%r10d),%ebx /* Const + dst + ... */
+ or %ecx, %r11d /* x | ... */
+ xor %edx, %r11d /* y ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
+ mov $0xffffffff, %r11d
+ rol $21, %ebx /* dst <<< s */
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
+ add %ecx, %ebx /* dst += x */
+ lea -145523070(%eax,%r10d),%eax /* Const + dst + ... */
+ or %ebx, %r11d /* x | ... */
+ xor %ecx, %r11d /* y ^ ... */
+ add %r11d, %eax /* dst += ... */
+ mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
+ mov $0xffffffff, %r11d
+ rol $6, %eax /* dst <<< s */
+ xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
+ add %ebx, %eax /* dst += x */
+ lea -1120210379(%edx,%r10d),%edx /* Const + dst + ... */
+ or %eax, %r11d /* x | ... */
+ xor %ebx, %r11d /* y ^ ... */
+ add %r11d, %edx /* dst += ... */
+ mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
+ mov $0xffffffff, %r11d
+ rol $10, %edx /* dst <<< s */
+ xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
+ add %eax, %edx /* dst += x */
+ lea 718787259(%ecx,%r10d),%ecx /* Const + dst + ... */
+ or %edx, %r11d /* x | ... */
+ xor %eax, %r11d /* y ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
+ mov $0xffffffff, %r11d
+ rol $15, %ecx /* dst <<< s */
+ xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
+ add %edx, %ecx /* dst += x */
+ lea -343485551(%ebx,%r10d),%ebx /* Const + dst + ... */
+ or %ecx, %r11d /* x | ... */
+ xor %edx, %r11d /* y ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ mov $0xffffffff, %r11d
+ rol $21, %ebx /* dst <<< s */
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
+ add %ecx, %ebx /* dst += x */
+ # add old values of A, B, C, D
+ add %r8d, %eax
+ add %r9d, %ebx
+ add %r14d, %ecx
+ add %r15d, %edx
+
+ # loop control
+ add $64, %rsi # ptr += 64
+ cmp %rdi, %rsi # cmp end with ptr
+ jb 2b # jmp if ptr < end
+ # END of loop over 16-word blocks
+1:
+ mov %eax, 0*4(%rbp) # ctx->A = A
+ mov %ebx, 1*4(%rbp) # ctx->B = B
+ mov %ecx, 2*4(%rbp) # ctx->C = C
+ mov %edx, 3*4(%rbp) # ctx->D = D
+
+ pop %r15
+ pop %r14
+ pop %r13 # not really useful (r13 is unused)
+ pop %r12
+ pop %rbx
+ pop %rbp
+ ret
+
+#endif /* } USE_MD5_ASM */
diff --git a/lib/md5.c b/lib/md5.c
new file mode 100644
index 0000000..f36c5ba
--- /dev/null
+++ b/lib/md5.c
@@ -0,0 +1,321 @@
+/*
+ * RFC 1321 compliant MD5 implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+void md5_begin(md_context *ctx)
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xEFCDAB89;
+ ctx->C = 0x98BADCFE;
+ ctx->D = 0x10325476;
+
+ ctx->totalN = ctx->totalN2 = 0;
+}
+
+static void md5_process(md_context *ctx, const uchar data[CSUM_CHUNK])
+{
+ uint32 X[16], A, B, C, D;
+
+ A = ctx->A;
+ B = ctx->B;
+ C = ctx->C;
+ D = ctx->D;
+
+ X[0] = IVALu(data, 0);
+ X[1] = IVALu(data, 4);
+ X[2] = IVALu(data, 8);
+ X[3] = IVALu(data, 12);
+ X[4] = IVALu(data, 16);
+ X[5] = IVALu(data, 20);
+ X[6] = IVALu(data, 24);
+ X[7] = IVALu(data, 28);
+ X[8] = IVALu(data, 32);
+ X[9] = IVALu(data, 36);
+ X[10] = IVALu(data, 40);
+ X[11] = IVALu(data, 44);
+ X[12] = IVALu(data, 48);
+ X[13] = IVALu(data, 52);
+ X[14] = IVALu(data, 56);
+ X[15] = IVALu(data, 60);
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t) a += F(b,c,d) + X[k] + t, a = S(a,s) + b
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+ P(A, B, C, D, 0, 7, 0xD76AA478);
+ P(D, A, B, C, 1, 12, 0xE8C7B756);
+ P(C, D, A, B, 2, 17, 0x242070DB);
+ P(B, C, D, A, 3, 22, 0xC1BDCEEE);
+ P(A, B, C, D, 4, 7, 0xF57C0FAF);
+ P(D, A, B, C, 5, 12, 0x4787C62A);
+ P(C, D, A, B, 6, 17, 0xA8304613);
+ P(B, C, D, A, 7, 22, 0xFD469501);
+ P(A, B, C, D, 8, 7, 0x698098D8);
+ P(D, A, B, C, 9, 12, 0x8B44F7AF);
+ P(C, D, A, B, 10, 17, 0xFFFF5BB1);
+ P(B, C, D, A, 11, 22, 0x895CD7BE);
+ P(A, B, C, D, 12, 7, 0x6B901122);
+ P(D, A, B, C, 13, 12, 0xFD987193);
+ P(C, D, A, B, 14, 17, 0xA679438E);
+ P(B, C, D, A, 15, 22, 0x49B40821);
+
+#undef F
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+ P(A, B, C, D, 1, 5, 0xF61E2562);
+ P(D, A, B, C, 6, 9, 0xC040B340);
+ P(C, D, A, B, 11, 14, 0x265E5A51);
+ P(B, C, D, A, 0, 20, 0xE9B6C7AA);
+ P(A, B, C, D, 5, 5, 0xD62F105D);
+ P(D, A, B, C, 10, 9, 0x02441453);
+ P(C, D, A, B, 15, 14, 0xD8A1E681);
+ P(B, C, D, A, 4, 20, 0xE7D3FBC8);
+ P(A, B, C, D, 9, 5, 0x21E1CDE6);
+ P(D, A, B, C, 14, 9, 0xC33707D6);
+ P(C, D, A, B, 3, 14, 0xF4D50D87);
+ P(B, C, D, A, 8, 20, 0x455A14ED);
+ P(A, B, C, D, 13, 5, 0xA9E3E905);
+ P(D, A, B, C, 2, 9, 0xFCEFA3F8);
+ P(C, D, A, B, 7, 14, 0x676F02D9);
+ P(B, C, D, A, 12, 20, 0x8D2A4C8A);
+
+#undef F
+#define F(x,y,z) (x ^ y ^ z)
+
+ P(A, B, C, D, 5, 4, 0xFFFA3942);
+ P(D, A, B, C, 8, 11, 0x8771F681);
+ P(C, D, A, B, 11, 16, 0x6D9D6122);
+ P(B, C, D, A, 14, 23, 0xFDE5380C);
+ P(A, B, C, D, 1, 4, 0xA4BEEA44);
+ P(D, A, B, C, 4, 11, 0x4BDECFA9);
+ P(C, D, A, B, 7, 16, 0xF6BB4B60);
+ P(B, C, D, A, 10, 23, 0xBEBFBC70);
+ P(A, B, C, D, 13, 4, 0x289B7EC6);
+ P(D, A, B, C, 0, 11, 0xEAA127FA);
+ P(C, D, A, B, 3, 16, 0xD4EF3085);
+ P(B, C, D, A, 6, 23, 0x04881D05);
+ P(A, B, C, D, 9, 4, 0xD9D4D039);
+ P(D, A, B, C, 12, 11, 0xE6DB99E5);
+ P(C, D, A, B, 15, 16, 0x1FA27CF8);
+ P(B, C, D, A, 2, 23, 0xC4AC5665);
+
+#undef F
+#define F(x,y,z) (y ^ (x | ~z))
+
+ P(A, B, C, D, 0, 6, 0xF4292244);
+ P(D, A, B, C, 7, 10, 0x432AFF97);
+ P(C, D, A, B, 14, 15, 0xAB9423A7);
+ P(B, C, D, A, 5, 21, 0xFC93A039);
+ P(A, B, C, D, 12, 6, 0x655B59C3);
+ P(D, A, B, C, 3, 10, 0x8F0CCC92);
+ P(C, D, A, B, 10, 15, 0xFFEFF47D);
+ P(B, C, D, A, 1, 21, 0x85845DD1);
+ P(A, B, C, D, 8, 6, 0x6FA87E4F);
+ P(D, A, B, C, 15, 10, 0xFE2CE6E0);
+ P(C, D, A, B, 6, 15, 0xA3014314);
+ P(B, C, D, A, 13, 21, 0x4E0811A1);
+ P(A, B, C, D, 4, 6, 0xF7537E82);
+ P(D, A, B, C, 11, 10, 0xBD3AF235);
+ P(C, D, A, B, 2, 15, 0x2AD7D2BB);
+ P(B, C, D, A, 9, 21, 0xEB86D391);
+
+#undef F
+
+ ctx->A += A;
+ ctx->B += B;
+ ctx->C += C;
+ ctx->D += D;
+}
+
+#ifdef USE_MD5_ASM
+#if CSUM_CHUNK != 64
+#error The MD5 ASM code does not support CSUM_CHUNK != 64
+#endif
+extern void md5_process_asm(md_context *ctx, const void *data, size_t num);
+#endif
+
+void md5_update(md_context *ctx, const uchar *input, uint32 length)
+{
+ uint32 left, fill;
+
+ if (!length)
+ return;
+
+ left = ctx->totalN & 0x3F;
+ fill = CSUM_CHUNK - left;
+
+ ctx->totalN += length;
+ ctx->totalN &= 0xFFFFFFFF;
+
+ if (ctx->totalN < length)
+ ctx->totalN2++;
+
+ if (left && length >= fill) {
+ memcpy(ctx->buffer + left, input, fill);
+ md5_process(ctx, ctx->buffer);
+ length -= fill;
+ input += fill;
+ left = 0;
+ }
+
+#ifdef USE_MD5_ASM /* { */
+ if (length >= CSUM_CHUNK) {
+ uint32 chunks = length / CSUM_CHUNK;
+ md5_process_asm(ctx, input, chunks);
+ length -= chunks * CSUM_CHUNK;
+ input += chunks * CSUM_CHUNK;
+ }
+#else /* } { */
+ while (length >= CSUM_CHUNK) {
+ md5_process(ctx, input);
+ length -= CSUM_CHUNK;
+ input += CSUM_CHUNK;
+ }
+#endif /* } */
+
+ if (length)
+ memcpy(ctx->buffer + left, input, length);
+}
+
+static uchar md5_padding[CSUM_CHUNK] = { 0x80 };
+
+void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
+{
+ uint32 last, padn;
+ uint32 high, low;
+ uchar msglen[8];
+
+ high = (ctx->totalN >> 29)
+ | (ctx->totalN2 << 3);
+ low = (ctx->totalN << 3);
+
+ SIVALu(msglen, 0, low);
+ SIVALu(msglen, 4, high);
+
+ last = ctx->totalN & 0x3F;
+ padn = last < 56 ? 56 - last : 120 - last;
+
+ md5_update(ctx, md5_padding, padn);
+ md5_update(ctx, msglen, 8);
+
+ SIVALu(digest, 0, ctx->A);
+ SIVALu(digest, 4, ctx->B);
+ SIVALu(digest, 8, ctx->C);
+ SIVALu(digest, 12, ctx->D);
+}
+
+#ifdef TEST_MD5 /* { */
+
+void get_md5(uchar *out, const uchar *input, int n)
+{
+ md_context ctx;
+ md5_begin(&ctx);
+ md5_update(&ctx, input, n);
+ md5_result(&ctx, out);
+}
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * those are the standard RFC 1321 test vectors
+ */
+
+static struct {
+ char *str, *md5;
+} tests[] = {
+ { "",
+ "d41d8cd98f00b204e9800998ecf8427e" },
+ { "a",
+ "0cc175b9c0f1b6a831c399e269772661" },
+ { "abc",
+ "900150983cd24fb0d6963f7d28e17f72" },
+ { "message digest",
+ "f96b697d7cb7938d525a2f31aaf161d0" },
+ { "abcdefghijklmnopqrstuvwxyz",
+ "c3fcd3d76192e4007dfb496cca67e13b" },
+ { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "d174ab98d277d9f5a5611c2c9f419d9f" },
+ { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "57edf4a22be3c955ac49da2e2107b67a" },
+ { NULL, NULL }
+};
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ int i, j;
+ char output[33];
+ md_context ctx;
+ uchar buf[1000];
+ uchar md5sum[MD5_DIGEST_LEN];
+
+ if (argc < 2) {
+ printf("\nMD5 Validation Tests:\n\n");
+
+ for (i = 0; tests[i].str; i++) {
+ char *str = tests[i].str;
+ char *chk = tests[i].md5;
+
+ printf(" Test %d ", i + 1);
+
+ get_md5(md5sum, str, strlen(str));
+
+ for (j = 0; j < MD5_DIGEST_LEN; j++)
+ sprintf(output + j * 2, "%02x", md5sum[j]);
+
+ if (memcmp(output, chk, 32)) {
+ printf("failed!\n");
+ return 1;
+ }
+
+ printf("passed.\n");
+ }
+
+ printf("\n");
+ return 0;
+ }
+
+ while (--argc) {
+ if (!(f = fopen(*++argv, "rb"))) {
+ perror("fopen");
+ return 1;
+ }
+
+ md5_begin(&ctx);
+
+ while ((i = fread(buf, 1, sizeof buf, f)) > 0)
+ md5_update(&ctx, buf, i);
+
+ md5_result(&ctx, md5sum);
+
+ for (j = 0; j < MD5_DIGEST_LEN; j++)
+ printf("%02x", md5sum[j]);
+
+ printf(" %s\n", *argv);
+ }
+
+ return 0;
+}
+
+#endif /* } */
diff --git a/lib/mdfour.c b/lib/mdfour.c
new file mode 100644
index 0000000..6203658
--- /dev/null
+++ b/lib/mdfour.c
@@ -0,0 +1,247 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * An implementation of MD4 designed for use in the SMB authentication protocol.
+ *
+ * Copyright (C) 1997-1998 Andrew Tridgell
+ * Copyright (C) 2005-2020 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+/* NOTE: This code makes no attempt to be fast!
+ *
+ * It assumes that a int is at least 32 bits long. */
+
+static md_context *m;
+
+#define MASK32 (0xffffffff)
+
+#define F(X,Y,Z) ((((X)&(Y)) | ((~(X))&(Z))))
+#define G(X,Y,Z) ((((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))))
+#define H(X,Y,Z) (((X)^(Y)^(Z)))
+#define lshift(x,s) (((((x)<<(s))&MASK32) | (((x)>>(32-(s)))&MASK32)))
+
+#define ROUND1(a,b,c,d,k,s) a = lshift((a + F(b,c,d) + M[k])&MASK32, s)
+#define ROUND2(a,b,c,d,k,s) a = lshift((a + G(b,c,d) + M[k] + 0x5A827999)&MASK32,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift((a + H(b,c,d) + M[k] + 0x6ED9EBA1)&MASK32,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(uint32 *M)
+{
+ uint32 AA, BB, CC, DD;
+ uint32 A,B,C,D;
+
+ A = m->A; B = m->B; C = m->C; D = m->D;
+ AA = A; BB = B; CC = C; DD = D;
+
+ ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7);
+ ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19);
+ ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7);
+ ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19);
+ ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7);
+ ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19);
+ ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7);
+ ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19);
+
+ ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5);
+ ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13);
+ ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5);
+ ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13);
+ ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5);
+ ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13);
+ ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5);
+ ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13);
+
+ ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9);
+ ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15);
+ ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9);
+ ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15);
+ ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9);
+ ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15);
+ ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9);
+ ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15);
+
+ A += AA; B += BB;
+ C += CC; D += DD;
+
+ A &= MASK32; B &= MASK32;
+ C &= MASK32; D &= MASK32;
+
+ m->A = A; m->B = B; m->C = C; m->D = D;
+}
+
+static void copy64(uint32 *M, const uchar *in)
+{
+ int i;
+
+ for (i = 0; i < MD4_DIGEST_LEN; i++) {
+ M[i] = (in[i*4+3] << 24) | (in[i*4+2] << 16)
+ | (in[i*4+1] << 8) | (in[i*4+0] << 0);
+ }
+}
+
+static void copy4(uchar *out,uint32 x)
+{
+ out[0] = x&0xFF;
+ out[1] = (x>>8)&0xFF;
+ out[2] = (x>>16)&0xFF;
+ out[3] = (x>>24)&0xFF;
+}
+
+void mdfour_begin(md_context *md)
+{
+ md->A = 0x67452301;
+ md->B = 0xefcdab89;
+ md->C = 0x98badcfe;
+ md->D = 0x10325476;
+ md->totalN = 0;
+ md->totalN2 = 0;
+}
+
+static void mdfour_tail(const uchar *in, uint32 length)
+{
+ uchar buf[128];
+ uint32 M[16];
+ extern int protocol_version;
+
+ /*
+ * Count total number of bits, modulo 2^64
+ */
+ m->totalN += length << 3;
+ if (m->totalN < (length << 3))
+ m->totalN2++;
+ m->totalN2 += length >> 29;
+
+ memset(buf, 0, 128);
+ if (length)
+ memcpy(buf, in, length);
+ buf[length] = 0x80;
+
+ if (length <= 55) {
+ copy4(buf+56, m->totalN);
+ /*
+ * Prior to protocol version 27 only the number of bits
+ * modulo 2^32 was included. MD4 requires the number
+ * of bits modulo 2^64, which was fixed starting with
+ * protocol version 27.
+ */
+ if (protocol_version >= 27)
+ copy4(buf+60, m->totalN2);
+ copy64(M, buf);
+ mdfour64(M);
+ } else {
+ copy4(buf+120, m->totalN);
+ /*
+ * Prior to protocol version 27 only the number of bits
+ * modulo 2^32 was included. MD4 requires the number
+ * of bits modulo 2^64, which was fixed starting with
+ * protocol version 27.
+ */
+ if (protocol_version >= 27)
+ copy4(buf+124, m->totalN2);
+ copy64(M, buf);
+ mdfour64(M);
+ copy64(M, buf+64);
+ mdfour64(M);
+ }
+}
+
+void mdfour_update(md_context *md, const uchar *in, uint32 length)
+{
+ uint32 M[16];
+
+ m = md;
+
+ if (length == 0)
+ mdfour_tail(in, length);
+
+ while (length >= 64) {
+ copy64(M, in);
+ mdfour64(M);
+ in += 64;
+ length -= 64;
+ m->totalN += 64 << 3;
+ if (m->totalN < 64 << 3)
+ m->totalN2++;
+ }
+
+ if (length)
+ mdfour_tail(in, length);
+}
+
+void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN])
+{
+ m = md;
+
+ copy4(digest, m->A);
+ copy4(digest+4, m->B);
+ copy4(digest+8, m->C);
+ copy4(digest+12, m->D);
+}
+
+#ifdef TEST_MDFOUR
+
+void mdfour(uchar digest[MD4_DIGEST_LEN], uchar *in, int length)
+{
+ md_context md;
+ mdfour_begin(&md);
+ mdfour_update(&md, in, length);
+ mdfour_result(&md, digest);
+}
+
+int protocol_version = 28;
+
+static void file_checksum1(char *fname)
+{
+ int fd, i, was_multiple_of_64 = 1;
+ md_context md;
+ uchar buf[64*1024], sum[MD4_DIGEST_LEN];
+
+ fd = open(fname,O_RDONLY);
+ if (fd == -1) {
+ perror("fname");
+ exit(1);
+ }
+
+ mdfour_begin(&md);
+
+ while (1) {
+ int n = read(fd, buf, sizeof buf);
+ if (n <= 0)
+ break;
+ was_multiple_of_64 = !(n % 64);
+ mdfour_update(&md, buf, n);
+ }
+ if (was_multiple_of_64 && protocol_version >= 27)
+ mdfour_update(&md, buf, 0);
+
+ close(fd);
+
+ mdfour_result(&md, sum);
+
+ for (i = 0; i < MD4_DIGEST_LEN; i++)
+ printf("%02X", sum[i]);
+ printf("\n");
+}
+
+ int main(int argc, char *argv[])
+{
+ while (--argc)
+ file_checksum1(*++argv);
+ return 0;
+}
+#endif
diff --git a/lib/mdigest.h b/lib/mdigest.h
new file mode 100644
index 0000000..9d52ef5
--- /dev/null
+++ b/lib/mdigest.h
@@ -0,0 +1,22 @@
+/* The include file for both the MD4 and MD5 routines. */
+
+#ifdef USE_OPENSSL
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+#endif
+#include "md-defines.h"
+
+typedef struct {
+ uint32 A, B, C, D;
+ uint32 totalN; /* bit count, lower 32 bits */
+ uint32 totalN2; /* bit count, upper 32 bits */
+ uchar buffer[CSUM_CHUNK];
+} md_context;
+
+void mdfour_begin(md_context *md);
+void mdfour_update(md_context *md, const uchar *in, uint32 length);
+void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN]);
+
+void md5_begin(md_context *ctx);
+void md5_update(md_context *ctx, const uchar *input, uint32 length);
+void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN]);
diff --git a/lib/permstring.c b/lib/permstring.c
new file mode 100644
index 0000000..ab8e26c
--- /dev/null
+++ b/lib/permstring.c
@@ -0,0 +1,65 @@
+/*
+ * A single utility routine.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2019 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+/* Produce a string representation of Unix mode bits like that used by ls(1).
+ * The "buf" buffer must be at least 11 characters. */
+void permstring(char *perms, mode_t mode)
+{
+ static const char *perm_map = "rwxrwxrwx";
+ int i;
+
+ strlcpy(perms, "----------", 11);
+
+ for (i = 0; i < 9; i++) {
+ if (mode & (1 << i))
+ perms[9-i] = perm_map[8-i];
+ }
+
+ /* Handle setuid/sticky bits. You might think the indices are
+ * off by one, but remember there's a type char at the
+ * start. */
+ if (mode & S_ISUID)
+ perms[3] = (mode & S_IXUSR) ? 's' : 'S';
+
+ if (mode & S_ISGID)
+ perms[6] = (mode & S_IXGRP) ? 's' : 'S';
+
+#ifdef S_ISVTX
+ if (mode & S_ISVTX)
+ perms[9] = (mode & S_IXOTH) ? 't' : 'T';
+#endif
+
+ if (S_ISDIR(mode))
+ perms[0] = 'd';
+ else if (S_ISLNK(mode))
+ perms[0] = 'l';
+ else if (S_ISBLK(mode))
+ perms[0] = 'b';
+ else if (S_ISCHR(mode))
+ perms[0] = 'c';
+ else if (S_ISSOCK(mode))
+ perms[0] = 's';
+ else if (S_ISFIFO(mode))
+ perms[0] = 'p';
+}
diff --git a/lib/permstring.h b/lib/permstring.h
new file mode 100644
index 0000000..b996f2a
--- /dev/null
+++ b/lib/permstring.h
@@ -0,0 +1,3 @@
+#define PERMSTRING_SIZE 11
+
+void permstring(char *perms, mode_t mode);
diff --git a/lib/pool_alloc.3 b/lib/pool_alloc.3
new file mode 100644
index 0000000..128c1f7
--- /dev/null
+++ b/lib/pool_alloc.3
@@ -0,0 +1,268 @@
+.ds d \-\^\-
+.ds o \fR[\fP
+.ds c \fR]\fP
+.ds | \fR|\fP
+.de D
+\\.B \*d\\$1
+..
+.de DI
+\\.BI \*d\\$1 \\$2
+..
+.de DR
+\\.BR \*d\\$1 \\$2
+..
+.de Di
+\\.BI \*d\\$1 " \\$2"
+..
+.de Db
+\\.B \*d\\$1 " \\$2"
+..
+.de Df
+\\.B \*d\*ono\*c\\$1
+..
+.de See
+See \fB\\$1\fP for details.
+..
+.de SeeIn
+See \fB\\$1\fP in \fB\\$2\fP for details.
+..
+.TH POOL_ALLOC 3
+.SH NAME
+pool_alloc, pool_free, pool_free_old, pool_talloc, pool_tfree, pool_create, pool_destroy, pool_boundary
+\- Allocate and free memory in managed allocation pools.
+.SH SYNOPSIS
+.B #include "pool_alloc.h"
+
+\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char*,char*,int), int \fIflags\fB);
+
+\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB);
+
+\fBvoid *pool_alloc(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, char *\fImsg\fB);
+
+\fBvoid pool_free(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, void *\fIaddr\fB);
+
+\fBvoid pool_free_old(struct alloc_pool *\fIpool\fB, void *\fIaddr\fB);
+
+\fBvoid *pool_talloc(struct alloc_pool *\fIpool\fB, \fItype\fB), int \fIcount\fB, char *\fImsg\fB);
+
+\fBvoid pool_tfree(struct alloc_pool *\fIpool\fB, \fItype\fB, int \fIcount\fB, void *\fIaddr\fB);
+
+\fBvoid pool_boundary(struct alloc_pool *\fIpool\fB, sise_t \fIsize\fB);
+.SH DESCRIPTION
+.P
+The pool allocation routines use
+.B malloc()
+for underlying memory management.
+What allocation pools do is cause memory within a given pool
+to be allocated in large contiguous blocks
+(called extents) that will be reusable when freed. Unlike
+.BR malloc() ,
+the allocations are not managed individually.
+Instead, each extent tracks the total free memory within the
+extent. Each extent can either be used to allocate memory
+or to manage the freeing of memory within that extent.
+When an extent has less free memory than a given
+allocation request, the current extent ceases to be used
+for allocation. See also the
+.B pool_boundary()
+function.
+.P
+This form of memory management is suited to large numbers of small
+related allocations that are held for a while
+and then freed as a group.
+Because the
+underlying allocations are done in large contiguous extents,
+when an extent is freed, it can release a large enough
+contiguous block of memory to allow the memory to be returned
+to the OS for use by whatever program needs it.
+You can allocate from one or more memory pools and/or
+.B malloc()
+all at the same time without interfering with how pools work.
+.P
+.B pool_create()
+Creates an allocation pool for subsequent calls to the pool
+allocation functions.
+When an extent is created for allocations it will be
+.I size
+bytes.
+Allocations from the pool have their sizes rounded up to a
+multiple of
+.I quantum
+bytes in length.
+Specifying
+.B 0
+for
+.I quantum
+will produce a quantum that should meet maximal alignment
+on most platforms.
+Unless
+.B POOL_NO_QALIGN
+is set in the
+.IR flags ,
+allocations will be aligned to addresses that are a
+multiple of
+.IR quantum .
+A
+.B NULL
+may be specified for the
+.I bomb
+function pointer if it is not needed. (See the
+.B pool_alloc()
+function for how it is used.)
+If
+.B POOL_CLEAR
+is set in the
+.IR flags ,
+all allocations from the pool will be initialized to zeros.
+If either
+.B POOL_PREPEND
+or
+.B POOL_INTERN
+is specified in the
+.IR flags ,
+each extent's data structure will be allocated at the start of the
+.IR size -length
+buffer (rather than as a separate, non-pool allocation), with the
+former extending the
+.I size
+to hold the structure, and the latter subtracting the structure's
+length from the indicated
+.IR size .
+.P
+.B pool_destroy()
+destroys an allocation
+.I pool
+and frees all its associated memory.
+.P
+.B pool_alloc()
+allocates
+.I size
+bytes from the specified
+.IR pool .
+If
+.I size
+is
+.BR 0 ,
+.I quantum
+bytes will be allocated.
+If the pool has been created without
+.BR POOL_NO_QALIGN ,
+every chunk of memory that is returned will be suitably aligned.
+You can use this with the default
+.I quantum
+size to ensure that all memory can store a variable of any type.
+If the requested memory cannot be allocated, the
+.I bomb()
+function will be called with
+.I msg
+as its sole argument (if the function was defined at the time
+the pool was created), and then a
+.B NULL
+address is returned (assuming that the bomb function didn't exit).
+.P
+.B pool_free()
+frees
+.I size
+bytes pointed to by an
+.I addr
+that was previously allocated in the specified
+.IR pool .
+If
+.I size
+is
+.BR 0 ,
+.I quantum
+bytes will be freed.
+The memory freed within an extent will not be reusable until
+all of the memory in that extent has been freed with one
+exception: the most recent pool allocation may be freed back
+into the pool prior to making any further allocations.
+If enough free calls are made to indicate that an extent has no
+remaining allocated objects (as computed by the total freed size for
+an extent), its memory will be completely freed back to the system.
+If
+.I addr
+is
+.BR NULL ,
+no memory will be freed, but subsequent allocations will come
+from a new extent.
+.P
+.B pool_free_old()
+takes a boundary
+.I addr
+value that was returned by
+.B pool_boundary()
+and frees up any extents in the
+.I pool
+that have data allocated from that point backward in time.
+NOTE: you must NOT mix calls to both
+.B pool_free
+and
+.B pool_free_old
+on the same pool!
+.P
+.B pool_boundary()
+asks for a boundary value that can be sent to
+.B pool_free_old()
+at a later time to free up all memory allocated prior to a particular
+moment in time.
+If the extent that holds the boundary point has allocations from after the
+boundary point, it will not be freed until a future
+.B pool_free_old()
+call encompasses the entirety of the extent's data.
+If
+.I len
+is non-zero, the call will also check if the active extent has at least
+that much free memory available in it, and if not, it will mark the
+extent as inactive, forcing a new extent to be used for future allocations.
+(You can specify -1 for
+.I len
+if you want to force a new extent to start.)
+.P
+.B pool_talloc()
+is a macro that takes a
+.I type
+and a
+.I count
+instead of a
+.IR size .
+It casts the return value to the correct pointer type.
+.P
+.B pool_tfree
+is a macro that calls
+.B pool_free
+on memory that was allocated by
+.BR pool_talloc() .
+.SH RETURN VALUE
+.B pool_create()
+returns a pointer to
+.BR "struct alloc_pool" .
+.P
+.B pool_alloc()
+and
+.B pool_talloc()
+return pointers to the allocated memory,
+or NULL if the request fails.
+The return type of
+.B pool_alloc()
+will normally require casting to the desired type but
+.B pool_talloc()
+will returns a pointer of the requested
+.IR type .
+.P
+.B pool_boundary()
+returns a pointer that should only be used in a call to
+.BR pool_free_old() .
+.P
+.BR pool_free() ,
+.BR pool_free_old() ,
+.B pool_tfree()
+and
+.B pool_destroy()
+return no value.
+.SH SEE ALSO
+.nf
+malloc(3)
+.SH AUTHOR
+pool_alloc was created by J.W. Schultz of Pegasystems Technologies.
+.SH BUGS AND ISSUES
diff --git a/lib/pool_alloc.c b/lib/pool_alloc.c
new file mode 100644
index 0000000..a1a7245
--- /dev/null
+++ b/lib/pool_alloc.c
@@ -0,0 +1,375 @@
+#include "rsync.h"
+
+#define POOL_DEF_EXTENT (32 * 1024)
+
+#define POOL_QALIGN_P2 (1<<16) /* power-of-2 qalign */
+
+struct alloc_pool
+{
+ size_t size; /* extent size */
+ size_t quantum; /* allocation quantum */
+ struct pool_extent *extents; /* top extent is "live" */
+ void (*bomb)(); /* called if malloc fails */
+ int flags;
+
+ /* statistical data */
+ unsigned long e_created; /* extents created */
+ unsigned long e_freed; /* extents destroyed */
+ int64 n_allocated; /* calls to alloc */
+ int64 n_freed; /* calls to free */
+ int64 b_allocated; /* cum. bytes allocated */
+ int64 b_freed; /* cum. bytes freed */
+};
+
+struct pool_extent
+{
+ struct pool_extent *next;
+ void *start; /* starting address */
+ size_t free; /* free bytecount */
+ size_t bound; /* trapped free bytes */
+};
+
+struct align_test {
+ uchar foo;
+ union {
+ int64 i;
+ void *p;
+ } bar;
+};
+
+#define MINALIGN offsetof(struct align_test, bar)
+
+/* Temporarily cast a void* var into a char* var when adding an offset (to
+ * keep some compilers from complaining about the pointer arithmetic). */
+#define PTR_ADD(b,o) ( (void*) ((char*)(b) + (o)) )
+
+alloc_pool_t
+pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags)
+{
+ struct alloc_pool *pool;
+
+ if ((MINALIGN & (MINALIGN - 1)) != (0)) {
+ if (bomb)
+ (*bomb)("Compiler error: MINALIGN is not a power of 2", __FILE__, __LINE__);
+ return NULL;
+ }
+
+ if (!(pool = new0(struct alloc_pool)))
+ return NULL;
+
+ if (!size)
+ size = POOL_DEF_EXTENT;
+ if (!quantum)
+ quantum = MINALIGN;
+
+ if (flags & POOL_INTERN) {
+ if (size <= sizeof (struct pool_extent))
+ size = quantum;
+ else
+ size -= sizeof (struct pool_extent);
+ flags |= POOL_PREPEND;
+ }
+
+ if (quantum <= 1)
+ flags = (flags | POOL_NO_QALIGN) & ~POOL_QALIGN_P2;
+ else if (!(flags & POOL_NO_QALIGN)) {
+ if (size % quantum)
+ size += quantum - size % quantum;
+ /* If quantum is a power of 2, we'll avoid using modulus. */
+ if (!(quantum & (quantum - 1)))
+ flags |= POOL_QALIGN_P2;
+ }
+
+ pool->size = size;
+ pool->quantum = quantum;
+ pool->bomb = bomb;
+ pool->flags = flags;
+
+ return pool;
+}
+
+void
+pool_destroy(alloc_pool_t p)
+{
+ struct alloc_pool *pool = (struct alloc_pool *) p;
+ struct pool_extent *cur, *next;
+
+ if (!pool)
+ return;
+
+ for (cur = pool->extents; cur; cur = next) {
+ next = cur->next;
+ if (pool->flags & POOL_PREPEND)
+ free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
+ else {
+ free(cur->start);
+ free(cur);
+ }
+ }
+
+ free(pool);
+}
+
+void *
+pool_alloc(alloc_pool_t p, size_t len, const char *bomb_msg)
+{
+ struct alloc_pool *pool = (struct alloc_pool *) p;
+ if (!pool)
+ return NULL;
+
+ if (!len)
+ len = pool->quantum;
+ else if (pool->flags & POOL_QALIGN_P2) {
+ if (len & (pool->quantum - 1))
+ len += pool->quantum - (len & (pool->quantum - 1));
+ } else if (!(pool->flags & POOL_NO_QALIGN)) {
+ if (len % pool->quantum)
+ len += pool->quantum - len % pool->quantum;
+ }
+
+ if (len > pool->size)
+ goto bomb_out;
+
+ if (!pool->extents || len > pool->extents->free) {
+ void *start;
+ size_t asize;
+ struct pool_extent *ext;
+
+ asize = pool->size;
+ if (pool->flags & POOL_PREPEND)
+ asize += sizeof (struct pool_extent);
+
+ if (!(start = new_array(char, asize)))
+ goto bomb_out;
+
+ if (pool->flags & POOL_CLEAR)
+ memset(start, 0, asize);
+
+ if (pool->flags & POOL_PREPEND) {
+ ext = start;
+ start = PTR_ADD(start, sizeof (struct pool_extent));
+ } else if (!(ext = new(struct pool_extent)))
+ goto bomb_out;
+ ext->start = start;
+ ext->free = pool->size;
+ ext->bound = 0;
+ ext->next = pool->extents;
+ pool->extents = ext;
+
+ pool->e_created++;
+ }
+
+ pool->n_allocated++;
+ pool->b_allocated += len;
+
+ pool->extents->free -= len;
+
+ return PTR_ADD(pool->extents->start, pool->extents->free);
+
+ bomb_out:
+ if (pool->bomb)
+ (*pool->bomb)(bomb_msg, __FILE__, __LINE__);
+ return NULL;
+}
+
+/* This function allows you to declare memory in the pool that you are done
+ * using. If you free all the memory in a pool's extent, that extent will
+ * be freed. */
+void
+pool_free(alloc_pool_t p, size_t len, void *addr)
+{
+ struct alloc_pool *pool = (struct alloc_pool *)p;
+ struct pool_extent *cur, *prev;
+
+ if (!pool)
+ return;
+
+ if (!addr) {
+ /* A NULL addr starts a fresh extent for new allocations. */
+ if ((cur = pool->extents) != NULL && cur->free != pool->size) {
+ cur->bound += cur->free;
+ cur->free = 0;
+ }
+ return;
+ }
+
+ if (!len)
+ len = pool->quantum;
+ else if (pool->flags & POOL_QALIGN_P2) {
+ if (len & (pool->quantum - 1))
+ len += pool->quantum - (len & (pool->quantum - 1));
+ } else if (!(pool->flags & POOL_NO_QALIGN)) {
+ if (len % pool->quantum)
+ len += pool->quantum - len % pool->quantum;
+ }
+
+ pool->n_freed++;
+ pool->b_freed += len;
+
+ for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) {
+ if (addr >= cur->start
+ && addr < PTR_ADD(cur->start, pool->size))
+ break;
+ }
+ if (!cur)
+ return;
+
+ if (!prev) {
+ /* The "live" extent is kept ready for more allocations. */
+ if (cur->free + cur->bound + len >= pool->size) {
+ if (pool->flags & POOL_CLEAR) {
+ memset(PTR_ADD(cur->start, cur->free), 0,
+ pool->size - cur->free);
+ }
+ cur->free = pool->size;
+ cur->bound = 0;
+ } else if (addr == PTR_ADD(cur->start, cur->free)) {
+ if (pool->flags & POOL_CLEAR)
+ memset(addr, 0, len);
+ cur->free += len;
+ } else
+ cur->bound += len;
+ } else {
+ cur->bound += len;
+
+ if (cur->free + cur->bound >= pool->size) {
+ prev->next = cur->next;
+ if (pool->flags & POOL_PREPEND)
+ free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
+ else {
+ free(cur->start);
+ free(cur);
+ }
+ pool->e_freed++;
+ } else if (prev != pool->extents) {
+ /* Move the extent to be the first non-live extent. */
+ prev->next = cur->next;
+ cur->next = pool->extents->next;
+ pool->extents->next = cur;
+ }
+ }
+}
+
+/* This allows you to declare that the given address marks the edge of some
+ * pool memory that is no longer needed. Any extents that hold only data
+ * older than the boundary address are freed. NOTE: You MUST NOT USE BOTH
+ * pool_free() and pool_free_old() on the same pool!! */
+void
+pool_free_old(alloc_pool_t p, void *addr)
+{
+ struct alloc_pool *pool = (struct alloc_pool *)p;
+ struct pool_extent *cur, *prev, *next;
+
+ if (!pool || !addr)
+ return;
+
+ for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) {
+ if (addr >= cur->start
+ && addr < PTR_ADD(cur->start, pool->size))
+ break;
+ }
+ if (!cur)
+ return;
+
+ if (addr == PTR_ADD(cur->start, cur->free)) {
+ if (prev) {
+ prev->next = NULL;
+ next = cur;
+ } else {
+ /* The most recent live extent can just be reset. */
+ if (pool->flags & POOL_CLEAR)
+ memset(addr, 0, pool->size - cur->free);
+ cur->free = pool->size;
+ cur->bound = 0;
+ next = cur->next;
+ cur->next = NULL;
+ }
+ } else {
+ next = cur->next;
+ cur->next = NULL;
+ }
+
+ while ((cur = next) != NULL) {
+ next = cur->next;
+ if (pool->flags & POOL_PREPEND)
+ free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
+ else {
+ free(cur->start);
+ free(cur);
+ }
+ pool->e_freed++;
+ }
+}
+
+/* If the current extent doesn't have "len" free space in it, mark it as full
+ * so that the next alloc will start a new extent. If len is (size_t)-1, this
+ * bump will always occur. The function returns a boundary address that can
+ * be used with pool_free_old(), or a NULL if no memory is allocated. */
+void *
+pool_boundary(alloc_pool_t p, size_t len)
+{
+ struct alloc_pool *pool = (struct alloc_pool *)p;
+ struct pool_extent *cur;
+
+ if (!pool || !pool->extents)
+ return NULL;
+
+ cur = pool->extents;
+
+ if (cur->free < len) {
+ cur->bound += cur->free;
+ cur->free = 0;
+ }
+
+ return PTR_ADD(cur->start, cur->free);
+}
+
+#define FDPRINT(label, value) \
+ do { \
+ int len = snprintf(buf, sizeof buf, label, value); \
+ if (write(fd, buf, len) != len) \
+ ret = -1; \
+ } while (0)
+
+#define FDEXTSTAT(ext) \
+ do { \
+ int len = snprintf(buf, sizeof buf, " %12ld %5ld\n", \
+ (long)ext->free, (long)ext->bound); \
+ if (write(fd, buf, len) != len) \
+ ret = -1; \
+ } while (0)
+
+int
+pool_stats(alloc_pool_t p, int fd, int summarize)
+{
+ struct alloc_pool *pool = (struct alloc_pool *) p;
+ struct pool_extent *cur;
+ char buf[BUFSIZ];
+ int ret = 0;
+
+ if (!pool)
+ return ret;
+
+ FDPRINT(" Extent size: %12ld\n", (long) pool->size);
+ FDPRINT(" Alloc quantum: %12ld\n", (long) pool->quantum);
+ FDPRINT(" Extents created: %12ld\n", pool->e_created);
+ FDPRINT(" Extents freed: %12ld\n", pool->e_freed);
+ FDPRINT(" Alloc count: %12.0f\n", (double) pool->n_allocated);
+ FDPRINT(" Free Count: %12.0f\n", (double) pool->n_freed);
+ FDPRINT(" Bytes allocated: %12.0f\n", (double) pool->b_allocated);
+ FDPRINT(" Bytes freed: %12.0f\n", (double) pool->b_freed);
+
+ if (summarize)
+ return ret;
+
+ if (!pool->extents)
+ return ret;
+
+ if (write(fd, "\n", 1) != 1)
+ ret = -1;
+
+ for (cur = pool->extents; cur; cur = cur->next)
+ FDEXTSTAT(cur);
+
+ return ret;
+}
diff --git a/lib/pool_alloc.h b/lib/pool_alloc.h
new file mode 100644
index 0000000..2805921
--- /dev/null
+++ b/lib/pool_alloc.h
@@ -0,0 +1,21 @@
+#include <stddef.h>
+
+#define POOL_CLEAR (1<<0) /* zero fill allocations */
+#define POOL_NO_QALIGN (1<<1) /* don't align data to quanta */
+#define POOL_INTERN (1<<2) /* Allocate extent structures */
+#define POOL_PREPEND (1<<3) /* or prepend to extent data */
+
+typedef void *alloc_pool_t;
+
+alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags);
+void pool_destroy(alloc_pool_t pool);
+void *pool_alloc(alloc_pool_t pool, size_t size, const char *bomb_msg);
+void pool_free(alloc_pool_t pool, size_t size, void *addr);
+void pool_free_old(alloc_pool_t pool, void *addr);
+void *pool_boundary(alloc_pool_t pool, size_t size);
+
+#define pool_talloc(pool, type, count, bomb_msg) \
+ ((type *)pool_alloc(pool, sizeof(type) * count, bomb_msg))
+
+#define pool_tfree(pool, type, count, addr) \
+ (pool_free(pool, sizeof(type) * count, addr))
diff --git a/lib/snprintf.c b/lib/snprintf.c
new file mode 100644
index 0000000..15c0529
--- /dev/null
+++ b/lib/snprintf.c
@@ -0,0 +1,1512 @@
+/*
+ * NOTE: If you change this file, please merge it into rsync, samba, etc.
+ */
+
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell@astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nasty effects.
+ *
+ * More Recently:
+ * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formatted the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -I.. -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * Andrew Tridgell (tridge@samba.org) Oct 1998
+ * fixed handling of %.0f
+ * added test for HAVE_LONG_DOUBLE
+ *
+ * tridge@samba.org, idra@samba.org, April 2001
+ * got rid of fcvt code (twas buggy and made testing harder)
+ * added C99 semantics
+ *
+ * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0
+ * actually print args for %g and %e
+ *
+ * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0
+ * Since includes.h isn't included here, VA_COPY has to be defined here. I don't
+ * see any include file that is guaranteed to be here, so I'm defining it
+ * locally. Fixes AIX and Solaris builds.
+ *
+ * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13
+ * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
+ * functions
+ *
+ * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4
+ * Fix usage of va_list passed as an arg. Use __va_copy before using it
+ * when it exists.
+ *
+ * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14
+ * Fix incorrect zpadlen handling in fmtfp.
+ * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
+ * few mods to make it easier to compile the tests.
+ * added the "Ollie" test to the floating point ones.
+ *
+ * Martin Pool (mbp@samba.org) April 2003
+ * Remove NO_CONFIG_H so that the test case can be built within a source
+ * tree with less trouble.
+ * Remove unnecessary SAFE_FREE() definition.
+ *
+ * Martin Pool (mbp@samba.org) May 2003
+ * Put in a prototype for dummy_snprintf() to quiet compiler warnings.
+ *
+ * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
+ * if the C library has some snprintf functions already.
+ *
+ * Darren Tucker (dtucker@zip.com.au) 2005
+ * Fix bug allowing read overruns of the source string with "%.*s"
+ * Usually harmless unless the read runs outside the process' allocation
+ * (eg if your malloc does guard pages) in which case it will segfault.
+ * From OpenSSH. Also added test for same.
+ *
+ * Simo Sorce (idra@samba.org) Jan 2006
+ *
+ * Add support for position independent parameters
+ * fix fmtstr now it conforms to sprintf wrt min.max
+ *
+ **************************************************************/
+
+#include "config.h"
+
+#ifdef TEST_SNPRINTF /* need math library headers for testing */
+
+/* In test mode, we pretend that this system doesn't have any snprintf
+ * functions, regardless of what config.h says. */
+# undef HAVE_SNPRINTF
+# undef HAVE_VSNPRINTF
+# undef HAVE_C99_VSNPRINTF
+# undef HAVE_ASPRINTF
+# undef HAVE_VASPRINTF
+# include <math.h>
+#endif /* TEST_SNPRINTF */
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#include <sys/types.h>
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
+/* only include stdio.h if we are not re-defining snprintf or vsnprintf */
+#include <stdio.h>
+ /* make the compiler happy with an empty file */
+ void dummy_snprintf(void);
+ void dummy_snprintf(void) {}
+#endif /* HAVE_SNPRINTF, etc */
+
+#ifdef STDC_HEADERS
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif
+
+#if !defined HAVE_LONG_LONG && SIZEOF_LONG_LONG
+#define HAVE_LONG_LONG 1
+#endif
+#ifdef HAVE_LONG_LONG
+#define LLONG long long
+#else
+#define LLONG long
+#endif
+
+#ifndef VA_COPY
+#if defined HAVE_VA_COPY || defined va_copy
+#define VA_COPY(dest, src) va_copy(dest, src)
+#else
+#ifdef HAVE___VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#else
+#define VA_COPY(dest, src) (dest) = (src)
+#endif
+#endif
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge) */
+#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_CONV 6
+#define DP_S_DONE 7
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_CHAR 1
+#define DP_C_SHORT 2
+#define DP_C_LONG 3
+#define DP_C_LDOUBLE 4
+#define DP_C_LLONG 5
+#define DP_C_SIZET 6
+
+/* Chunk types */
+#define CNK_FMT_STR 0
+#define CNK_INT 1
+#define CNK_OCTAL 2
+#define CNK_UINT 3
+#define CNK_HEX 4
+#define CNK_FLOAT 5
+#define CNK_CHAR 6
+#define CNK_STRING 7
+#define CNK_PTR 8
+#define CNK_NUM 9
+#define CNK_PRCNT 10
+
+#define char_to_int(p) ((p)- '0')
+#ifndef MAX
+#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
+#endif
+
+struct pr_chunk {
+ int type; /* chunk type */
+ int num; /* parameter number */
+ int min;
+ int max;
+ int flags;
+ int cflags;
+ int start;
+ int len;
+ LLONG value;
+ LDOUBLE fvalue;
+ char *strvalue;
+ void *pnum;
+ struct pr_chunk *min_star;
+ struct pr_chunk *max_star;
+ struct pr_chunk *next;
+};
+
+struct pr_chunk_x {
+ struct pr_chunk **chunks;
+ int num;
+};
+
+static int dopr(char *buffer, size_t maxlen, const char *format,
+ va_list args_in);
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max);
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ LLONG value, int base, int min, int max, int flags);
+static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags);
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
+static struct pr_chunk *new_chunk(void);
+static int add_cnk_list_entry(struct pr_chunk_x **list,
+ int max_num, struct pr_chunk *chunk);
+
+static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
+{
+ char ch;
+ int state;
+ int pflag;
+ int pnum;
+ int pfirst;
+ size_t currlen;
+ va_list args;
+ const char *base;
+ struct pr_chunk *chunks = NULL;
+ struct pr_chunk *cnk = NULL;
+ struct pr_chunk_x *clist = NULL;
+ int max_pos;
+ int ret = -1;
+
+ VA_COPY(args, args_in);
+
+ state = DP_S_DEFAULT;
+ pfirst = 1;
+ pflag = 0;
+ pnum = 0;
+
+ max_pos = 0;
+ base = format;
+ ch = *format++;
+
+ /* retrieve the string structure as chunks */
+ while (state != DP_S_DONE) {
+ if (ch == '\0')
+ state = DP_S_DONE;
+
+ switch(state) {
+ case DP_S_DEFAULT:
+
+ if (cnk) {
+ cnk->next = new_chunk();
+ cnk = cnk->next;
+ } else {
+ cnk = new_chunk();
+ }
+ if (!cnk) goto done;
+ if (!chunks) chunks = cnk;
+
+ if (ch == '%') {
+ state = DP_S_FLAGS;
+ ch = *format++;
+ } else {
+ cnk->type = CNK_FMT_STR;
+ cnk->start = format - base -1;
+ while ((ch != '\0') && (ch != '%')) ch = *format++;
+ cnk->len = format - base - cnk->start -1;
+ }
+ break;
+ case DP_S_FLAGS:
+ switch (ch) {
+ case '-':
+ cnk->flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ cnk->flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ cnk->flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ cnk->flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ cnk->flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ case 'I':
+ /* internationalization not supported yet */
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if (isdigit((unsigned char)ch)) {
+ cnk->min = 10 * cnk->min + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '$') {
+ if (!pfirst && !pflag) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ if (pfirst) {
+ pfirst = 0;
+ pflag = 1;
+ }
+ if (cnk->min == 0) /* what ?? */
+ goto done;
+ cnk->num = cnk->min;
+ cnk->min = 0;
+ ch = *format++;
+ } else if (ch == '*') {
+ if (pfirst) pfirst = 0;
+ cnk->min_star = new_chunk();
+ if (!cnk->min_star) /* out of memory :-( */
+ goto done;
+ cnk->min_star->type = CNK_INT;
+ if (pflag) {
+ int num;
+ ch = *format++;
+ if (!isdigit((unsigned char)ch)) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
+ num = 10 * num + char_to_int(ch);
+ }
+ cnk->min_star->num = num;
+ if (ch != '$') /* what ?? */
+ goto done;
+ } else {
+ cnk->min_star->num = ++pnum;
+ }
+ max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star);
+ if (max_pos == 0) /* out of memory :-( */
+ goto done;
+ ch = *format++;
+ state = DP_S_DOT;
+ } else {
+ if (pfirst) pfirst = 0;
+ state = DP_S_DOT;
+ }
+ break;
+ case DP_S_DOT:
+ if (ch == '.') {
+ state = DP_S_MAX;
+ ch = *format++;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MAX:
+ if (isdigit((unsigned char)ch)) {
+ if (cnk->max < 0)
+ cnk->max = 0;
+ cnk->max = 10 * cnk->max + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '$') {
+ if (!pfirst && !pflag) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ if (cnk->max <= 0) /* what ?? */
+ goto done;
+ cnk->num = cnk->max;
+ cnk->max = -1;
+ ch = *format++;
+ } else if (ch == '*') {
+ cnk->max_star = new_chunk();
+ if (!cnk->max_star) /* out of memory :-( */
+ goto done;
+ cnk->max_star->type = CNK_INT;
+ if (pflag) {
+ int num;
+ ch = *format++;
+ if (!isdigit((unsigned char)ch)) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
+ num = 10 * num + char_to_int(ch);
+ }
+ cnk->max_star->num = num;
+ if (ch != '$') /* what ?? */
+ goto done;
+ } else {
+ cnk->max_star->num = ++pnum;
+ }
+ max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star);
+ if (max_pos == 0) /* out of memory :-( */
+ goto done;
+
+ ch = *format++;
+ state = DP_S_MOD;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MOD:
+ switch (ch) {
+ case 'h':
+ cnk->cflags = DP_C_SHORT;
+ ch = *format++;
+ if (ch == 'h') {
+ cnk->cflags = DP_C_CHAR;
+ ch = *format++;
+ }
+ break;
+ case 'l':
+ cnk->cflags = DP_C_LONG;
+ ch = *format++;
+ if (ch == 'l') { /* It's a long long */
+ cnk->cflags = DP_C_LLONG;
+ ch = *format++;
+ }
+ break;
+ case 'L':
+ cnk->cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ case 'z':
+ cnk->cflags = DP_C_SIZET;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ if (cnk->num == 0) cnk->num = ++pnum;
+ max_pos = add_cnk_list_entry(&clist, max_pos, cnk);
+ if (max_pos == 0) /* out of memory :-( */
+ goto done;
+
+ switch (ch) {
+ case 'd':
+ case 'i':
+ cnk->type = CNK_INT;
+ break;
+ case 'o':
+ cnk->type = CNK_OCTAL;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'u':
+ cnk->type = CNK_UINT;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'X':
+ cnk->flags |= DP_F_UP;
+ case 'x':
+ cnk->type = CNK_HEX;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'A':
+ /* hex float not supported yet */
+ case 'E':
+ case 'G':
+ case 'F':
+ cnk->flags |= DP_F_UP;
+ case 'a':
+ /* hex float not supported yet */
+ case 'e':
+ case 'f':
+ case 'g':
+ cnk->type = CNK_FLOAT;
+ break;
+ case 'c':
+ cnk->type = CNK_CHAR;
+ break;
+ case 's':
+ cnk->type = CNK_STRING;
+ break;
+ case 'p':
+ cnk->type = CNK_PTR;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'n':
+ cnk->type = CNK_NUM;
+ break;
+ case '%':
+ cnk->type = CNK_PRCNT;
+ break;
+ default:
+ /* Unknown, bail out*/
+ goto done;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+
+ /* retrieve the format arguments */
+ for (pnum = 0; pnum < max_pos; pnum++) {
+ int i;
+
+ if (clist[pnum].num == 0) {
+ /* ignoring a parameter should not be permitted
+ * all parameters must be matched at least once
+ * BUT seem some system ignore this rule ...
+ * at least my glibc based system does --SSS
+ */
+#ifdef DEBUG_SNPRINTF
+ printf("parameter at position %d not used\n", pnum+1);
+#endif
+ /* eat the parameter */
+ va_arg (args, int);
+ continue;
+ }
+ for (i = 1; i < clist[pnum].num; i++) {
+ if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) {
+ /* nooo noo no!
+ * all the references to a parameter
+ * must be of the same type
+ */
+ goto done;
+ }
+ }
+ cnk = clist[pnum].chunks[0];
+ switch (cnk->type) {
+ case CNK_INT:
+ if (cnk->cflags == DP_C_SHORT)
+ cnk->value = va_arg (args, int);
+ else if (cnk->cflags == DP_C_LONG)
+ cnk->value = va_arg (args, long int);
+ else if (cnk->cflags == DP_C_LLONG)
+ cnk->value = va_arg (args, LLONG);
+ else if (cnk->cflags == DP_C_SIZET)
+ cnk->value = va_arg (args, ssize_t);
+ else
+ cnk->value = va_arg (args, int);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->value = cnk->value;
+ }
+ break;
+
+ case CNK_OCTAL:
+ case CNK_UINT:
+ case CNK_HEX:
+ if (cnk->cflags == DP_C_SHORT)
+ cnk->value = va_arg (args, unsigned int);
+ else if (cnk->cflags == DP_C_LONG)
+ cnk->value = (unsigned long int)va_arg (args, unsigned long int);
+ else if (cnk->cflags == DP_C_LLONG)
+ cnk->value = (LLONG)va_arg (args, unsigned LLONG);
+ else if (cnk->cflags == DP_C_SIZET)
+ cnk->value = (size_t)va_arg (args, size_t);
+ else
+ cnk->value = (unsigned int)va_arg (args, unsigned int);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->value = cnk->value;
+ }
+ break;
+
+ case CNK_FLOAT:
+ if (cnk->cflags == DP_C_LDOUBLE)
+ cnk->fvalue = va_arg (args, LDOUBLE);
+ else
+ cnk->fvalue = va_arg (args, double);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->fvalue = cnk->fvalue;
+ }
+ break;
+
+ case CNK_CHAR:
+ cnk->value = va_arg (args, int);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->value = cnk->value;
+ }
+ break;
+
+ case CNK_STRING:
+ cnk->strvalue = va_arg (args, char *);
+ if (!cnk->strvalue) cnk->strvalue = "(NULL)";
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->strvalue = cnk->strvalue;
+ }
+ break;
+
+ case CNK_PTR:
+ cnk->strvalue = va_arg (args, void *);
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->strvalue = cnk->strvalue;
+ }
+ break;
+
+ case CNK_NUM:
+ if (cnk->cflags == DP_C_CHAR)
+ cnk->pnum = va_arg (args, char *);
+ else if (cnk->cflags == DP_C_SHORT)
+ cnk->pnum = va_arg (args, short int *);
+ else if (cnk->cflags == DP_C_LONG)
+ cnk->pnum = va_arg (args, long int *);
+ else if (cnk->cflags == DP_C_LLONG)
+ cnk->pnum = va_arg (args, LLONG *);
+ else if (cnk->cflags == DP_C_SIZET)
+ cnk->pnum = va_arg (args, ssize_t *);
+ else
+ cnk->pnum = va_arg (args, int *);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->pnum = cnk->pnum;
+ }
+ break;
+
+ case CNK_PRCNT:
+ break;
+
+ default:
+ /* what ?? */
+ goto done;
+ }
+ }
+ /* print out the actual string from chunks */
+ currlen = 0;
+ cnk = chunks;
+ while (cnk) {
+ int len, min, max;
+
+ if (cnk->min_star) min = cnk->min_star->value;
+ else min = cnk->min;
+ if (cnk->max_star) max = cnk->max_star->value;
+ else max = cnk->max;
+
+ switch (cnk->type) {
+
+ case CNK_FMT_STR:
+ if (maxlen != 0 && maxlen > currlen) {
+ if (maxlen > (currlen + cnk->len)) len = cnk->len;
+ else len = maxlen - currlen;
+
+ memcpy(&(buffer[currlen]), &(base[cnk->start]), len);
+ }
+ currlen += cnk->len;
+
+ break;
+
+ case CNK_INT:
+ case CNK_UINT:
+ fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags);
+ break;
+
+ case CNK_OCTAL:
+ fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags);
+ break;
+
+ case CNK_HEX:
+ fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags);
+ break;
+
+ case CNK_FLOAT:
+ fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags);
+ break;
+
+ case CNK_CHAR:
+ dopr_outch (buffer, &currlen, maxlen, cnk->value);
+ break;
+
+ case CNK_STRING:
+ if (max == -1) {
+ max = strlen(cnk->strvalue);
+ }
+ fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max);
+ break;
+
+ case CNK_PTR:
+ fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags);
+ break;
+
+ case CNK_NUM:
+ if (cnk->cflags == DP_C_CHAR)
+ *((char *)(cnk->pnum)) = (char)currlen;
+ else if (cnk->cflags == DP_C_SHORT)
+ *((short int *)(cnk->pnum)) = (short int)currlen;
+ else if (cnk->cflags == DP_C_LONG)
+ *((long int *)(cnk->pnum)) = (long int)currlen;
+ else if (cnk->cflags == DP_C_LLONG)
+ *((LLONG *)(cnk->pnum)) = (LLONG)currlen;
+ else if (cnk->cflags == DP_C_SIZET)
+ *((ssize_t *)(cnk->pnum)) = (ssize_t)currlen;
+ else
+ *((int *)(cnk->pnum)) = (int)currlen;
+ break;
+
+ case CNK_PRCNT:
+ dopr_outch (buffer, &currlen, maxlen, '%');
+ break;
+
+ default:
+ /* what ?? */
+ goto done;
+ }
+ cnk = cnk->next;
+ }
+ if (maxlen != 0) {
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else if (maxlen > 0)
+ buffer[maxlen - 1] = '\0';
+ }
+ ret = currlen;
+
+done:
+ va_end(args);
+
+ while (chunks) {
+ cnk = chunks->next;
+ free(chunks);
+ chunks = cnk;
+ }
+ if (clist) {
+ for (pnum = 0; pnum < max_pos; pnum++) {
+ if (clist[pnum].chunks) free(clist[pnum].chunks);
+ }
+ free(clist);
+ }
+ return ret;
+}
+
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
+#endif
+ if (value == 0) {
+ value = "<NULL>";
+ }
+
+ for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ while (*value && (cnt < max)) {
+ dopr_outch (buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while (padlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ LLONG value, int base, int min, int max, int flags)
+{
+ int signvalue = 0;
+ unsigned LLONG uvalue;
+ char convert[20];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if(!(flags & DP_F_UNSIGNED)) {
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ } else {
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ } while(uvalue && (place < 20));
+ if (place == 20) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO) {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place);
+#endif
+
+ /* Spaces */
+ while (spadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0) {
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch (buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static LDOUBLE abs_val(LDOUBLE value)
+{
+ LDOUBLE result = value;
+
+ if (value < 0)
+ result = -value;
+
+ return result;
+}
+
+static LDOUBLE POW10(int exp)
+{
+ LDOUBLE result = 1;
+
+ while (exp) {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static LLONG ROUND(LDOUBLE value)
+{
+ LLONG intpart;
+
+ intpart = (LLONG)value;
+ value = value - intpart;
+ if (value >= 0.5) intpart++;
+
+ return intpart;
+}
+
+/* a replacement for modf that doesn't need the math library. Should
+ be portable, but slow */
+static double my_modf(double x0, double *iptr)
+{
+ int i;
+ LLONG l=0;
+ double x = x0;
+ double f = 1.0;
+
+ for (i=0;i<100;i++) {
+ l = (long)x;
+ if (l <= (x+1) && l >= (x-1)) break;
+ x *= 0.1;
+ f *= 10.0;
+ }
+
+ if (i == 100) {
+ /* yikes! the number is beyond what we can handle. What do we do? */
+ (*iptr) = 0;
+ return 0;
+ }
+
+ if (i != 0) {
+ double i2;
+ double ret;
+
+ ret = my_modf(x0-l*f, &i2);
+ (*iptr) = l*f + i2;
+ return ret;
+ }
+
+ (*iptr) = l;
+ return x - (*iptr);
+}
+
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ double ufvalue;
+ char iconvert[311];
+ char fconvert[311];
+ int iplace = 0;
+ int fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int caps = 0;
+ int idx;
+ double intpart;
+ double fracpart;
+ double temp;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0) {
+ signvalue = '-';
+ } else {
+ if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
+ signvalue = '+';
+ } else {
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+#if 0
+ if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
+#endif
+
+ /*
+ * Sorry, we only support 9 digits past the decimal because of our
+ * conversion method
+ */
+ if (max > 9)
+ max = 9;
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+
+ temp = ufvalue;
+ my_modf(temp, &intpart);
+
+ fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
+
+ if (fracpart >= POW10(max)) {
+ intpart++;
+ fracpart -= POW10(max);
+ }
+
+
+ /* Convert integer part */
+ do {
+ temp = intpart*0.1;
+ my_modf(temp, &intpart);
+ idx = (int) ((temp -intpart +0.05)* 10.0);
+ /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
+ /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
+ iconvert[iplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while (intpart && (iplace < 311));
+ if (iplace == 311) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ if (fracpart)
+ {
+ do {
+ temp = fracpart*0.1;
+ my_modf(temp, &fracpart);
+ idx = (int) ((temp -fracpart +0.05)* 10.0);
+ /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
+ /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
+ fconvert[fplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while(fracpart && (fplace < 311));
+ if (fplace == 311) fplace--;
+ }
+ fconvert[fplace] = 0;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
+ zpadlen = max - fplace;
+ if (zpadlen < 0) zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0)) {
+ if (signvalue) {
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
+#endif
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ if (max > 0) {
+ dopr_outch (buffer, currlen, maxlen, '.');
+
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (fplace > 0)
+ dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+ }
+
+ while (padlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen < maxlen) {
+ buffer[(*currlen)] = c;
+ }
+ (*currlen)++;
+}
+
+static struct pr_chunk *new_chunk(void) {
+ struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chunk));
+
+ if (!new_c)
+ return NULL;
+
+ new_c->type = 0;
+ new_c->num = 0;
+ new_c->min = 0;
+ new_c->min_star = NULL;
+ new_c->max = -1;
+ new_c->max_star = NULL;
+ new_c->flags = 0;
+ new_c->cflags = 0;
+ new_c->start = 0;
+ new_c->len = 0;
+ new_c->value = 0;
+ new_c->fvalue = 0;
+ new_c->strvalue = NULL;
+ new_c->pnum = NULL;
+ new_c->next = NULL;
+
+ return new_c;
+}
+
+static int add_cnk_list_entry(struct pr_chunk_x **list,
+ int max_num, struct pr_chunk *chunk) {
+ struct pr_chunk_x *l;
+ struct pr_chunk **c;
+ int max;
+ int cnum;
+ int i, pos;
+
+ if (chunk->num > max_num) {
+ max = chunk->num;
+
+ if (*list == NULL) {
+ l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x) * max);
+ pos = 0;
+ } else {
+ l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr_chunk_x) * max);
+ pos = max_num;
+ }
+ if (l == NULL) {
+ for (i = 0; i < max; i++) {
+ if ((*list)[i].chunks) free((*list)[i].chunks);
+ }
+ return 0;
+ }
+ for (i = pos; i < max; i++) {
+ l[i].chunks = NULL;
+ l[i].num = 0;
+ }
+ } else {
+ l = *list;
+ max = max_num;
+ }
+
+ i = chunk->num - 1;
+ cnum = l[i].num + 1;
+ if (l[i].chunks == NULL) {
+ c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum);
+ } else {
+ c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_chunk *) * cnum);
+ }
+ if (c == NULL) {
+ for (i = 0; i < max; i++) {
+ if (l[i].chunks) free(l[i].chunks);
+ }
+ return 0;
+ }
+ c[l[i].num] = chunk;
+ l[i].chunks = c;
+ l[i].num = cnum;
+
+ *list = l;
+ return max;
+}
+
+ int rsync_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ return dopr(str, count, fmt, args);
+}
+#define vsnprintf rsync_vsnprintf
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge)
+ *
+ * The logic for these two is that we need our own definition if the
+ * OS *either* has no definition of *sprintf, or if it does have one
+ * that doesn't work properly according to the autoconf test.
+ */
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+int rsync_snprintf(char *str,size_t count,const char *fmt,...)
+{
+ size_t ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(str, count, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+#define snprintf rsync_snprintf
+#endif
+
+#ifndef HAVE_VASPRINTF
+ int vasprintf(char **ptr, const char *format, va_list ap)
+{
+ int ret;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+ ret = vsnprintf(NULL, 0, format, ap2);
+ va_end(ap2);
+ if (ret < 0) return ret;
+
+ (*ptr) = (char *)malloc(ret+1);
+ if (!*ptr) return -1;
+
+ VA_COPY(ap2, ap);
+ ret = vsnprintf(*ptr, ret+1, format, ap2);
+ va_end(ap2);
+
+ return ret;
+}
+#endif
+
+
+#ifndef HAVE_ASPRINTF
+ int asprintf(char **ptr, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ *ptr = NULL;
+ va_start(ap, format);
+ ret = vasprintf(ptr, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+#endif
+
+#ifdef TEST_SNPRINTF
+
+ int sprintf(char *str,const char *fmt,...);
+ int printf(const char *fmt,...);
+
+ int main (void)
+{
+ char buf1[1024];
+ char buf2[1024];
+ char *buf3;
+ char *fp_fmt[] = {
+ "%1.1f",
+ "%-1.5f",
+ "%1.5f",
+ "%123.9f",
+ "%10.5f",
+ "% 10.5f",
+ "%+22.9f",
+ "%+4.9f",
+ "%01.3f",
+ "%4f",
+ "%3.1f",
+ "%3.2f",
+ "%.0f",
+ "%f",
+ "%-8.8f",
+ "%-9.9f",
+ NULL
+ };
+ double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996,
+ 0.9996, 1.996, 4.136, 5.030201, 0.00205,
+ /* END LIST */ 0};
+ char *int_fmt[] = {
+ "%-1.5d",
+ "%1.5d",
+ "%123.9d",
+ "%5.5d",
+ "%10.5d",
+ "% 10.5d",
+ "%+22.33d",
+ "%01.3d",
+ "%4d",
+ "%d",
+ NULL
+ };
+ long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0};
+ char *str_fmt[] = {
+ "%10.5s",
+ "%-10.5s",
+ "%5.10s",
+ "%-5.10s",
+ "%10.1s",
+ "%0.10s",
+ "%10.0s",
+ "%1.10s",
+ "%s",
+ "%.1s",
+ "%.10s",
+ "%10s",
+ NULL
+ };
+ char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
+#ifdef HAVE_LONG_LONG
+ char *ll_fmt[] = {
+ "%llu",
+ NULL
+ };
+ LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0};
+#endif
+ int x, y;
+ int fail = 0;
+ int num = 0;
+ int l1, l2;
+ char *ss_fmt[] = {
+ "%zd",
+ "%zu",
+ NULL
+ };
+ size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0};
+
+ printf ("Testing snprintf format codes against system sprintf...\n");
+
+ for (x = 0; fp_fmt[x] ; x++) {
+ for (y = 0; fp_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
+ l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ fp_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; int_fmt[x] ; x++) {
+ for (y = 0; int_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
+ l2 = sprintf (buf2, int_fmt[x], int_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ int_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; str_fmt[x] ; x++) {
+ for (y = 0; str_vals[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
+ l2 = sprintf (buf2, str_fmt[x], str_vals[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ str_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+#ifdef HAVE_LONG_LONG
+ for (x = 0; ll_fmt[x] ; x++) {
+ for (y = 0; ll_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
+ l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ ll_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+#endif
+
+#define BUFSZ 2048
+
+ buf1[0] = buf2[0] = '\0';
+ if ((buf3 = malloc(BUFSZ)) == NULL) {
+ fail++;
+ } else {
+ num++;
+ memset(buf3, 'a', BUFSZ);
+ snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3);
+ buf1[1023] = '\0';
+ if (strcmp(buf1, "a") != 0) {
+ printf("length limit buf1 '%s' expected 'a'\n", buf1);
+ fail++;
+ }
+ }
+
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
+ l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
+ fail++;
+ }
+
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
+ l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
+ fail++;
+ }
+
+ for (x = 0; ss_fmt[x] ; x++) {
+ for (y = 0; ss_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y]);
+ l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ ss_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+#if 0
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890);
+ l2 = sprintf(buf2, "%lld", (LLONG)1234567890);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%lld", l1, buf1, l2, buf2);
+ fail++;
+ }
+
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123);
+ l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%Lf", l1, buf1, l2, buf2);
+ fail++;
+ }
+#endif
+ printf ("%d tests failed out of %d.\n", fail, num);
+
+ printf("seeing how many digits we support\n");
+ {
+ double v0 = 0.12345678901234567890123456789012345678901;
+ for (x=0; x<100; x++) {
+ double p = pow(10, x);
+ double r = v0*p;
+ snprintf(buf1, sizeof(buf1), "%1.1f", r);
+ sprintf(buf2, "%1.1f", r);
+ if (strcmp(buf1, buf2)) {
+ printf("we seem to support %d digits\n", x-1);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /* TEST_SNPRINTF */
diff --git a/lib/sysacls.c b/lib/sysacls.c
new file mode 100644
index 0000000..a5abe40
--- /dev/null
+++ b/lib/sysacls.c
@@ -0,0 +1,2802 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Based on the Samba ACL support code.
+ * Copyright (C) Jeremy Allison 2000.
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * The permission functions have been changed to get/set all bits via
+ * one call. Some functions that rsync doesn't need were also removed.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "sysacls.h"
+
+#ifdef SUPPORT_ACLS
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#define DEBUG(x, y)
+
+void SAFE_FREE(void *mem)
+{
+ if (mem)
+ free(mem);
+}
+
+/*
+ This file wraps all differing system ACL interfaces into a consistent
+ one based on the POSIX interface. It also returns the correct errors
+ for older UNIX systems that don't support ACLs.
+
+ The interfaces that each ACL implementation must support are as follows :
+
+ int sys_acl_get_entry(SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+ int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+ int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+ SMB_ACL_T sys_acl_get_fd(int fd)
+ SMB_ACL_T sys_acl_init(int count)
+ int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+ int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+ int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+ int sys_acl_valid(SMB_ACL_T theacl)
+ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+ int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+ int sys_acl_delete_def_file(const char *path)
+ int sys_acl_free_acl(SMB_ACL_T posix_acl)
+
+*/
+
+#if defined(HAVE_POSIX_ACLS) /*--------------------------------------------*/
+
+/* Identity mapping - easy. */
+
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ return acl_get_entry(the_acl, entry_id, entry_p);
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type(entry_d, tag_type_p);
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file(path_p, type);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd);
+}
+#endif
+
+#if defined(HAVE_ACL_GET_PERM_NP)
+#define acl_get_perm(p, b) acl_get_perm_np(p, b)
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ acl_permset_t permset;
+
+ if (acl_get_tag_type(entry, tag_type_p) != 0
+ || acl_get_permset(entry, &permset) != 0)
+ return -1;
+
+ *bits_p = (acl_get_perm(permset, ACL_READ) ? 4 : 0)
+ | (acl_get_perm(permset, ACL_WRITE) ? 2 : 0)
+ | (acl_get_perm(permset, ACL_EXECUTE) ? 1 : 0);
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP) {
+ void *qual;
+ if ((qual = acl_get_qualifier(entry)) == NULL)
+ return -1;
+ *u_g_id_p = *(id_t*)qual;
+ acl_free(qual);
+ }
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ if (acl_set_tag_type(entry, tag_type) != 0)
+ return -1;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP) {
+ if (acl_set_qualifier(entry, (void*)&u_g_id) != 0)
+ return -1;
+ }
+
+ return sys_acl_set_access_bits(entry, bits);
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ acl_permset_t permset;
+ int rc;
+ if ((rc = acl_get_permset(entry, &permset)) != 0)
+ return rc;
+ acl_clear_perms(permset);
+ if (bits & 4)
+ acl_add_perm(permset, ACL_READ);
+ if (bits & 2)
+ acl_add_perm(permset, ACL_WRITE);
+ if (bits & 1)
+ acl_add_perm(permset, ACL_EXECUTE);
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid(SMB_ACL_T theacl)
+{
+ return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file(name, acltype, theacl);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, theacl);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+#elif defined(HAVE_TRU64_ACLS) /*--------------------------------------------*/
+/*
+ * The interface to DEC/Compaq Tru64 UNIX ACLs
+ * is based on Draft 13 of the POSIX spec which is
+ * slightly different from the Draft 16 interface.
+ *
+ * Also, some of the permset manipulation functions
+ * such as acl_clear_perm() and acl_add_perm() appear
+ * to be broken on Tru64 so we have to manipulate
+ * the permission bits in the permset directly.
+ */
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY && acl_first_entry(the_acl) != 0) {
+ return -1;
+ }
+
+ errno = 0;
+ if ((entry = acl_get_entry(the_acl)) != NULL) {
+ *entry_p = entry;
+ return 1;
+ }
+
+ return errno ? -1 : 0;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type(entry_d, tag_type_p);
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file((char *)path_p, type);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd, ACL_TYPE_ACCESS);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ acl_permset_t permset;
+
+ if (acl_get_tag_type(entry, tag_type_p) != 0
+ || acl_get_permset(entry, &permset) != 0)
+ return -1;
+
+ *bits_p = *permset & 7; /* Tru64 doesn't have acl_get_perm() */
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP) {
+ void *qual;
+ if ((qual = acl_get_qualifier(entry)) == NULL)
+ return -1;
+ *u_g_id_p = *(id_t*)qual;
+ acl_free_qualifier(qual, *tag_type_p);
+ }
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if ((entry = acl_create_entry(pacl)) == NULL) {
+ return -1;
+ }
+
+ *pentry = entry;
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ if (acl_set_tag_type(entry, tag_type) != 0)
+ return -1;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP) {
+ if (acl_set_qualifier(entry, (void*)&u_g_id) != 0)
+ return -1;
+ }
+
+ return sys_acl_set_access_bits(entry, bits);
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ acl_permset_t permset;
+ int rc;
+ if ((rc = acl_get_permset(entry, &permset)) != 0)
+ return rc;
+ *permset = bits & 7;
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid(SMB_ACL_T theacl)
+{
+ acl_entry_t entry;
+
+ return acl_valid(theacl, &entry);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file((char *)name, acltype, theacl);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, ACL_TYPE_ACCESS, theacl);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file((char *)name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS) /*-----------*/
+
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/*
+ * The only difference between Solaris and UnixWare / OpenUNIX is
+ * that the #defines for the ACL operations have different names
+ */
+#if defined(HAVE_UNIXWARE_ACLS)
+
+#define SETACL ACL_SET
+#define GETACL ACL_GET
+#define GETACLCNT ACL_CNT
+
+#endif
+
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * GETACL will be unless you first call GETACLCNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use GETACLCNT to find out the actual
+ * size, reallocate the ACL buffer, and then call GETACL again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * GETACLCNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * GETACLCNT and the call to GETACL.
+ */
+ while ((count = acl(path_p, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries precede any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ while ((count = facl(fd, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = facl(fd, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+
+ acl_d->count = naccess;
+
+ return acl_d;
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ *tag_type_p = entry->a_type;
+
+ *bits_p = entry->a_perm;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ *u_g_id_p = entry->a_id;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof a[0] + count * sizeof (struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->a_type = tag_type;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ entry->a_id = u_g_id;
+
+ entry->a_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry_d, uint32 bits)
+{
+ entry_d->a_perm = bits;
+ return 0;
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+ int fixmask = (acl_d->count <= 4);
+
+ if (aclsort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count);
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof acl_buf[0]);
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof acl_buf[0]);
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, SETACL, acl_count, acl_p);
+
+ SAFE_FREE(acl_buf);
+
+ return ret;
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ return facl(fd, SETACL, acl_d->count, &acl_d->acl[0]);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, SETACL, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ SAFE_FREE(acl_d);
+ return 0;
+}
+
+#elif defined(HAVE_HPUX_ACLS) /*---------------------------------------------*/
+
+#ifdef HAVE_DL_H
+#include <dl.h>
+#endif
+
+/*
+ * Based on the Solaris/SCO code - with modifications.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/* This checks if the POSIX ACL system call is defined */
+/* which basically corresponds to whether JFS 3.3 or */
+/* higher is installed. If acl() was called when it */
+/* isn't defined, it causes the process to core dump */
+/* so it is important to check this and avoid acl() */
+/* calls if it isn't there. */
+
+#ifdef __TANDEM
+inline int do_acl(const char *path_p, int cmd, int nentries, struct acl *aclbufp)
+{
+ return acl((char*)path_p, cmd, nentries, aclbufp);
+}
+#define acl(p,c,n,a) do_acl(p,c,n,a)
+#endif
+
+static BOOL hpux_acl_call_presence(void)
+{
+#ifndef __TANDEM
+ shl_t handle = NULL;
+ void *value;
+ int ret_val=0;
+ static BOOL already_checked=0;
+
+ if (already_checked)
+ return True;
+
+ ret_val = shl_findsym(&handle, "acl", TYPE_PROCEDURE, &value);
+
+ if (ret_val != 0) {
+ DEBUG(5, ("hpux_acl_call_presence: shl_findsym() returned %d, errno = %d, error %s\n",
+ ret_val, errno, strerror(errno)));
+ DEBUG(5, ("hpux_acl_call_presence: acl() system call is not present. Check if you have JFS 3.3 and above?\n"));
+ return False;
+ }
+
+ DEBUG(10, ("hpux_acl_call_presence: acl() system call is present. We have JFS 3.3 or above \n"));
+
+ already_checked = True;
+#endif
+ return True;
+}
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * ACL_GET will be unless you first call ACL_CNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use ACL_CNT to find out the actual
+ * size, reallocate the ACL buffer, and then call ACL_GET again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+#ifndef NACLENTRIES
+#define NACLENTRIES 0
+#endif
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if (hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ return NULL;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * ACL_CNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * ACL_CNT and the call to ACL_GET.
+ */
+ while ((count = acl(path_p, ACL_GET, count, &acl_d->acl[0])) < 0 && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, ACL_CNT, NACLENTRIES, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries precede any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ *tag_type_p = entry->a_type;
+
+ *bits_p = entry->a_perm;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ *u_g_id_p = entry->a_id;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof a[0] + count * sizeof (struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->a_type = tag_type;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ entry->a_id = u_g_id;
+
+ entry->a_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry_d, uint32 bits)
+{
+ entry_d->a_perm = bits;
+
+ return 0;
+}
+
+/* Structure to capture the count for each type of ACE. */
+
+struct hpux_acl_types {
+ int n_user;
+ int n_def_user;
+ int n_user_obj;
+ int n_def_user_obj;
+
+ int n_group;
+ int n_def_group;
+ int n_group_obj;
+ int n_def_group_obj;
+
+ int n_other;
+ int n_other_obj;
+ int n_def_other_obj;
+
+ int n_class_obj;
+ int n_def_class_obj;
+
+ int n_illegal_obj;
+};
+
+/* count_obj:
+ * Counts the different number of objects in a given array of ACL
+ * structures.
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL structures.
+ * aclp - Array of ACL structures.
+ * acl_type_count - Pointer to acl_types structure. Should already be
+ * allocated.
+ * Output:
+ *
+ * acl_type_count - This structure is filled up with counts of various
+ * acl types.
+ */
+
+static void hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_types *acl_type_count)
+{
+ int i;
+
+ memset(acl_type_count, 0, sizeof (struct hpux_acl_types));
+
+ for (i = 0; i < acl_count; i++) {
+ switch (aclp[i].a_type) {
+ case USER:
+ acl_type_count->n_user++;
+ break;
+ case USER_OBJ:
+ acl_type_count->n_user_obj++;
+ break;
+ case DEF_USER_OBJ:
+ acl_type_count->n_def_user_obj++;
+ break;
+ case GROUP:
+ acl_type_count->n_group++;
+ break;
+ case GROUP_OBJ:
+ acl_type_count->n_group_obj++;
+ break;
+ case DEF_GROUP_OBJ:
+ acl_type_count->n_def_group_obj++;
+ break;
+ case OTHER_OBJ:
+ acl_type_count->n_other_obj++;
+ break;
+ case DEF_OTHER_OBJ:
+ acl_type_count->n_def_other_obj++;
+ break;
+ case CLASS_OBJ:
+ acl_type_count->n_class_obj++;
+ break;
+ case DEF_CLASS_OBJ:
+ acl_type_count->n_def_class_obj++;
+ break;
+ case DEF_USER:
+ acl_type_count->n_def_user++;
+ break;
+ case DEF_GROUP:
+ acl_type_count->n_def_group++;
+ break;
+ default:
+ acl_type_count->n_illegal_obj++;
+ break;
+ }
+ }
+}
+
+/* swap_acl_entries: Swaps two ACL entries.
+ *
+ * Inputs: aclp0, aclp1 - ACL entries to be swapped.
+ */
+
+static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1)
+{
+ struct acl temp_acl;
+
+ temp_acl.a_type = aclp0->a_type;
+ temp_acl.a_id = aclp0->a_id;
+ temp_acl.a_perm = aclp0->a_perm;
+
+ aclp0->a_type = aclp1->a_type;
+ aclp0->a_id = aclp1->a_id;
+ aclp0->a_perm = aclp1->a_perm;
+
+ aclp1->a_type = temp_acl.a_type;
+ aclp1->a_id = temp_acl.a_id;
+ aclp1->a_perm = temp_acl.a_perm;
+}
+
+/* prohibited_duplicate_type
+ * Identifies if given ACL type can have duplicate entries or
+ * not.
+ *
+ * Inputs: acl_type - ACL Type.
+ *
+ * Outputs:
+ *
+ * Return..
+ *
+ * True - If the ACL type matches any of the prohibited types.
+ * False - If the ACL type doesn't match any of the prohibited types.
+ */
+
+static BOOL hpux_prohibited_duplicate_type(int acl_type)
+{
+ switch (acl_type) {
+ case USER:
+ case GROUP:
+ case DEF_USER:
+ case DEF_GROUP:
+ return True;
+ default:
+ return False;
+ }
+}
+
+/* get_needed_class_perm
+ * Returns the permissions of a ACL structure only if the ACL
+ * type matches one of the pre-determined types for computing
+ * CLASS_OBJ permissions.
+ *
+ * Inputs: aclp - Pointer to ACL structure.
+ */
+
+static int hpux_get_needed_class_perm(struct acl *aclp)
+{
+ switch (aclp->a_type) {
+ case USER:
+ case GROUP_OBJ:
+ case GROUP:
+ case DEF_USER_OBJ:
+ case DEF_USER:
+ case DEF_GROUP_OBJ:
+ case DEF_GROUP:
+ case DEF_CLASS_OBJ:
+ case DEF_OTHER_OBJ:
+ return aclp->a_perm;
+ default:
+ return 0;
+ }
+}
+
+/* acl_sort for HPUX.
+ * Sorts the array of ACL structures as per the description in
+ * aclsort man page. Refer to aclsort man page for more details
+ *
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL structures.
+ * calclass - If this is not zero, then we compute the CLASS_OBJ
+ * permissions.
+ * aclp - Array of ACL structures.
+ *
+ * Outputs:
+ *
+ * aclp - Sorted array of ACL structures.
+ *
+ * Outputs:
+ *
+ * Returns 0 for success -1 for failure. Prints a message to the Samba
+ * debug log in case of failure.
+ */
+
+static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
+{
+#if !defined(HAVE_HPUX_ACLSORT)
+ /*
+ * The aclsort() system call is available on the latest HPUX General
+ * Patch Bundles. So for HPUX, we developed our version of acl_sort
+ * function. Because, we don't want to update to a new
+ * HPUX GR bundle just for aclsort() call.
+ */
+
+ struct hpux_acl_types acl_obj_count;
+ int n_class_obj_perm = 0;
+ int i, j;
+
+ if (!acl_count) {
+ DEBUG(10, ("Zero acl count passed. Returning Success\n"));
+ return 0;
+ }
+
+ if (aclp == NULL) {
+ DEBUG(0, ("Null ACL pointer in hpux_acl_sort. Returning Failure. \n"));
+ return -1;
+ }
+
+ /* Count different types of ACLs in the ACLs array */
+
+ hpux_count_obj(acl_count, aclp, &acl_obj_count);
+
+ /* There should be only one entry each of type USER_OBJ, GROUP_OBJ,
+ * CLASS_OBJ and OTHER_OBJ
+ */
+
+ if (acl_obj_count.n_user_obj != 1
+ || acl_obj_count.n_group_obj != 1
+ || acl_obj_count.n_class_obj != 1
+ || acl_obj_count.n_other_obj != 1) {
+ DEBUG(0, ("hpux_acl_sort: More than one entry or no entries for \
+USER OBJ or GROUP_OBJ or OTHER_OBJ or CLASS_OBJ\n"));
+ return -1;
+ }
+
+ /* If any of the default objects are present, there should be only
+ * one of them each.
+ */
+ if (acl_obj_count.n_def_user_obj > 1 || acl_obj_count.n_def_group_obj > 1
+ || acl_obj_count.n_def_other_obj > 1 || acl_obj_count.n_def_class_obj > 1) {
+ DEBUG(0, ("hpux_acl_sort: More than one entry for DEF_CLASS_OBJ \
+or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n"));
+ return -1;
+ }
+
+ /* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl
+ * structures.
+ *
+ * Sorting crieteria - First sort by ACL type. If there are multiple entries of
+ * same ACL type, sort by ACL id.
+ *
+ * I am using the trivial kind of sorting method here because, performance isn't
+ * really effected by the ACLs feature. More over there aren't going to be more
+ * than 17 entries on HPUX.
+ */
+
+ for (i = 0; i < acl_count; i++) {
+ for (j = i+1; j < acl_count; j++) {
+ if (aclp[i].a_type > aclp[j].a_type) {
+ /* ACL entries out of order, swap them */
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+ } else if (aclp[i].a_type == aclp[j].a_type) {
+ /* ACL entries of same type, sort by id */
+ if (aclp[i].a_id > aclp[j].a_id) {
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+ } else if (aclp[i].a_id == aclp[j].a_id) {
+ /* We have a duplicate entry. */
+ if (hpux_prohibited_duplicate_type(aclp[i].a_type)) {
+ DEBUG(0, ("hpux_acl_sort: Duplicate entry: Type(hex): %x Id: %d\n",
+ aclp[i].a_type, aclp[i].a_id));
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+ /* set the class obj permissions to the computed one. */
+ if (calclass) {
+ int n_class_obj_index = -1;
+
+ for (i = 0;i < acl_count; i++) {
+ n_class_obj_perm |= hpux_get_needed_class_perm((aclp+i));
+ if (aclp[i].a_type == CLASS_OBJ)
+ n_class_obj_index = i;
+ }
+ aclp[n_class_obj_index].a_perm = n_class_obj_perm;
+ }
+
+ return 0;
+#else
+ return aclsort(acl_count, calclass, aclp);
+#endif
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+ int fixmask = (acl_d->count <= 4);
+
+ if (hpux_acl_sort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if (hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ errno=ENOSYS;
+ return -1;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count);
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof acl_buf[0]);
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof acl_buf[0]);
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, ACL_SET, acl_count, acl_p);
+
+ if (acl_buf) {
+ free(acl_buf);
+ }
+
+ return ret;
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_set_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS, acl_d);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, ACL_SET, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ free(acl_d);
+ return 0;
+}
+
+#elif defined(HAVE_IRIX_ACLS) /*---------------------------------------------*/
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->aclp->acl_cnt) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->aclp->acl_entry[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->ae_tag;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T a;
+
+ if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_file(path_p, type)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T a;
+
+ if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_fd(fd)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ *tag_type_p = entry->ae_tag;
+
+ *bits_p = entry->ae_perm;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ *u_g_id_p = entry->ae_id;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof a[0] + sizeof (struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->next = -1;
+ a->freeaclp = False;
+ a->aclp = (struct acl *)((char *)a + sizeof a[0]);
+ a->aclp->acl_cnt = 0;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->aclp->acl_cnt >= ACL_MAX_ENTRIES) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->aclp->acl_entry[acl_d->aclp->acl_cnt++];
+ entry_d->ae_tag = 0;
+ entry_d->ae_id = 0;
+ entry_d->ae_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->ae_tag = tag_type;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ entry->ae_id = u_g_id;
+
+ entry->ae_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry_d, uint32 bits)
+{
+ entry_d->ae_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_valid(acl_d->aclp);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ return acl_set_file(name, type, acl_d->aclp);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ return acl_set_fd(fd, acl_d->aclp);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ if (acl_d->freeaclp) {
+ acl_free(acl_d->aclp);
+ }
+ acl_free(acl_d);
+ return 0;
+}
+
+#elif defined(HAVE_AIX_ACLS) /*----------------------------------------------*/
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+int sys_acl_get_entry(SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ struct acl_entry_link *link;
+ int keep_going;
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ theacl->count = 0;
+ else if (entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ DEBUG(10, ("This is the count: %d\n", theacl->count));
+
+ /* Check if count was previously set to -1. *
+ * If it was, that means we reached the end *
+ * of the acl last time. */
+ if (theacl->count == -1)
+ return 0;
+
+ link = theacl;
+ /* To get to the next acl, traverse linked list until index *
+ * of acl matches the count we are keeping. This count is *
+ * incremented each time we return an acl entry. */
+
+ for (keep_going = 0; keep_going < theacl->count; keep_going++)
+ link = link->nextp;
+
+ *entry_p = link->entryp;
+
+#if 0
+ {
+ struct new_acl_entry *entry = *entry_p;
+ DEBUG(10, ("*entry_p is %lx\n", (long)entry));
+ DEBUG(10, ("*entry_p->ace_access is %d\n", entry->ace_access));
+ }
+#endif
+
+ /* Increment count */
+ theacl->count++;
+ if (link->nextp == NULL)
+ theacl->count = -1;
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ /* Initialize tag type */
+
+ *tag_type_p = -1;
+ DEBUG(10, ("the tagtype is %d\n", entry_d->ace_id->id_type));
+
+ /* Depending on what type of entry we have, *
+ * return tag type. */
+ switch (entry_d->ace_id->id_type) {
+ case ACEID_USER:
+ *tag_type_p = SMB_ACL_USER;
+ break;
+ case ACEID_GROUP:
+ *tag_type_p = SMB_ACL_GROUP;
+ break;
+
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ *tag_type_p = entry_d->ace_id->id_type;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+
+ /* AIX has no DEFAULT */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+#ifdef ENOTSUP
+ errno = ENOTSUP;
+#else
+ errno = ENOSYS;
+#endif
+ return NULL;
+ }
+
+ /* Get the acl using statacl */
+
+ DEBUG(10, ("Entering sys_acl_get_file\n"));
+ DEBUG(10, ("path_p is %s\n", path_p));
+
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if (file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file: %d\n", errno));
+ return NULL;
+ }
+
+ memset(file_acl, 0, BUFSIZ);
+
+ rc = statacl((char *)path_p, 0, file_acl, BUFSIZ);
+ if (rc == -1) {
+ DEBUG(0, ("statacl returned %d with errno %d\n", rc, errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ DEBUG(10, ("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+ acl_entry = file_acl->acl_ext;
+
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if (acl_entry_link_head == NULL)
+ return NULL;
+
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+
+ DEBUG(10, ("acl_entry is %d\n", acl_entry));
+ DEBUG(10, ("acl_last(file_acl) id %d\n", acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if (file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while (acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+ idp = id_nxt(acl_entry->ace_id);
+ if ((acl_entry->ace_type == ACC_SPECIFY || acl_entry->ace_type == ACC_PERMIT)
+ && idp != id_last(acl_entry)) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if (acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+
+ if (acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id, idp, sizeof (struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch (acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10, ("acl_entry->ace_access is %d\n", acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10, ("acl_entry_link->entryp->ace_access is %d\n", acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return 0;
+ }
+
+ DEBUG(10, ("acl_entry = %d\n", acl_entry));
+ DEBUG(10, ("The ace_type is %d\n", acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for (i = 1; i < 4; i++) {
+ DEBUG(10, ("i is %d\n", i));
+ if (acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+ if (acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof (struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof (struct ace_id);
+ DEBUG(10, ("idp->id_len = %d\n", idp->id_len));
+ memset(idp->id_data, 0, sizeof (uid_t));
+
+ switch (i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return NULL;
+
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10, ("new_acl_entry->ace_access = %d\n", new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return acl_entry_link_head;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+
+ /* Get the acl using fstatacl */
+
+ DEBUG(10, ("Entering sys_acl_get_fd\n"));
+ DEBUG(10, ("fd is %d\n", fd));
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if (file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ return NULL;
+ }
+
+ memset(file_acl, 0, BUFSIZ);
+
+ rc = fstatacl(fd, 0, file_acl, BUFSIZ);
+ if (rc == -1) {
+ DEBUG(0, ("The fstatacl call returned %d with errno %d\n", rc, errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ DEBUG(10, ("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+
+ acl_entry = file_acl->acl_ext;
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if (acl_entry_link_head == NULL){
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+
+ if (acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ DEBUG(10, ("acl_entry is %d\n", acl_entry));
+ DEBUG(10, ("acl_last(file_acl) id %d\n", acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if (file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while (acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+
+ idp = id_nxt(acl_entry->ace_id);
+ if ((acl_entry->ace_type == ACC_SPECIFY ||
+ (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if (acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+ if (acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id, idp, sizeof (struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch (acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10, ("acl_entry->ace_access is %d\n", acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10, ("acl_entry_link->entryp->ace_access is %d\n", acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return 0;
+ }
+
+ DEBUG(10, ("acl_entry = %d\n", acl_entry));
+ DEBUG(10, ("The ace_type is %d\n", acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for (i = 1; i < 4; i++) {
+ DEBUG(10, ("i is %d\n", i));
+ if (acl_entry_link_head->count != 0){
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+ if (acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+
+ if (acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ return NULL;
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof (struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof (struct ace_id);
+ DEBUG(10, ("idp->id_len = %d\n", idp->id_len));
+ memset(idp->id_data, 0, sizeof (uid_t));
+
+ switch (i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10, ("new_acl_entry->ace_access = %d\n", new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return acl_entry_link_head;
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ uint *permset;
+
+ if (sys_acl_get_tag_type(entry, tag_type_p) != 0)
+ return -1;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ memcpy(u_g_id_p, entry->ace_id->id_data, sizeof (id_t));
+
+ permset = &entry->ace_access;
+
+ DEBUG(10, ("*permset is %d\n", *permset));
+ *bits_p = (*permset & S_IRUSR ? 4 : 0)
+ | (*permset & S_IWUSR ? 2 : 0)
+ | (*permset & S_IXUSR ? 1 : 0);
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ struct acl_entry_link *theacl = NULL;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ DEBUG(10, ("Entering sys_acl_init\n"));
+
+ theacl = SMB_MALLOC_P(struct acl_entry_link);
+ if (theacl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_init is %d\n", errno));
+ return NULL;
+ }
+
+ theacl->count = 0;
+ theacl->nextp = NULL;
+ theacl->prevp = NULL;
+ theacl->entryp = NULL;
+ DEBUG(10, ("Exiting sys_acl_init\n"));
+ return theacl;
+}
+
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ struct acl_entry_link *theacl;
+ struct acl_entry_link *acl_entryp;
+ struct acl_entry_link *temp_entry = NULL;
+ int counting;
+
+ DEBUG(10, ("Entering the sys_acl_create_entry\n"));
+
+ theacl = acl_entryp = *pacl;
+
+ /* Get to the end of the acl before adding entry */
+
+ for (counting = 0; counting < theacl->count; counting++){
+ DEBUG(10, ("The acl_entryp is %d\n", acl_entryp));
+ temp_entry = acl_entryp;
+ acl_entryp = acl_entryp->nextp;
+ }
+
+ if (theacl->count != 0){
+ temp_entry->nextp = acl_entryp = SMB_MALLOC_P(struct acl_entry_link);
+ if (acl_entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_create_entry is %d\n", errno));
+ return -1;
+ }
+
+ DEBUG(10, ("The acl_entryp is %d\n", acl_entryp));
+ acl_entryp->prevp = temp_entry;
+ DEBUG(10, ("The acl_entryp->prevp is %d\n", acl_entryp->prevp));
+ }
+
+ *pentry = acl_entryp->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (*pentry == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_create_entry is %d\n", errno));
+ return -1;
+ }
+
+ memset(*pentry, 0, sizeof (struct new_acl_entry));
+ acl_entryp->entryp->ace_len = sizeof (struct acl_entry);
+ acl_entryp->entryp->ace_type = ACC_PERMIT;
+ acl_entryp->entryp->ace_id->id_len = sizeof (struct ace_id);
+ acl_entryp->nextp = NULL;
+ theacl->count++;
+ DEBUG(10, ("Exiting sys_acl_create_entry\n"));
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->ace_id->id_type = tag_type;
+ DEBUG(10, ("The tag type is %d\n", entry->ace_id->id_type));
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ memcpy(entry->ace_id->id_data, &u_g_id, sizeof (id_t));
+
+ entry->ace_access = bits;
+ DEBUG(10, ("entry->ace_access = %d\n", entry->ace_access));
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ DEBUG(10, ("Starting AIX sys_acl_set_permset\n"));
+ entry->ace_access = bits;
+ DEBUG(10, ("entry->ace_access = %d\n", entry->ace_access));
+ DEBUG(10, ("Ending AIX sys_acl_set_permset\n"));
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T theacl)
+{
+ int user_obj = 0;
+ int group_obj = 0;
+ int other_obj = 0;
+ struct acl_entry_link *acl_entry;
+
+ for (acl_entry=theacl; acl_entry != NULL; acl_entry = acl_entry->nextp) {
+ user_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_USER_OBJ);
+ group_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_GROUP_OBJ);
+ other_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_OTHER);
+ }
+
+ DEBUG(10, ("user_obj=%d, group_obj=%d, other_obj=%d\n", user_obj, group_obj, other_obj));
+
+ if (user_obj != 1 || group_obj != 1 || other_obj != 1)
+ return -1;
+
+ return 0;
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10, ("Entering sys_acl_set_file\n"));
+ DEBUG(10, ("File name is %s\n", name));
+
+ /* AIX has no default ACL */
+ if (acltype == SMB_ACL_TYPE_DEFAULT)
+ return 0;
+
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if (file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_set_file is %d\n", errno));
+ return -1;
+ }
+
+ memset(file_acl, 0, BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for (acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+
+ switch (id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if ((file_acl->acl_len + sizeof (struct acl_entry)) > acl_length) {
+ acl_length += sizeof (struct acl_entry);
+ file_acl_temp = (struct acl *)SMB_MALLOC(acl_length);
+ if (file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_set_file is %d\n", errno));
+ return -1;
+ }
+
+ memcpy(file_acl_temp, file_acl, file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof (struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if (!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10, ("The id type is %d\n", ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof (uid_t));
+ memcpy(acl_entry->ace_id->id_data, &user_id, sizeof (uid_t));
+ }
+
+ rc = chacl((char*)name, file_acl, file_acl->acl_len);
+ DEBUG(10, ("errno is %d\n", errno));
+ DEBUG(10, ("return code is %d\n", rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10, ("Exiting the sys_acl_set_file\n"));
+ return rc;
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10, ("Entering sys_acl_set_fd\n"));
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if (file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_set_fd is %d\n", errno));
+ return -1;
+ }
+
+ memset(file_acl, 0, BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for (acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10, ("The id_type is %d\n", id_type));
+
+ switch (id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if ((file_acl->acl_len + sizeof (struct acl_entry)) > acl_length) {
+ acl_length += sizeof (struct acl_entry);
+ file_acl_temp = (struct acl *)SMB_MALLOC(acl_length);
+ if (file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_set_fd is %d\n", errno));
+ return -1;
+ }
+
+ memcpy(file_acl_temp, file_acl, file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof (struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if (!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10, ("The id type is %d\n", ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof (uid_t));
+ memcpy(ace_id->id_data, &user_id, sizeof (uid_t));
+ }
+
+ rc = fchacl(fd, file_acl, file_acl->acl_len);
+ DEBUG(10, ("errno is %d\n", errno));
+ DEBUG(10, ("return code is %d\n", rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10, ("Exiting sys_acl_set_fd\n"));
+ return rc;
+}
+#endif
+
+int sys_acl_delete_def_file(UNUSED(const char *name))
+{
+ /* AIX has no default ACL */
+ return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T posix_acl)
+{
+ struct acl_entry_link *acl_entry_link;
+
+ for (acl_entry_link = posix_acl->nextp; acl_entry_link->nextp != NULL; acl_entry_link = acl_entry_link->nextp) {
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ }
+
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ SAFE_FREE(acl_entry_link->entryp);
+ SAFE_FREE(acl_entry_link);
+
+ return 0;
+}
+
+#elif defined(HAVE_OSX_ACLS) /*----------------------------------------------*/
+
+#define OSX_BROKEN_GETENTRY /* returns 0 instead of 1 */
+
+#include <membership.h>
+
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ int ret = acl_get_entry(the_acl, entry_id, entry_p);
+#ifdef OSX_BROKEN_GETENTRY
+ if (ret == 0)
+ ret = 1;
+ else if (ret == -1 && errno == 22)
+ ret = 0;
+#endif
+ return ret;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ if (type == ACL_TYPE_DEFAULT) {
+ errno = ENOTSUP;
+ return NULL;
+ }
+ errno = 0;
+ return acl_get_file(path_p, type);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ uuid_t *uup;
+ acl_tag_t tag;
+ acl_flagset_t flagset;
+ acl_permset_t permset;
+ uint32 bits, fb, bb, pb;
+ int id_type = -1;
+ int rc;
+
+ if (acl_get_tag_type(entry, &tag) != 0
+ || acl_get_flagset_np(entry, &flagset) != 0
+ || acl_get_permset(entry, &permset) != 0
+ || (uup = acl_get_qualifier(entry)) == NULL)
+ return -1;
+
+ rc = mbr_uuid_to_id(*uup, u_g_id_p, &id_type);
+ acl_free(uup);
+ if (rc != 0)
+ return rc;
+
+ if (id_type == ID_TYPE_UID)
+ *tag_type_p = SMB_ACL_USER;
+ else
+ *tag_type_p = SMB_ACL_GROUP;
+
+ bits = tag == ACL_EXTENDED_ALLOW ? 1 : 0;
+
+ for (fb = (1u<<4), bb = (1u<<1); bb < (1u<<12); fb *= 2, bb *= 2) {
+ if (acl_get_flag_np(flagset, fb) == 1)
+ bits |= bb;
+ }
+
+ for (pb = (1u<<1), bb = (1u<<12); bb < (1u<<25); pb *= 2, bb *= 2) {
+ if (acl_get_perm_np(permset, pb) == 1)
+ bits |= bb;
+ }
+
+ *bits_p = bits;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ acl_flagset_t flagset;
+ acl_permset_t permset;
+ uint32 fb, bb, pb;
+ int is_user = tag_type == SMB_ACL_USER;
+ uuid_t uu;
+ int rc;
+
+ tag_type = bits & 1 ? ACL_EXTENDED_ALLOW : ACL_EXTENDED_DENY;
+
+ if (acl_get_flagset_np(entry, &flagset) != 0
+ || acl_get_permset(entry, &permset) != 0)
+ return -1;
+
+ acl_clear_flags_np(flagset);
+ acl_clear_perms(permset);
+
+ for (fb = (1u<<4), bb = (1u<<1); bb < (1u<<12); fb *= 2, bb *= 2) {
+ if (bits & bb)
+ acl_add_flag_np(flagset, fb);
+ }
+
+ for (pb = (1u<<1), bb = (1u<<12); bb < (1u<<25); pb *= 2, bb *= 2) {
+ if (bits & bb)
+ acl_add_perm(permset, pb);
+ }
+
+ if (is_user)
+ rc = mbr_uid_to_uuid(u_g_id, uu);
+ else
+ rc = mbr_gid_to_uuid(u_g_id, uu);
+ if (rc != 0)
+ return rc;
+
+ if (acl_set_tag_type(entry, tag_type) != 0
+ || acl_set_qualifier(entry, &uu) != 0
+ || acl_set_permset(entry, permset) != 0
+ || acl_set_flagset_np(entry, flagset) != 0)
+ return -1;
+
+ return 0;
+}
+
+#if 0
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ return -1; /* Not needed for OS X. */
+}
+#endif
+
+int sys_acl_valid(SMB_ACL_T theacl)
+{
+ return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file(name, acltype, theacl);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, theacl);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+#else /* No ACLs. */
+
+#error No ACL functions defined for this platform!
+
+#endif
+
+/************************************************************************
+ Deliberately outside the ACL defines. Return 1 if this is a "no acls"
+ errno, 0 if not.
+************************************************************************/
+
+int no_acl_syscall_error(int err)
+{
+#ifdef HAVE_OSX_ACLS
+ if (err == ENOENT)
+ return 1; /* Weird problem with directory ACLs. */
+#endif
+#if defined(ENOSYS)
+ if (err == ENOSYS) {
+ return 1;
+ }
+#endif
+#if defined(ENOTSUP)
+ if (err == ENOTSUP) {
+ return 1;
+ }
+#endif
+ if (err == EINVAL) {
+ /* If the type of SMB_ACL_TYPE_ACCESS or SMB_ACL_TYPE_DEFAULT
+ * isn't valid, then the ACLs must be non-POSIX. */
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* SUPPORT_ACLS */
diff --git a/lib/sysacls.h b/lib/sysacls.h
new file mode 100644
index 0000000..c069597
--- /dev/null
+++ b/lib/sysacls.h
@@ -0,0 +1,307 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 2.2.x
+ * Portable SMB ACL interface
+ * Copyright (C) Jeremy Allison 2000
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#ifdef SUPPORT_ACLS
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_ACL_LIBACL_H
+#include <acl/libacl.h>
+#endif
+
+#define SMB_MALLOC(cnt) new_array(char, cnt)
+#define SMB_MALLOC_P(obj) new_array(obj, 1)
+#define SMB_MALLOC_ARRAY(obj, cnt) new_array(obj, cnt)
+#define SMB_REALLOC(mem, cnt) realloc_array(mem, char, cnt)
+#define slprintf snprintf
+
+#if defined HAVE_POSIX_ACLS /*-----------------------------------------------*/
+
+/* This is an identity mapping (just remove the SMB_). */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_TRU64_ACLS /*---------------------------------------------*/
+
+/* This is for DEC/Compaq Tru64 UNIX */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_UNIXWARE_ACLS || defined HAVE_SOLARIS_ACLS /*-------------*/
+
+/* Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris. */
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#ifdef __CYGWIN__
+#define SMB_ACL_LOSES_SPECIAL_MODE_BITS
+#endif
+
+#elif defined HAVE_HPUX_ACLS /*----------------------------------------------*/
+
+/* Based on the Solaris & UnixWare code. */
+
+#ifndef __TANDEM
+#undef GROUP
+#endif
+#include <sys/aclv.h>
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_IRIX_ACLS /*----------------------------------------------*/
+
+/* IRIX ACLs */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER_OBJ
+#define SMB_ACL_MASK ACL_MASK
+
+typedef struct SMB_ACL_T {
+ int next;
+ BOOL freeaclp;
+ struct acl *aclp;
+} *SMB_ACL_T;
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_AIX_ACLS /*-----------------------------------------------*/
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+#include "/usr/include/acl.h"
+
+struct acl_entry_link{
+ struct acl_entry_link *prevp;
+ struct new_acl_entry *entryp;
+ struct acl_entry_link *nextp;
+ int count;
+};
+
+struct new_acl_entry{
+ unsigned short ace_len;
+ unsigned short ace_type;
+ unsigned int ace_access;
+ struct ace_id ace_id[1];
+};
+
+#define SMB_ACL_ENTRY_T struct new_acl_entry*
+#define SMB_ACL_T struct acl_entry_link*
+
+#define SMB_ACL_TAG_T unsigned short
+#define SMB_ACL_TYPE_T int
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACEID_USER
+#define SMB_ACL_USER_OBJ 3
+#define SMB_ACL_GROUP ACEID_GROUP
+#define SMB_ACL_GROUP_OBJ 4
+#define SMB_ACL_OTHER 5
+#define SMB_ACL_MASK 6
+
+#define SMB_ACL_FIRST_ENTRY 1
+#define SMB_ACL_NEXT_ENTRY 2
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined(HAVE_OSX_ACLS) /*----------------------------------------------*/
+
+/* Special handling for OS X ACLs */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_USER 1
+#define SMB_ACL_GROUP 2
+
+#define SMB_ACL_FIRST_ENTRY ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_EXTENDED
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS ((1<<25)-1)
+#define SMB_ACL_VALID_OBJ_BITS 0
+
+/*#undef SMB_ACL_NEED_SORT*/
+
+#else /*---------------------------------------------------------------------*/
+
+/* Unknown platform. */
+
+#error Cannot handle ACLs on this platform!
+
+#endif
+
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p);
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p);
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p);
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type);
+SMB_ACL_T sys_acl_get_fd(int fd);
+SMB_ACL_T sys_acl_init(int count);
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry);
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype, uint32 bits, id_t u_g_id);
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits);
+int sys_acl_valid(SMB_ACL_T theacl);
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl);
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl);
+int sys_acl_delete_def_file(const char *name);
+int sys_acl_free_acl(SMB_ACL_T the_acl);
+int no_acl_syscall_error(int err);
+
+#endif /* SUPPORT_ACLS */
diff --git a/lib/sysxattrs.c b/lib/sysxattrs.c
new file mode 100644
index 0000000..ca08d13
--- /dev/null
+++ b/lib/sysxattrs.c
@@ -0,0 +1,301 @@
+/*
+ * Extended attribute support for rsync.
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2003-2022 Wayne Davison
+ * Written by Jay Fenlason.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "sysxattrs.h"
+
+#ifdef SUPPORT_XATTRS
+
+#ifdef HAVE_OSX_XATTRS
+#define GETXATTR_FETCH_LIMIT (64*1024*1024)
+#endif
+
+#if defined HAVE_LINUX_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ return lgetxattr(path, name, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ return fgetxattr(filedes, name, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ return lsetxattr(path, name, value, size, 0);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ return lremovexattr(path, name);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ return llistxattr(path, list, size);
+}
+
+#elif HAVE_OSX_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ ssize_t len = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
+
+ /* If we're retrieving data, handle resource forks > 64MB specially */
+ if (value != NULL && len == GETXATTR_FETCH_LIMIT && (size_t)len < size) {
+ /* getxattr will only return 64MB of data at a time, need to call again with a new offset */
+ u_int32_t offset = len;
+ size_t data_retrieved = len;
+ while (data_retrieved < size) {
+ len = getxattr(path, name, (char*)value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
+ if (len <= 0)
+ break;
+ data_retrieved += len;
+ offset += (u_int32_t)len;
+ }
+ len = data_retrieved;
+ }
+
+ return len;
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ return fgetxattr(filedes, name, value, size, 0, 0);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ return removexattr(path, name, XATTR_NOFOLLOW);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ return listxattr(path, list, size, XATTR_NOFOLLOW);
+}
+
+#elif HAVE_FREEBSD_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ unsigned char keylen;
+ ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
+
+ if (len <= 0 || (size_t)len > size)
+ return len;
+
+ /* FreeBSD puts a single-byte length before each string, with no '\0'
+ * terminator. We need to change this into a series of null-terminted
+ * strings. Since the size is the same, we can simply transform the
+ * output in place. */
+ for (off = 0; off < len; off += keylen + 1) {
+ keylen = ((unsigned char*)list)[off];
+ if (off + keylen >= len) {
+ /* Should be impossible, but kernel bugs happen! */
+ errno = EINVAL;
+ return -1;
+ }
+ memmove(list+off, list+off+1, keylen);
+ list[off+keylen] = '\0';
+ }
+
+ return len;
+}
+
+#elif HAVE_SOLARIS_XATTRS
+
+static ssize_t read_xattr(int attrfd, void *buf, size_t buflen)
+{
+ STRUCT_STAT sb;
+ ssize_t ret;
+
+ if (fstat(attrfd, &sb) < 0)
+ ret = -1;
+ else if (sb.st_size > SSIZE_MAX) {
+ errno = ERANGE;
+ ret = -1;
+ } else if (buflen == 0)
+ ret = sb.st_size;
+ else if (sb.st_size > buflen) {
+ errno = ERANGE;
+ ret = -1;
+ } else {
+ size_t bufpos;
+ for (bufpos = 0; bufpos < sb.st_size; ) {
+ ssize_t cnt = read(attrfd, (char*)buf + bufpos, sb.st_size - bufpos);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EINTR)
+ continue;
+ bufpos = -1;
+ break;
+ }
+ bufpos += cnt;
+ }
+ ret = bufpos;
+ }
+
+ close(attrfd);
+
+ return ret;
+}
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ int attrfd;
+
+ if ((attrfd = attropen(path, name, O_RDONLY)) < 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+
+ return read_xattr(attrfd, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ int attrfd;
+
+ if ((attrfd = openat(filedes, name, O_RDONLY|O_XATTR, 0)) < 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+
+ return read_xattr(attrfd, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ int attrfd;
+ size_t bufpos;
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+ if ((attrfd = attropen(path, name, O_CREAT|O_TRUNC|O_WRONLY, mode)) < 0)
+ return -1;
+
+ for (bufpos = 0; bufpos < size; ) {
+ ssize_t cnt = write(attrfd, (char*)value + bufpos, size);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EINTR)
+ continue;
+ bufpos = -1;
+ break;
+ }
+ bufpos += cnt;
+ }
+
+ close(attrfd);
+
+ return bufpos > 0 ? 0 : -1;
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ int attrdirfd;
+ int ret;
+
+ if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0)
+ return -1;
+
+ ret = unlinkat(attrdirfd, name, 0);
+
+ close(attrdirfd);
+
+ return ret;
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ int attrdirfd;
+ DIR *dirp;
+ struct dirent *dp;
+ ssize_t ret = 0;
+
+ if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ if ((dirp = fdopendir(attrdirfd)) == NULL) {
+ close(attrdirfd);
+ return -1;
+ }
+
+ while ((dp = readdir(dirp))) {
+ int len = strlen(dp->d_name);
+
+ if (dp->d_name[0] == '.' && (len == 1 || (len == 2 && dp->d_name[1] == '.')))
+ continue;
+ if (len == 11 && dp->d_name[0] == 'S' && strncmp(dp->d_name, "SUNWattr_r", 10) == 0
+ && (dp->d_name[10] == 'o' || dp->d_name[10] == 'w'))
+ continue;
+
+ ret += len + 1;
+ if ((size_t)ret > size) {
+ if (size == 0)
+ continue;
+ ret = -1;
+ errno = ERANGE;
+ break;
+ }
+ memcpy(list, dp->d_name, len+1);
+ list += len+1;
+ }
+
+ closedir(dirp);
+ close(attrdirfd);
+
+ return ret;
+}
+
+#else
+
+#error You need to create xattr compatibility functions.
+
+#endif
+
+#endif /* SUPPORT_XATTRS */
diff --git a/lib/sysxattrs.h b/lib/sysxattrs.h
new file mode 100644
index 0000000..024bbd1
--- /dev/null
+++ b/lib/sysxattrs.h
@@ -0,0 +1,26 @@
+#ifdef SUPPORT_XATTRS
+
+#if defined HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#elif defined HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#elif defined HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+
+/* Linux 2.4 does not define this as a distinct errno value: */
+#ifndef ENOATTR
+#define ENOATTR ENODATA
+#endif
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
+int sys_lremovexattr(const char *path, const char *name);
+ssize_t sys_llistxattr(const char *path, char *list, size_t size);
+
+#else
+
+/* No xattrs available */
+
+#endif
diff --git a/lib/wildmatch.c b/lib/wildmatch.c
new file mode 100644
index 0000000..f3a1731
--- /dev/null
+++ b/lib/wildmatch.c
@@ -0,0 +1,368 @@
+/*
+** Do shell-style pattern matching for ?, \, [], and * characters.
+** It is 8bit clean.
+**
+** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+** Rich $alz is now <rsalz@bbn.com>.
+**
+** Modified by Wayne Davison to special-case '/' matching, to make '**'
+** work differently than '*', and to fix the character-class code.
+*/
+
+#include "rsync.h"
+
+/* What character marks an inverted character class? */
+#define NEGATE_CLASS '!'
+#define NEGATE_CLASS2 '^'
+
+#define FALSE 0
+#define TRUE 1
+#define ABORT_ALL -1
+#define ABORT_TO_STARSTAR -2
+
+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
+ && *(class) == *(litmatch) \
+ && strncmp((char*)class, litmatch, len) == 0)
+
+#if defined STDC_HEADERS || !defined isascii
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII(c) && isblank(c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
+#else
+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
+#endif
+
+#define ISPRINT(c) (ISASCII(c) && isprint(c))
+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
+#define ISALNUM(c) (ISASCII(c) && isalnum(c))
+#define ISALPHA(c) (ISASCII(c) && isalpha(c))
+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
+#define ISLOWER(c) (ISASCII(c) && islower(c))
+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
+#define ISSPACE(c) (ISASCII(c) && isspace(c))
+#define ISUPPER(c) (ISASCII(c) && isupper(c))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+
+#ifdef WILD_TEST_ITERATIONS
+int wildmatch_iteration_count;
+#endif
+
+static int force_lower_case = 0;
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
+{
+ uchar p_ch;
+
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count++;
+#endif
+
+ for ( ; (p_ch = *p) != '\0'; text++, p++) {
+ int matched, special;
+ uchar t_ch, prev_ch;
+ while ((t_ch = *text) == '\0') {
+ if (*a == NULL) {
+ if (p_ch != '*')
+ return ABORT_ALL;
+ break;
+ }
+ text = *a++;
+ }
+ if (force_lower_case && ISUPPER(t_ch))
+ t_ch = tolower(t_ch);
+ switch (p_ch) {
+ case '\\':
+ /* Literal match with following character. Note that the test
+ * in "default" handles the p[1] == '\0' failure case. */
+ p_ch = *++p;
+ /* FALLTHROUGH */
+ default:
+ if (t_ch != p_ch)
+ return FALSE;
+ continue;
+ case '?':
+ /* Match anything but '/'. */
+ if (t_ch == '/')
+ return FALSE;
+ continue;
+ case '*':
+ if (*++p == '*') {
+ while (*++p == '*') {}
+ special = TRUE;
+ } else
+ special = FALSE;
+ if (*p == '\0') {
+ /* Trailing "**" matches everything. Trailing "*" matches
+ * only if there are no more slash characters. */
+ if (!special) {
+ do {
+ if (strchr((char*)text, '/') != NULL)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+ }
+ return TRUE;
+ }
+ while (1) {
+ if (t_ch == '\0') {
+ if ((text = *a++) == NULL)
+ break;
+ t_ch = *text;
+ continue;
+ }
+ if ((matched = dowild(p, text, a)) != FALSE) {
+ if (!special || matched != ABORT_TO_STARSTAR)
+ return matched;
+ } else if (!special && t_ch == '/')
+ return ABORT_TO_STARSTAR;
+ t_ch = *++text;
+ }
+ return ABORT_ALL;
+ case '[':
+ p_ch = *++p;
+#ifdef NEGATE_CLASS2
+ if (p_ch == NEGATE_CLASS2)
+ p_ch = NEGATE_CLASS;
+#endif
+ /* Assign literal TRUE/FALSE because of "matched" comparison. */
+ special = p_ch == NEGATE_CLASS? TRUE : FALSE;
+ if (special) {
+ /* Inverted character class. */
+ p_ch = *++p;
+ }
+ prev_ch = 0;
+ matched = FALSE;
+ do {
+ if (!p_ch)
+ return ABORT_ALL;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return ABORT_ALL;
+ if (t_ch == p_ch)
+ matched = TRUE;
+ } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
+ p_ch = *++p;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return ABORT_ALL;
+ }
+ if (t_ch <= p_ch && t_ch >= prev_ch)
+ matched = TRUE;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (p_ch == '[' && p[1] == ':') {
+ const uchar *s;
+ int i;
+ for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
+ if (!p_ch)
+ return ABORT_ALL;
+ i = p - s - 1;
+ if (i < 0 || p[-1] != ':') {
+ /* Didn't find ":]", so treat like a normal set. */
+ p = s - 2;
+ p_ch = '[';
+ if (t_ch == p_ch)
+ matched = TRUE;
+ continue;
+ }
+ if (CC_EQ(s,i, "alnum")) {
+ if (ISALNUM(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "alpha")) {
+ if (ISALPHA(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "blank")) {
+ if (ISBLANK(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "cntrl")) {
+ if (ISCNTRL(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "digit")) {
+ if (ISDIGIT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "graph")) {
+ if (ISGRAPH(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "lower")) {
+ if (ISLOWER(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "print")) {
+ if (ISPRINT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "punct")) {
+ if (ISPUNCT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "space")) {
+ if (ISSPACE(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "upper")) {
+ if (ISUPPER(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "xdigit")) {
+ if (ISXDIGIT(t_ch))
+ matched = TRUE;
+ } else /* malformed [:class:] string */
+ return ABORT_ALL;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (t_ch == p_ch)
+ matched = TRUE;
+ } while (prev_ch = p_ch, (p_ch = *++p) != ']');
+ if (matched == special || t_ch == '/')
+ return FALSE;
+ continue;
+ }
+ }
+
+ do {
+ if (*text)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+
+ return TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
+{
+ for ( ; *s != '\0'; text++, s++) {
+ while (*text == '\0') {
+ if ((text = *a++) == NULL)
+ return FALSE;
+ }
+ if (*text != *s)
+ return FALSE;
+ }
+
+ do {
+ if (*text)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+
+ return TRUE;
+}
+
+/* Return the last "count" path elements from the concatenated string.
+ * We return a string pointer to the start of the string, and update the
+ * array pointer-pointer to point to any remaining string elements. */
+static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
+{
+ const uchar*const *a = *a_ptr;
+ const uchar*const *first_a = a;
+
+ while (*a)
+ a++;
+
+ while (a != first_a) {
+ const uchar *s = *--a;
+ s += strlen((char*)s);
+ while (--s >= *a) {
+ if (*s == '/' && !--count) {
+ *a_ptr = a+1;
+ return s+1;
+ }
+ }
+ }
+
+ if (count == 1) {
+ *a_ptr = a+1;
+ return *a;
+ }
+
+ return NULL;
+}
+
+/* Match the "pattern" against the "text" string. */
+int wildmatch(const char *pattern, const char *text)
+{
+ static const uchar *nomore[1]; /* A NULL pointer. */
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+ return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+}
+
+/* Match the "pattern" against the forced-to-lower-case "text" string. */
+int iwildmatch(const char *pattern, const char *text)
+{
+ static const uchar *nomore[1]; /* A NULL pointer. */
+ int ret;
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+ force_lower_case = 1;
+ ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+ force_lower_case = 0;
+ return ret;
+}
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end). The int "where" can be 0 (normal matching), > 0 (match only
+ * the trailing N slash-separated filename components of "texts"), or < 0
+ * (match the "pattern" at the start or after any slash in "texts"). */
+int wildmatch_array(const char *pattern, const char*const *texts, int where)
+{
+ const uchar *p = (const uchar*)pattern;
+ const uchar*const *a = (const uchar*const*)texts;
+ const uchar *text;
+ int matched;
+
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+
+ if (where > 0)
+ text = trailing_N_elements(&a, where);
+ else
+ text = *a++;
+ if (!text)
+ return FALSE;
+
+ if ((matched = dowild(p, text, a)) != TRUE && where < 0
+ && matched != ABORT_ALL) {
+ while (1) {
+ if (*text == '\0') {
+ if ((text = (uchar*)*a++) == NULL)
+ return FALSE;
+ continue;
+ }
+ if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
+ && matched != ABORT_TO_STARSTAR)
+ break;
+ }
+ }
+ return matched == TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end). The int "where" can be 0 (normal matching), or > 0 (match
+ * only the trailing N slash-separated filename components of "texts"). */
+int litmatch_array(const char *string, const char*const *texts, int where)
+{
+ const uchar *s = (const uchar*)string;
+ const uchar*const *a = (const uchar* const*)texts;
+ const uchar *text;
+
+ if (where > 0)
+ text = trailing_N_elements(&a, where);
+ else
+ text = *a++;
+ if (!text)
+ return FALSE;
+
+ return doliteral(s, text, a) == TRUE;
+}
diff --git a/lib/wildmatch.h b/lib/wildmatch.h
new file mode 100644
index 0000000..e7f1a35
--- /dev/null
+++ b/lib/wildmatch.h
@@ -0,0 +1,6 @@
+/* wildmatch.h */
+
+int wildmatch(const char *pattern, const char *text);
+int iwildmatch(const char *pattern, const char *text);
+int wildmatch_array(const char *pattern, const char*const *texts, int where);
+int litmatch_array(const char *string, const char*const *texts, int where);