diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/addrinfo.h | 180 | ||||
-rw-r--r-- | lib/compat.c | 272 | ||||
-rw-r--r-- | lib/dummy.in | 2 | ||||
-rw-r--r-- | lib/getaddrinfo.c | 504 | ||||
-rw-r--r-- | lib/getpass.c | 72 | ||||
-rw-r--r-- | lib/inet_ntop.c | 186 | ||||
-rw-r--r-- | lib/inet_pton.c | 212 | ||||
-rw-r--r-- | lib/md-defines.h | 37 | ||||
-rw-r--r-- | lib/md5-asm-x86_64.S | 701 | ||||
-rw-r--r-- | lib/md5.c | 321 | ||||
-rw-r--r-- | lib/mdfour.c | 247 | ||||
-rw-r--r-- | lib/mdigest.h | 22 | ||||
-rw-r--r-- | lib/permstring.c | 65 | ||||
-rw-r--r-- | lib/permstring.h | 3 | ||||
-rw-r--r-- | lib/pool_alloc.3 | 268 | ||||
-rw-r--r-- | lib/pool_alloc.c | 375 | ||||
-rw-r--r-- | lib/pool_alloc.h | 21 | ||||
-rw-r--r-- | lib/snprintf.c | 1512 | ||||
-rw-r--r-- | lib/sysacls.c | 2802 | ||||
-rw-r--r-- | lib/sysacls.h | 307 | ||||
-rw-r--r-- | lib/sysxattrs.c | 301 | ||||
-rw-r--r-- | lib/sysxattrs.h | 26 | ||||
-rw-r--r-- | lib/wildmatch.c | 368 | ||||
-rw-r--r-- | lib/wildmatch.h | 6 |
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); |