diff options
88 files changed, 6777 insertions, 1209 deletions
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT index be4d50b..d9aa09c 100644 --- a/adb/SERVICES.TXT +++ b/adb/SERVICES.TXT @@ -17,8 +17,10 @@ host:kill upgrade. host:devices +host:devices-l Ask to return the list of available Android devices and their - state. After the OKAY, this is followed by a 4-byte hex len, + state. devices-l includes the device paths in the state. + After the OKAY, this is followed by a 4-byte hex len, and a string that will be dumped as-is by the client, then the connection is closed @@ -88,6 +90,9 @@ host:<request> Returns the serial number of the corresponding device/emulator. Note that emulator serial numbers are of the form "emulator-5554" +<host-prefix>:get-devpath + Returns the device path of the corresponding device/emulator. + <host-prefix>:get-state Returns the state of a given device as a string. @@ -21,6 +21,7 @@ #include <ctype.h> #include <stdarg.h> #include <errno.h> +#include <stddef.h> #include <string.h> #include <time.h> #include <sys/time.h> @@ -28,6 +29,8 @@ #include "sysdeps.h" #include "adb.h" +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + #if !ADB_HOST #include <private/android_filesystem_config.h> #include <linux/capability.h> @@ -42,7 +45,9 @@ ADB_MUTEX_DEFINE( D_lock ); int HOST = 0; +#if !ADB_HOST static const char *adb_device_banner = "device"; +#endif void fatal(const char *fmt, ...) { @@ -269,6 +274,36 @@ static void send_close(unsigned local, unsigned remote, atransport *t) send_packet(p, t); } +static size_t fill_connect_data(char *buf, size_t bufsize) +{ +#if ADB_HOST + return snprintf(buf, bufsize, "host::") + 1; +#else + static const char *cnxn_props[] = { + "ro.product.name", + "ro.product.model", + "ro.product.device", + }; + static const int num_cnxn_props = ARRAY_SIZE(cnxn_props); + int i; + size_t remaining = bufsize; + size_t len; + + len = snprintf(buf, remaining, "%s::", adb_device_banner); + remaining -= len; + buf += len; + for (i = 0; i < num_cnxn_props; i++) { + char value[PROPERTY_VALUE_MAX]; + property_get(cnxn_props[i], value, ""); + len = snprintf(buf, remaining, "%s=%s;", cnxn_props[i], value); + remaining -= len; + buf += len; + } + + return bufsize - remaining + 1; +#endif +} + static void send_connect(atransport *t) { D("Calling send_connect \n"); @@ -276,9 +311,8 @@ static void send_connect(atransport *t) cp->msg.command = A_CNXN; cp->msg.arg0 = A_VERSION; cp->msg.arg1 = MAX_PAYLOAD; - snprintf((char*) cp->data, sizeof cp->data, "%s::", - HOST ? "host" : adb_device_banner); - cp->msg.data_length = strlen((char*) cp->data) + 1; + cp->msg.data_length = fill_connect_data((char *)cp->data, + sizeof(cp->data)); send_packet(cp, t); #if ADB_HOST /* XXX why sleep here? */ @@ -305,29 +339,56 @@ static char *connection_state_name(atransport *t) } } +/* qual_overwrite is used to overwrite a qualifier string. dst is a + * pointer to a char pointer. It is assumed that if *dst is non-NULL, it + * was malloc'ed and needs to freed. *dst will be set to a dup of src. + */ +static void qual_overwrite(char **dst, const char *src) +{ + if (!dst) + return; + + free(*dst); + *dst = NULL; + + if (!src || !*src) + return; + + *dst = strdup(src); +} + void parse_banner(char *banner, atransport *t) { - char *type, *product, *end; + static const char *prop_seps = ";"; + static const char key_val_sep = '='; + char *cp; + char *type; D("parse_banner: %s\n", banner); type = banner; - product = strchr(type, ':'); - if(product) { - *product++ = 0; - } else { - product = ""; - } - - /* remove trailing ':' */ - end = strchr(product, ':'); - if(end) *end = 0; - - /* save product name in device structure */ - if (t->product == NULL) { - t->product = strdup(product); - } else if (strcmp(product, t->product) != 0) { - free(t->product); - t->product = strdup(product); + cp = strchr(type, ':'); + if (cp) { + *cp++ = 0; + /* Nothing is done with second field. */ + cp = strchr(cp, ':'); + if (cp) { + char *save; + char *key; + key = adb_strtok_r(cp + 1, prop_seps, &save); + while (key) { + cp = strchr(key, key_val_sep); + if (cp) { + *cp++ = '\0'; + if (!strcmp(key, "ro.product.name")) + qual_overwrite(&t->product, cp); + else if (!strcmp(key, "ro.product.model")) + qual_overwrite(&t->model, cp); + else if (!strcmp(key, "ro.product.device")) + qual_overwrite(&t->device, cp); + } + key = adb_strtok_r(NULL, prop_seps, &save); + } + } } if(!strcmp(type, "bootloader")){ @@ -1014,24 +1075,29 @@ int adb_main(int is_daemon, int server_port) } } - /* for the device, start the usb transport if the - ** android usb device exists and the "service.adb.tcp.port" and - ** "persist.adb.tcp.port" properties are not set. - ** Otherwise start the network transport. - */ + int usb = 0; + if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) { + // listen on USB + usb_init(); + usb = 1; + } + + // If one of these properties is set, also listen on that port + // If one of the properties isn't set and we couldn't listen on usb, + // listen on the default port. property_get("service.adb.tcp.port", value, ""); - if (!value[0]) + if (!value[0]) { property_get("persist.adb.tcp.port", value, ""); + } if (sscanf(value, "%d", &port) == 1 && port > 0) { + printf("using port=%d\n", port); // listen on TCP port specified by service.adb.tcp.port property local_init(port); - } else if (access("/dev/android_adb", F_OK) == 0) { - // listen on USB - usb_init(); - } else { + } else if (!usb) { // listen on default port local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); } + D("adb_main(): pre init_jdwp()\n"); init_jdwp(); D("adb_main(): post init_jdwp()\n"); @@ -1067,7 +1133,7 @@ void connect_device(char* host, char* buffer, int buffer_size) strncpy(hostbuf, host, sizeof(hostbuf) - 1); if (portstr) { - if ((unsigned int)(portstr - host) >= sizeof(hostbuf)) { + if (portstr - host >= (ptrdiff_t)sizeof(hostbuf)) { snprintf(buffer, buffer_size, "bad host name %s", host); return; } @@ -1201,16 +1267,19 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } // return a list of all connected devices - if (!strcmp(service, "devices")) { + if (!strncmp(service, "devices", 7)) { char buffer[4096]; - memset(buf, 0, sizeof(buf)); - memset(buffer, 0, sizeof(buffer)); - D("Getting device list \n"); - list_transports(buffer, sizeof(buffer)); - snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); - D("Wrote device list \n"); - writex(reply_fd, buf, strlen(buf)); - return 0; + int use_long = !strcmp(service+7, "-l"); + if (use_long || service[7] == 0) { + memset(buf, 0, sizeof(buf)); + memset(buffer, 0, sizeof(buffer)); + D("Getting device list \n"); + list_transports(buffer, sizeof(buffer), use_long); + snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); + D("Wrote device list \n"); + writex(reply_fd, buf, strlen(buf)); + return 0; + } } // add a new TCP transport, device or emulator @@ -1276,6 +1345,16 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r writex(reply_fd, buf, strlen(buf)); return 0; } + if(!strncmp(service,"get-devpath",strlen("get-devpath"))) { + char *out = "unknown"; + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + if (transport && transport->devpath) { + out = transport->devpath; + } + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out); + writex(reply_fd, buf, strlen(buf)); + return 0; + } // indicates a new emulator instance has started if (!strncmp(service,"emulator:",9)) { int port = atoi(service+9); @@ -190,6 +190,9 @@ struct atransport /* used to identify transports for clients */ char *serial; char *product; + char *model; + char *device; + char *devpath; int adb_port; // Use for emulators (local transport) /* a list of adisconnect callbacks called when the transport is kicked */ @@ -253,7 +256,7 @@ int adb_main(int is_daemon, int server_port); ** get_device_transport does an acquire on your behalf before returning */ void init_transport_registration(void); -int list_transports(char *buf, size_t bufsize); +int list_transports(char *buf, size_t bufsize, int long_listing); void update_transports(void); asocket* create_device_tracker(void); @@ -286,7 +289,7 @@ void register_socket_transport(int s, const char *serial, int port, int local); void unregister_transport(atransport *t); void unregister_all_tcp_transports(); -void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable); +void register_usb_transport(usb_handle *h, const char *serial, const char *devpath, unsigned writeable); /* this should only be used for transports with connection_state == CS_NOPERM */ void unregister_usb_transport(usb_handle *usb); @@ -461,6 +464,17 @@ extern int SHELL_EXIT_NOTIFY_FD; #define CHUNK_SIZE (64*1024) +#if !ADB_HOST +#define USB_ADB_PATH "/dev/android_adb" + +#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/" +#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x + +#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0) +#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1) +#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2) +#endif + int sendfailmsg(int fd, const char *reason); int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); diff --git a/adb/commandline.c b/adb/commandline.c index 47c9bec..10b2332 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -84,8 +84,8 @@ void help() " returns an error if more than one USB device is present.\n" " -e - directs command to the only running emulator.\n" " returns an error if more than one emulator is running.\n" - " -s <serial number> - directs command to the USB device or emulator with\n" - " the given serial number. Overrides ANDROID_SERIAL\n" + " -s <specific device> - directs command to the device or emulator with the given\n" + " serial number or qualifier. Overrides ANDROID_SERIAL\n" " environment variable.\n" " -p <product name or path> - simple product name like 'sooner', or\n" " a relative/absolute path to a product\n" @@ -93,7 +93,8 @@ void help() " If -p is not specified, the ANDROID_PRODUCT_OUT\n" " environment variable is used, which must\n" " be an absolute path.\n" - " devices - list all connected devices\n" + " devices [-l] - list all connected devices\n" + " ('-l' will also list device qualifiers)\n" " connect <host>[:<port>] - connect to a device via TCP/IP\n" " Port 5555 is used by default if no port number is specified.\n" " disconnect [<host>[:<port>]] - disconnect from a TCP/IP device.\n" @@ -159,6 +160,7 @@ void help() " adb kill-server - kill the server if it is running\n" " adb get-state - prints: offline | bootloader | device\n" " adb get-serialno - prints: <serial-number>\n" + " adb get-devpath - prints: <device-path>\n" " adb status-window - continuously print device status for a specified device\n" " adb remount - remounts the /system partition on the device read-write\n" " adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n" @@ -1016,7 +1018,16 @@ top: if(!strcmp(argv[0], "devices")) { char *tmp; - snprintf(buf, sizeof buf, "host:%s", argv[0]); + char *listopt; + if (argc < 2) + listopt = ""; + else if (argc == 2 && !strcmp(argv[1], "-l")) + listopt = argv[1]; + else { + fprintf(stderr, "Usage: adb devices [-l]\n"); + return 1; + } + snprintf(buf, sizeof buf, "host:%s%s", argv[0], listopt); tmp = adb_query(buf); if(tmp) { printf("List of devices attached \n"); @@ -1298,7 +1309,8 @@ top: /* passthrough commands */ if(!strcmp(argv[0],"get-state") || - !strcmp(argv[0],"get-serialno")) + !strcmp(argv[0],"get-serialno") || + !strcmp(argv[0],"get-devpath")) { char *tmp; diff --git a/adb/protocol.txt b/adb/protocol.txt index 398d042..abd63f9 100644 --- a/adb/protocol.txt +++ b/adb/protocol.txt @@ -72,7 +72,7 @@ large maxdata value, the connection with the other side must be closed. The system identity string should be "<systemtype>:<serialno>:<banner>" where systemtype is "bootloader", "device", or "host", serialno is some kind of unique ID (or empty), and banner is a human-readable version -or identifier string (informational only). +or identifier string. The banner is used to transmit useful properties. --- OPEN(local-id, 0, "destination") ----------------------------------- diff --git a/adb/sockets.c b/adb/sockets.c index cd31b23..305cb44 100644 --- a/adb/sockets.c +++ b/adb/sockets.c @@ -608,12 +608,30 @@ unsigned unhex(unsigned char *s, int len) return n; } +#define PREFIX(str) { str, sizeof(str) - 1 } +static const struct prefix_struct { + const char *str; + const size_t len; +} prefixes[] = { + PREFIX("usb:"), + PREFIX("product:"), + PREFIX("model:"), + PREFIX("device:"), +}; +static const int num_prefixes = (sizeof(prefixes) / sizeof(prefixes[0])); + /* skip_host_serial return the position in a string skipping over the 'serial' parameter in the ADB protocol, where parameter string may be a host:port string containing the protocol delimiter (colon). */ char *skip_host_serial(char *service) { char *first_colon, *serial_end; + int i; + + for (i = 0; i < num_prefixes; i++) { + if (!strncmp(service, prefixes[i].str, prefixes[i].len)) + return strchr(service + prefixes[i].len, ':'); + } first_colon = strchr(service, ':'); if (!first_colon) { diff --git a/adb/sysdeps.h b/adb/sysdeps.h index b518076..605fa17 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -254,6 +254,8 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) return isalpha(path[0]) && path[1] == ':' && path[2] == '\\'; } +extern char* adb_strtok_r(char *str, const char *delim, char **saveptr); + #else /* !_WIN32 a.k.a. Unix */ #include "fdevent.h" @@ -490,6 +492,13 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) return path[0] == '/'; } +static __inline__ char* adb_strtok_r(char *str, const char *delim, char **saveptr) +{ + return strtok_r(str, delim, saveptr); +} +#undef strtok_r +#define strtok_r ___xxx_strtok_r + #endif /* !_WIN32 */ #endif /* _ADB_SYSDEPS_H */ diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c index c426718..d41c42c 100644 --- a/adb/sysdeps_win32.c +++ b/adb/sysdeps_win32.c @@ -2140,3 +2140,81 @@ adb_sysdeps_init( void ) InitializeCriticalSection( &_win32_lock ); } +/* Windows doesn't have strtok_r. Use the one from bionic. */ + +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +char * +adb_strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + + if (s == NULL && (s = *last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} diff --git a/adb/transport.c b/adb/transport.c index 2f7bd27..9fd6cc2 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -370,7 +370,7 @@ static int list_transports_msg(char* buffer, size_t bufferlen) char head[5]; int len; - len = list_transports(buffer+4, bufferlen-4); + len = list_transports(buffer+4, bufferlen-4, 0); snprintf(head, sizeof(head), "%04x", len); memcpy(buffer, head, 4); len += 4; @@ -601,6 +601,12 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) free(t->product); if (t->serial) free(t->serial); + if (t->model) + free(t->model); + if (t->device) + free(t->device); + if (t->devpath) + free(t->devpath); memset(t,0xee,sizeof(atransport)); free(t); @@ -737,6 +743,45 @@ void remove_transport_disconnect(atransport* t, adisconnect* dis) dis->next = dis->prev = dis; } +static int qual_char_is_invalid(char ch) +{ + if ('A' <= ch && ch <= 'Z') + return 0; + if ('a' <= ch && ch <= 'z') + return 0; + if ('0' <= ch && ch <= '9') + return 0; + return 1; +} + +static int qual_match(const char *to_test, + const char *prefix, const char *qual, int sanitize_qual) +{ + if (!to_test || !*to_test) + /* Return true if both the qual and to_test are null strings. */ + return !qual || !*qual; + + if (!qual) + return 0; + + if (prefix) { + while (*prefix) { + if (*prefix++ != *to_test++) + return 0; + } + } + + while (*qual) { + char ch = *qual++; + if (sanitize_qual && qual_char_is_invalid(ch)) + ch = '_'; + if (ch != *to_test++) + return 0; + } + + /* Everything matched so far. Return true if *to_test is a NUL. */ + return !*to_test; +} atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out) { @@ -758,9 +803,19 @@ retry: /* check for matching serial number */ if (serial) { - if (t->serial && !strcmp(serial, t->serial)) { + if ((t->serial && !strcmp(serial, t->serial)) || + (t->devpath && !strcmp(serial, t->devpath)) || + qual_match(serial, "product:", t->product, 0) || + qual_match(serial, "model:", t->model, 1) || + qual_match(serial, "device:", t->device, 0)) { + if (result) { + if (error_out) + *error_out = "more than one device"; + ambiguous = 1; + result = NULL; + break; + } result = t; - break; } } else { if (ttype == kTransportUsb && t->type == kTransportUsb) { @@ -837,7 +892,58 @@ static const char *statename(atransport *t) } } -int list_transports(char *buf, size_t bufsize) +static void add_qual(char **buf, size_t *buf_size, + const char *prefix, const char *qual, int sanitize_qual) +{ + size_t len; + int prefix_len; + + if (!buf || !*buf || !buf_size || !*buf_size || !qual || !*qual) + return; + + len = snprintf(*buf, *buf_size, "%s%n%s", prefix, &prefix_len, qual); + + if (sanitize_qual) { + char *cp; + for (cp = *buf + prefix_len; cp < *buf + len; cp++) { + if (qual_char_is_invalid(*cp)) + *cp = '_'; + } + } + + *buf_size -= len; + *buf += len; +} + +static size_t format_transport(atransport *t, char *buf, size_t bufsize, + int long_listing) +{ + const char* serial = t->serial; + if (!serial || !serial[0]) + serial = "????????????"; + + if (!long_listing) { + return snprintf(buf, bufsize, "%s\t%s\n", serial, statename(t)); + } else { + size_t len, remaining = bufsize; + + len = snprintf(buf, remaining, "%-22s %s", serial, statename(t)); + remaining -= len; + buf += len; + + add_qual(&buf, &remaining, " ", t->devpath, 0); + add_qual(&buf, &remaining, " product:", t->product, 0); + add_qual(&buf, &remaining, " model:", t->model, 1); + add_qual(&buf, &remaining, " device:", t->device, 0); + + len = snprintf(buf, remaining, "\n"); + remaining -= len; + + return bufsize - remaining; + } +} + +int list_transports(char *buf, size_t bufsize, int long_listing) { char* p = buf; char* end = buf + bufsize; @@ -847,11 +953,7 @@ int list_transports(char *buf, size_t bufsize) /* XXX OVERRUN PROBLEMS XXX */ adb_mutex_lock(&transport_lock); for(t = transport_list.next; t != &transport_list; t = t->next) { - const char* serial = t->serial; - if (!serial || !serial[0]) - serial = "????????????"; - len = snprintf(p, end - p, "%s\t%s\n", serial, statename(t)); - + len = format_transport(t, p, end - p, long_listing); if (p + len >= end) { /* discard last line if buffer is too short */ break; @@ -956,7 +1058,7 @@ void unregister_all_tcp_transports() #endif -void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable) +void register_usb_transport(usb_handle *usb, const char *serial, const char *devpath, unsigned writeable) { atransport *t = calloc(1, sizeof(atransport)); D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb, @@ -965,6 +1067,9 @@ void register_usb_transport(usb_handle *usb, const char *serial, unsigned writea if(serial) { t->serial = strdup(serial); } + if(devpath) { + t->devpath = strdup(devpath); + } register_transport(t); } diff --git a/adb/usb_libusb.c b/adb/usb_libusb.c index 8c75266..06ff5dc 100644 --- a/adb/usb_libusb.c +++ b/adb/usb_libusb.c @@ -347,7 +347,7 @@ register_device(struct usb_handle *uh, const char *serial) adb_mutex_unlock(&usb_lock); - register_usb_transport(usb, serial, 1); + register_usb_transport(usb, serial, NULL, 1); return (1); } diff --git a/adb/usb_linux.c b/adb/usb_linux.c index 4d55b74..7bf2057 100644 --- a/adb/usb_linux.c +++ b/adb/usb_linux.c @@ -116,7 +116,8 @@ static void kick_disconnected_devices() } -static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out, +static void register_device(const char *dev_name, const char *devpath, + unsigned char ep_in, unsigned char ep_out, int ifc, int serial_index, unsigned zero_mask); static inline int badname(const char *name) @@ -129,7 +130,7 @@ static inline int badname(const char *name) static void find_usb_device(const char *base, void (*register_device_callback) - (const char *, unsigned char, unsigned char, int, int, unsigned)) + (const char *, const char *, unsigned char, unsigned char, int, int, unsigned)) { char busname[32], devname[32]; unsigned char local_ep_in, local_ep_out; @@ -227,6 +228,11 @@ static void find_usb_device(const char *base, is_adb_interface(vid, pid, interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol)) { + struct stat st; + char pathbuf[128]; + char link[256]; + char *devpath = NULL; + DBGX("looking for bulk endpoints\n"); // looks like ADB... ep1 = (struct usb_endpoint_descriptor *)bufptr; @@ -263,7 +269,26 @@ static void find_usb_device(const char *base, local_ep_out = ep1->bEndpointAddress; } - register_device_callback(devname, local_ep_in, local_ep_out, + // Determine the device path + if (!fstat(fd, &st) && S_ISCHR(st.st_mode)) { + char *slash; + ssize_t link_len; + snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d", + major(st.st_rdev), minor(st.st_rdev)); + link_len = readlink(pathbuf, link, sizeof(link) - 1); + if (link_len > 0) { + link[link_len] = '\0'; + slash = strrchr(link, '/'); + if (slash) { + snprintf(pathbuf, sizeof(pathbuf), + "usb:%s", slash + 1); + devpath = pathbuf; + } + } + } + + register_device_callback(devname, devpath, + local_ep_in, local_ep_out, interface->bInterfaceNumber, device->iSerialNumber, zero_mask); break; } @@ -532,7 +557,7 @@ int usb_close(usb_handle *h) return 0; } -static void register_device(const char *dev_name, +static void register_device(const char *dev_name, const char *devpath, unsigned char ep_in, unsigned char ep_out, int interface, int serial_index, unsigned zero_mask) { @@ -644,7 +669,7 @@ static void register_device(const char *dev_name, usb->next->prev = usb; adb_mutex_unlock(&usb_lock); - register_usb_transport(usb, serial, usb->writeable); + register_usb_transport(usb, serial, devpath, usb->writeable); return; fail: diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c index 635fa4b..fb1dad0 100644 --- a/adb/usb_linux_client.c +++ b/adb/usb_linux_client.c @@ -19,6 +19,8 @@ #include <unistd.h> #include <string.h> +#include <linux/usb/ch9.h> +#include <linux/usb/functionfs.h> #include <sys/ioctl.h> #include <sys/types.h> #include <dirent.h> @@ -29,20 +31,122 @@ #define TRACE_TAG TRACE_USB #include "adb.h" +#define MAX_PACKET_SIZE_FS 64 +#define MAX_PACKET_SIZE_HS 512 + +#define cpu_to_le16(x) htole16(x) +#define cpu_to_le32(x) htole32(x) struct usb_handle { - int fd; adb_cond_t notify; adb_mutex_t lock; + + int (*write)(usb_handle *h, const void *data, int len); + int (*read)(usb_handle *h, void *data, int len); + void (*kick)(usb_handle *h); + + // Legacy f_adb + int fd; + + // FunctionFS + int control; + int bulk_out; /* "out" from the host's perspective => source for adbd */ + int bulk_in; /* "in" from the host's perspective => sink for adbd */ +}; + +static const struct { + struct usb_functionfs_descs_head header; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio source; + struct usb_endpoint_descriptor_no_audio sink; + } __attribute__((packed)) fs_descs, hs_descs; +} __attribute__((packed)) descriptors = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), + .length = cpu_to_le32(sizeof(descriptors)), + .fs_count = 3, + .hs_count = 3, + }, + .fs_descs = { + .intf = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = ADB_CLASS, + .bInterfaceSubClass = ADB_SUBCLASS, + .bInterfaceProtocol = ADB_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.fs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + .sink = { + .bLength = sizeof(descriptors.fs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + }, + .hs_descs = { + .intf = { + .bLength = sizeof(descriptors.hs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = ADB_CLASS, + .bInterfaceSubClass = ADB_SUBCLASS, + .bInterfaceProtocol = ADB_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.hs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + .sink = { + .bLength = sizeof(descriptors.hs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + }, +}; + +#define STR_INTERFACE_ "ADB Interface" + +static const struct { + struct usb_functionfs_strings_head header; + struct { + __le16 code; + const char str1[sizeof(STR_INTERFACE_)]; + } __attribute__((packed)) lang0; +} __attribute__((packed)) strings = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), + .length = cpu_to_le32(sizeof(strings)), + .str_count = cpu_to_le32(1), + .lang_count = cpu_to_le32(1), + }, + .lang0 = { + cpu_to_le16(0x0409), /* en-us */ + STR_INTERFACE_, + }, }; -void usb_cleanup() -{ - // nothing to do here -} -static void *usb_open_thread(void *x) + +static void *usb_adb_open_thread(void *x) { struct usb_handle *usb = (struct usb_handle *)x; int fd; @@ -72,14 +176,14 @@ static void *usb_open_thread(void *x) usb->fd = fd; D("[ usb_thread - registering device ]\n"); - register_usb_transport(usb, 0, 1); + register_usb_transport(usb, 0, 0, 1); } // never gets here return 0; } -int usb_write(usb_handle *h, const void *data, int len) +static int usb_adb_write(usb_handle *h, const void *data, int len) { int n; @@ -94,7 +198,7 @@ int usb_write(usb_handle *h, const void *data, int len) return 0; } -int usb_read(usb_handle *h, void *data, int len) +static int usb_adb_read(usb_handle *h, void *data, int len) { int n; @@ -109,14 +213,31 @@ int usb_read(usb_handle *h, void *data, int len) return 0; } -void usb_init() +static void usb_adb_kick(usb_handle *h) +{ + D("usb_kick\n"); + adb_mutex_lock(&h->lock); + adb_close(h->fd); + h->fd = -1; + + // notify usb_adb_open_thread that we are disconnected + adb_cond_signal(&h->notify); + adb_mutex_unlock(&h->lock); +} + +static void usb_adb_init() { usb_handle *h; adb_thread_t tid; int fd; h = calloc(1, sizeof(usb_handle)); + + h->write = usb_adb_write; + h->read = usb_adb_read; + h->kick = usb_adb_kick; h->fd = -1; + adb_cond_init(&h->notify, 0); adb_mutex_init(&h->lock, 0); @@ -133,25 +254,239 @@ void usb_init() } D("[ usb_init - starting thread ]\n"); - if(adb_thread_create(&tid, usb_open_thread, h)){ + if(adb_thread_create(&tid, usb_adb_open_thread, h)){ fatal_errno("cannot create usb thread"); } } -void usb_kick(usb_handle *h) + +static void init_functionfs(struct usb_handle *h) { - D("usb_kick\n"); + ssize_t ret; + + D("OPENING %s\n", USB_FFS_ADB_EP0); + h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR); + if (h->control < 0) { + D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + ret = adb_write(h->control, &descriptors, sizeof(descriptors)); + if (ret < 0) { + D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + ret = adb_write(h->control, &strings, sizeof(strings)); + if (ret < 0) { + D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR); + if (h->bulk_out < 0) { + D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno); + goto err; + } + + h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR); + if (h->bulk_in < 0) { + D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno); + goto err; + } + + return; + +err: + if (h->bulk_in > 0) { + adb_close(h->bulk_in); + h->bulk_in = -1; + } + if (h->bulk_out > 0) { + adb_close(h->bulk_out); + h->bulk_out = -1; + } + if (h->control > 0) { + adb_close(h->control); + h->control = -1; + } + return; +} + +static void *usb_ffs_open_thread(void *x) +{ + struct usb_handle *usb = (struct usb_handle *)x; + + while (1) { + // wait until the USB device needs opening + adb_mutex_lock(&usb->lock); + while (usb->control != -1) + adb_cond_wait(&usb->notify, &usb->lock); + adb_mutex_unlock(&usb->lock); + + while (1) { + init_functionfs(usb); + + if (usb->control >= 0) + break; + + adb_sleep_ms(1000); + } + + D("[ usb_thread - registering device ]\n"); + register_usb_transport(usb, 0, 0, 1); + } + + // never gets here + return 0; +} + +static int bulk_write(int bulk_in, const char *buf, size_t length) +{ + size_t count = 0; + int ret; + + do { + ret = adb_write(bulk_in, buf + count, length - count); + if (ret < 0) { + if (errno != EINTR) + return ret; + } else { + count += ret; + } + } while (count < length); + + D("[ bulk_write done fd=%d ]\n", bulk_in); + return count; +} + +static int usb_ffs_write(usb_handle *h, const void *data, int len) +{ + int n; + + D("about to write (fd=%d, len=%d)\n", h->bulk_in, len); + n = bulk_write(h->bulk_in, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_in, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->bulk_in); + return 0; +} + +static int bulk_read(int bulk_out, char *buf, size_t length) +{ + size_t count = 0; + int ret; + + do { + ret = adb_read(bulk_out, buf + count, length - count); + if (ret < 0) { + if (errno != EINTR) { + D("[ bulk_read failed fd=%d length=%d count=%d ]\n", + bulk_out, length, count); + return ret; + } + } else { + count += ret; + } + } while (count < length); + + return count; +} + +static int usb_ffs_read(usb_handle *h, void *data, int len) +{ + int n; + + D("about to read (fd=%d, len=%d)\n", h->bulk_out, len); + n = bulk_read(h->bulk_out, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_out, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->bulk_out); + return 0; +} + +static void usb_ffs_kick(usb_handle *h) +{ + int err; + + err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno); + + err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno); + adb_mutex_lock(&h->lock); - adb_close(h->fd); - h->fd = -1; + adb_close(h->control); + adb_close(h->bulk_out); + adb_close(h->bulk_in); + h->control = h->bulk_out = h->bulk_in = -1; - // notify usb_open_thread that we are disconnected + // notify usb_ffs_open_thread that we are disconnected adb_cond_signal(&h->notify); adb_mutex_unlock(&h->lock); } +static void usb_ffs_init() +{ + usb_handle *h; + adb_thread_t tid; + + D("[ usb_init - using FunctionFS ]\n"); + + h = calloc(1, sizeof(usb_handle)); + + h->write = usb_ffs_write; + h->read = usb_ffs_read; + h->kick = usb_ffs_kick; + + h->control = -1; + h->bulk_out = -1; + h->bulk_out = -1; + + adb_cond_init(&h->notify, 0); + adb_mutex_init(&h->lock, 0); + + D("[ usb_init - starting thread ]\n"); + if (adb_thread_create(&tid, usb_ffs_open_thread, h)){ + fatal_errno("[ cannot create usb thread ]\n"); + } +} + +void usb_init() +{ + if (access(USB_FFS_ADB_EP0, F_OK) == 0) + usb_ffs_init(); + else + usb_adb_init(); +} + +void usb_cleanup() +{ +} + +int usb_write(usb_handle *h, const void *data, int len) +{ + return h->write(h, data, len); +} + +int usb_read(usb_handle *h, void *data, int len) +{ + return h->read(h, data, len); +} int usb_close(usb_handle *h) { - // nothing to do here return 0; } + +void usb_kick(usb_handle *h) +{ + h->kick(h); +} diff --git a/adb/usb_osx.c b/adb/usb_osx.c index 00d02da..45ce444 100644 --- a/adb/usb_osx.c +++ b/adb/usb_osx.c @@ -125,10 +125,13 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) IOUSBDeviceInterface197 **dev = NULL; HRESULT result; SInt32 score; + UInt32 locationId; UInt16 vendor; UInt16 product; UInt8 serialIndex; char serial[256]; + char devpathBuf[64]; + char *devpath = NULL; while ((usbInterface = IOIteratorNext(iterator))) { //* Create an intermediate interface plugin @@ -192,6 +195,11 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) kr = (*dev)->GetDeviceVendor(dev, &vendor); kr = (*dev)->GetDeviceProduct(dev, &product); + kr = (*dev)->GetLocationID(dev, &locationId); + if (kr == 0) { + snprintf(devpathBuf, sizeof(devpathBuf), "usb:%lX", locationId); + devpath = devpathBuf; + } kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); if (serialIndex > 0) { @@ -256,7 +264,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) } DBG("AndroidDeviceAdded calling register_usb_transport\n"); - register_usb_transport(handle, (serial[0] ? serial : NULL), 1); + register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1); // Register for an interest notification of this device being removed. // Pass the reference to our private data as the refCon for the diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index b988115..d4ddce4 100644 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -127,6 +127,8 @@ #define VENDOR_ID_LAB126 0x1949 // Yulong Coolpad's USB Vendor ID #define VENDOR_ID_YULONG_COOLPAD 0x1EBF +// Kobo's USB Vendor ID +#define VENDOR_ID_KOBO 0x2237 /** built-in vendor list */ int builtInVendorIds[] = { @@ -176,6 +178,7 @@ int builtInVendorIds[] = { VENDOR_ID_SONY, VENDOR_ID_LAB126, VENDOR_ID_YULONG_COOLPAD, + VENDOR_ID_KOBO, }; #define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) diff --git a/adb/usb_windows.c b/adb/usb_windows.c index 251bf17..4936b77 100644 --- a/adb/usb_windows.c +++ b/adb/usb_windows.c @@ -490,7 +490,7 @@ void find_devices() { true)) { // Lets make sure that we don't duplicate this device if (register_new_device(handle)) { - register_usb_transport(handle, serial_number, 1); + register_usb_transport(handle, serial_number, NULL, 1); } else { D("register_new_device failed for %s\n", interf_name); usb_cleanup_handle(handle); diff --git a/fastboot/engine.c b/fastboot/engine.c index 93be3de..8d7b68a 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -565,6 +565,8 @@ int fb_execute_queue(usb_handle *usb) int status = 0; a = action_list; + if (!a) + return status; resp[FB_RESPONSE_SZ] = 0; double start = -1; @@ -605,3 +607,8 @@ int fb_execute_queue(usb_handle *usb) fprintf(stderr,"finished. total time: %.3fs\n", (now() - start)); return status; } + +int fb_queue_is_empty(void) +{ + return (action_list == NULL); +} diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index 57363d8..2a63905 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -70,6 +70,7 @@ static const char *product = 0; static const char *cmdline = 0; static int wipe_data = 0; static unsigned short vendor_id = 0; +static int long_listing = 0; static int64_t sparse_limit = -1; static int64_t target_sparse_limit = -1; @@ -184,6 +185,11 @@ oops: int match_fastboot(usb_ifc_info *info) { + return match_fastboot_with_serial(info, serial); +} + +int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial) +{ if(!(vendor_id && (info->dev_vendor == vendor_id)) && (info->dev_vendor != 0x18d1) && // Google (info->dev_vendor != 0x8087) && // Intel @@ -201,15 +207,16 @@ int match_fastboot(usb_ifc_info *info) if(info->ifc_class != 0xff) return -1; if(info->ifc_subclass != 0x42) return -1; if(info->ifc_protocol != 0x03) return -1; - // require matching serial number if a serial number is specified + // require matching serial number or device path if requested // at the command line with the -s option. - if (serial && strcmp(serial, info->serial_number) != 0) return -1; + if (local_serial && (strcmp(local_serial, info->serial_number) != 0 && + strcmp(local_serial, info->device_path) != 0)) return -1; return 0; } int list_devices_callback(usb_ifc_info *info) { - if (match_fastboot(info) == 0) { + if (match_fastboot_with_serial(info, NULL) == 0) { char* serial = info->serial_number; if (!info->writable) { serial = "no permissions"; // like "adb devices" @@ -218,7 +225,13 @@ int list_devices_callback(usb_ifc_info *info) serial = "????????????"; } // output compatible with "adb devices" - printf("%s\tfastboot\n", serial); + if (!long_listing) { + printf("%s\tfastboot\n", serial); + } else if (!info->device_path) { + printf("%-22s fastboot\n", serial); + } else { + printf("%-22s fastboot %s\n", serial, info->device_path); + } } return -1; @@ -272,7 +285,9 @@ void usage(void) "\n" "options:\n" " -w erase userdata and cache\n" - " -s <serial number> specify device serial number\n" + " -s <specific device> specify device serial number\n" + " or path to device port\n" + " -l with \"devices\", lists device paths\n" " -p <product> specify product name\n" " -c <cmdline> override kernel commandline\n" " -i <vendor id> specify a custom USB vendor id\n" @@ -764,7 +779,7 @@ int main(int argc, char **argv) serial = getenv("ANDROID_SERIAL"); while (1) { - c = getopt_long(argc, argv, "wb:n:s:S:p:c:i:m:h", &longopts, NULL); + c = getopt_long(argc, argv, "wb:n:s:S:lp:c:i:m:h", &longopts, NULL); if (c < 0) { break; } @@ -789,6 +804,9 @@ int main(int argc, char **argv) die("invalid sparse limit"); } break; + case 'l': + long_listing = 1; + break; case 'p': product = optarg; break; @@ -940,6 +958,9 @@ int main(int argc, char **argv) fb_queue_command("reboot-bootloader", "rebooting into bootloader"); } + if (fb_queue_is_empty()) + return 0; + status = fb_execute_queue(usb); return (status) ? 1 : 0; } diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 9177932..b4cf195 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -58,6 +58,7 @@ void fb_queue_command(const char *cmd, const char *msg); void fb_queue_download(const char *name, void *data, unsigned size); void fb_queue_notice(const char *notice); int fb_execute_queue(usb_handle *usb); +int fb_queue_is_empty(void); /* util stuff */ void die(const char *fmt, ...); diff --git a/fastboot/usb.h b/fastboot/usb.h index df9efde..d504ee2 100644 --- a/fastboot/usb.h +++ b/fastboot/usb.h @@ -53,6 +53,7 @@ struct usb_ifc_info unsigned char writable; char serial_number[256]; + char device_path[256]; }; typedef int (*ifc_match_func)(usb_ifc_info *ifc); diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index 83c6de9..b7a9ca3 100644 --- a/fastboot/usb_linux.c +++ b/fastboot/usb_linux.c @@ -32,6 +32,7 @@ #include <string.h> #include <sys/ioctl.h> +#include <sys/stat.h> #include <sys/types.h> #include <dirent.h> #include <fcntl.h> @@ -107,6 +108,9 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, int in, out; unsigned i; unsigned e; + + struct stat st; + int result; if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE)) return -1; @@ -134,7 +138,6 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, // Keep it short enough because some bootloaders are borked if the URB len is > 255 // 128 is too big by 1. __u16 buffer[127]; - int result; memset(buffer, 0, sizeof(buffer)); @@ -158,6 +161,42 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, } } + /* We need to get a path that represents a particular port on a particular + * hub. We are passed an fd that was obtained by opening an entry under + * /dev/bus/usb. Unfortunately, the names of those entries change each + * time devices are plugged and unplugged. So how to get a repeatable + * path? udevadm provided the inspiration. We can get the major and + * minor of the device file, read the symlink that can be found here: + * /sys/dev/char/<major>:<minor> + * and then use the last element of that path. As a concrete example, I + * have an Android device at /dev/bus/usb/001/027 so working with bash: + * $ ls -l /dev/bus/usb/001/027 + * crw-rw-r-- 1 root plugdev 189, 26 Apr 9 11:03 /dev/bus/usb/001/027 + * $ ls -l /sys/dev/char/189:26 + * lrwxrwxrwx 1 root root 0 Apr 9 11:03 /sys/dev/char/189:26 -> + * ../../devices/pci0000:00/0000:00:1a.7/usb1/1-4/1-4.2/1-4.2.3 + * So our device_path would be 1-4.2.3 which says my device is connected + * to port 3 of a hub on port 2 of a hub on port 4 of bus 1 (per + * http://www.linux-usb.org/FAQ.html). + */ + info.device_path[0] = '\0'; + result = fstat(fd, &st); + if (!result && S_ISCHR(st.st_mode)) { + char cdev[128]; + char link[256]; + char *slash; + ssize_t link_len; + snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d", + major(st.st_rdev), minor(st.st_rdev)); + link_len = readlink(cdev, link, sizeof(link) - 1); + if (link_len > 0) { + link[link_len] = '\0'; + slash = strrchr(link, '/'); + if (slash) + snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1); + } + } + for(i = 0; i < cfg->bNumInterfaces; i++) { if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE)) return -1; diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c index cbce9bd..1548ba8 100644 --- a/fastboot/usb_osx.c +++ b/fastboot/usb_osx.c @@ -264,6 +264,7 @@ static int try_device(io_service_t device, usb_handle *handle) { SInt32 score; HRESULT result; UInt8 serialIndex; + UInt32 locationId; // Create an intermediate plugin. kr = IOCreatePlugInInterfaceForService(device, @@ -322,6 +323,13 @@ static int try_device(io_service_t device, usb_handle *handle) { goto error; } + kr = (*dev)->GetLocationID(dev, &locationId); + if (kr != 0) { + ERR("GetLocationId"); + goto error; + } + snprintf(handle->info.device_path, sizeof(handle->info.device_path), "usb:%lX", locationId); + kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); if (serialIndex > 0) { diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c index 99027cc..7aa36b2 100644 --- a/fastboot/usb_windows.c +++ b/fastboot/usb_windows.c @@ -311,6 +311,8 @@ int recognized_device(usb_handle* handle, ifc_match_func callback) { info.serial_number[0] = 0; } + info.device_path[0] = 0; + if (callback(&info) == 0) { return 1; } diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 60aed42..538b5be 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -58,6 +58,12 @@ static struct flag_list mount_flags[] = { { "ro", MS_RDONLY }, { "rw", 0 }, { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, { "defaults", 0 }, { 0, 0 }, }; diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h index 48f8d9a..9da01c5 100644 --- a/include/arch/darwin-x86/AndroidConfig.h +++ b/include/arch/darwin-x86/AndroidConfig.h @@ -175,7 +175,7 @@ * with a memory address. If not defined, stack crawls will not have symbolic * information. */ -#define HAVE_DLADDR 0 +#define HAVE_DLADDR 1 /* * Defined if we have the cxxabi.h header for demangling C++ symbols. If diff --git a/include/corkscrew/map_info.h b/include/corkscrew/map_info.h index c5cd8f8..ea1d35f 100644 --- a/include/corkscrew/map_info.h +++ b/include/corkscrew/map_info.h @@ -21,6 +21,7 @@ #include <sys/types.h> #include <stdbool.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { diff --git a/include/corkscrew/ptrace.h b/include/corkscrew/ptrace.h index 9e0da78..76276d8 100644 --- a/include/corkscrew/ptrace.h +++ b/include/corkscrew/ptrace.h @@ -24,6 +24,7 @@ #include <sys/types.h> #include <stdbool.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { diff --git a/include/corkscrew/symbol_table.h b/include/corkscrew/symbol_table.h index 020c8b8..4998750 100644 --- a/include/corkscrew/symbol_table.h +++ b/include/corkscrew/symbol_table.h @@ -17,6 +17,7 @@ #ifndef _CORKSCREW_SYMBOL_TABLE_H #define _CORKSCREW_SYMBOL_TABLE_H +#include <stdint.h> #include <sys/types.h> #ifdef __cplusplus diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h new file mode 100644 index 0000000..22bb032 --- /dev/null +++ b/include/cutils/multiuser.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_MULTIUSER_H +#define __CUTILS_MULTIUSER_H + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// NOTE: keep in sync with android.os.UserId + +#define MULTIUSER_APP_PER_USER_RANGE 100000 + +typedef uid_t userid_t; +typedef uid_t appid_t; + +extern userid_t getUserId(uid_t uid); +extern appid_t getAppId(uid_t uid); +extern uid_t getUid(userid_t userId, appid_t appId); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_MULTIUSER_H */ diff --git a/include/ion/ion.h b/include/ion/ion.h index cafead5..78a6e2e 100644 --- a/include/ion/ion.h +++ b/include/ion/ion.h @@ -27,8 +27,10 @@ __BEGIN_DECLS int ion_open(); int ion_close(int fd); -int ion_alloc(int fd, size_t len, size_t align, unsigned int flags, - struct ion_handle **handle); +int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, struct ion_handle **handle); +int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, int *handle_fd); int ion_free(int fd, struct ion_handle *handle); int ion_map(int fd, struct ion_handle *handle, size_t length, int prot, int flags, off_t offset, unsigned char **ptr, int *map_fd); diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h index 7d7d571..d7429fc 100644 --- a/include/mincrypt/rsa.h +++ b/include/mincrypt/rsa.h @@ -13,14 +13,14 @@ ** be used to endorse or promote products derived from this software ** without specific prior written permission. ** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO ** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -42,6 +42,7 @@ typedef struct RSAPublicKey { uint32_t n0inv; /* -1 / n[0] mod 2^32 */ uint32_t n[RSANUMWORDS]; /* modulus as little endian array */ uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */ + int exponent; /* 3 or 65537 */ } RSAPublicKey; int RSA_verify(const RSAPublicKey *key, diff --git a/include/netutils/ifc.h b/include/netutils/ifc.h index 67a4a45..1f5421d 100644 --- a/include/netutils/ifc.h +++ b/include/netutils/ifc.h @@ -34,6 +34,9 @@ extern int ifc_down(const char *name); extern int ifc_enable(const char *ifname); extern int ifc_disable(const char *ifname); +#define RESET_IPV4_ADDRESSES 0x01 +#define RESET_IPV6_ADDRESSES 0x02 +#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES) extern int ifc_reset_connections(const char *ifname, const int reset_mask); extern int ifc_get_addr(const char *name, in_addr_t *addr); @@ -66,6 +69,8 @@ extern int ifc_configure(const char *ifname, in_addr_t address, uint32_t prefixLength, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2); +extern in_addr_t prefixLengthToIpv4Netmask(int prefix_length); + __END_DECLS #endif /* _NETUTILS_IFC_H_ */ diff --git a/include/sync/sync.h b/include/sync/sync.h index 6aa5f2d..f015fa1 100644 --- a/include/sync/sync.h +++ b/include/sync/sync.h @@ -19,11 +19,28 @@ #ifndef __SYS_CORE_SYNC_H #define __SYS_CORE_SYNC_H -#include <linux/sync.h> -#include <linux/sw_sync.h> +#include <sys/cdefs.h> +#include <stdint.h> __BEGIN_DECLS +// XXX: These structs are copied from the header "linux/sync.h". +struct sync_fence_info_data { + uint32_t len; + char name[32]; + int32_t status; + uint8_t pt_info[0]; +}; + +struct sync_pt_info { + uint32_t len; + char obj_name[32]; + char driver_name[32]; + int32_t status; + uint64_t timestamp_ns; + uint8_t driver_data[0]; +}; + /* timeout in msecs */ int sync_wait(int fd, unsigned int timeout); int sync_merge(const char *name, int fd1, int fd2); diff --git a/include/system/audio.h b/include/system/audio.h index 3807317..e6cd30b 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -152,7 +152,7 @@ typedef enum { AUDIO_FORMAT_PCM_SUB_8_24_BIT), } audio_format_t; -typedef enum { +enum { /* output channels */ AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, @@ -275,7 +275,7 @@ typedef enum { AUDIO_IN_ACOUSTICS_TX_DISABLE = 0, } audio_in_acoustics_t; -typedef enum { +enum { /* output devices */ AUDIO_DEVICE_OUT_EARPIECE = 0x1, AUDIO_DEVICE_OUT_SPEAKER = 0x2, @@ -339,7 +339,9 @@ typedef enum { AUDIO_DEVICE_IN_BACK_MIC | AUDIO_DEVICE_IN_DEFAULT), AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, -} audio_devices_t; +}; + +typedef uint32_t audio_devices_t; /* the audio output flags serve two purposes: * - when an AudioTrack is created they indicate a "wish" to be connected to an diff --git a/include/system/camera.h b/include/system/camera.h index e4cacc5..7a4dd53 100644 --- a/include/system/camera.h +++ b/include/system/camera.h @@ -163,6 +163,17 @@ enum { * can silently finish itself or show a dialog. */ CAMERA_CMD_PING = 9, + + /** + * Configure the number of video buffers used for recording. The intended + * video buffer count for recording is passed as arg1, which must be + * greater than 0. This command must be sent before recording is started. + * This command returns INVALID_OPERATION error if it is sent after video + * recording is started, or the command is not supported at all. This + * command also returns a BAD_VALUE error if the intended video buffer + * count is non-positive or too big to be realized. + */ + CAMERA_CMD_SET_VIDEO_BUFFER_COUNT = 10, }; /** camera fatal errors */ diff --git a/include/system/graphics.h b/include/system/graphics.h index 24e2bfb..e0ad257 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -109,6 +109,21 @@ enum { */ HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, + /* + * Android binary blob graphics buffer format: + * + * This format is used to carry task-specific data which does not have a + * standard image structure. The details of the format are left to the two + * endpoints. + * + * A typical use case is for transporting JPEG-compressed images from the + * Camera HAL to the framework or to applications. + * + * Buffers of this format must have a height of 1, and width equal to their + * size in bytes. + */ + HAL_PIXEL_FORMAT_BLOB = 0x21, + /* Legacy formats (deprecated), used by ImageFormat.java */ HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16 HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21 diff --git a/include/system/window.h b/include/system/window.h index 8e00bcd..685e068 100644 --- a/include/system/window.h +++ b/include/system/window.h @@ -17,11 +17,14 @@ #ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H #define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H +#include <cutils/native_handle.h> +#include <limits.h> #include <stdint.h> #include <string.h> +#include <sync/sync.h> #include <sys/cdefs.h> #include <system/graphics.h> -#include <cutils/native_handle.h> +#include <unistd.h> __BEGIN_DECLS @@ -38,6 +41,14 @@ __BEGIN_DECLS // --------------------------------------------------------------------------- +// This #define may be used to conditionally compile device-specific code to +// support either the prior ANativeWindow interface, which did not pass libsync +// fences around, or the new interface that does. This #define is only present +// when the ANativeWindow interface does include libsync support. +#define ANDROID_NATIVE_WINDOW_HAS_SYNC 1 + +// --------------------------------------------------------------------------- + typedef const native_handle_t* buffer_handle_t; // --------------------------------------------------------------------------- @@ -379,8 +390,12 @@ struct ANativeWindow * allowed if a specific buffer count has been set. * * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new dequeueBuffer function that + * outputs a fence file descriptor should be used in its place. */ - int (*dequeueBuffer)(struct ANativeWindow* window, + int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer** buffer); /* @@ -389,9 +404,14 @@ struct ANativeWindow * dequeueBuffer first. * * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but it is essentially a no-op, and calls + * to it should be removed. */ - int (*lockBuffer)(struct ANativeWindow* window, + int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); + /* * Hook called by EGL when modifications to the render buffer are done. * This unlocks and post the buffer. @@ -405,8 +425,13 @@ struct ANativeWindow * Buffers MUST be queued in the same order than they were dequeued. * * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new queueBuffer function that + * takes a fence file descriptor should be used in its place (pass a value + * of -1 for the fence file descriptor if there is no valid one to pass). */ - int (*queueBuffer)(struct ANativeWindow* window, + int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); /* @@ -463,12 +488,86 @@ struct ANativeWindow * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new cancelBuffer function that + * takes a fence file descriptor should be used in its place (pass a value + * of -1 for the fence file descriptor if there is no valid one to pass). */ - int (*cancelBuffer)(struct ANativeWindow* window, + int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); + /* + * Hook called by EGL to acquire a buffer. This call may block if no + * buffers are available. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The libsync fence file descriptor returned in the int pointed to by the + * fenceFd argument will refer to the fence that must signal before the + * dequeued buffer may be written to. A value of -1 indicates that the + * caller may access the buffer immediately without waiting on a fence. If + * a valid file descriptor is returned (i.e. any value except -1) then the + * caller is responsible for closing the file descriptor. + * + * Returns 0 on success or -errno on error. + */ + int (*dequeueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer** buffer, int* fenceFd); + + /* + * Hook called by EGL when modifications to the render buffer are done. + * This unlocks and post the buffer. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file descriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. The + * caller must not use the file descriptor after it is passed to + * queueBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ + int (*queueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer, int fenceFd); - void* reserved_proc[2]; + /* + * Hook used to cancel a buffer that has been dequeued. + * No synchronization is performed between dequeue() and cancel(), so + * either external synchronization is needed, or these functions must be + * called from the same thread. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file decsriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. + * + * Note that if the client has not waited on the fence that was returned + * from dequeueBuffer, that same fence should be passed to cancelBuffer to + * ensure that future uses of the buffer are preceded by a wait on that + * fence. The caller must not use the file descriptor after it is passed + * to cancelBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ + int (*cancelBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer, int fenceFd); }; /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C). @@ -551,7 +650,7 @@ static inline int native_window_set_post_transform_crop( /* * native_window_set_active_rect(..., active_rect) * - * This function is deprectated and will be removed soon. For now it simply + * This function is deprecated and will be removed soon. For now it simply * sets the post-transform crop for compatibility while multi-project commits * get checked. */ @@ -717,6 +816,28 @@ static inline int native_window_api_disconnect( return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api); } +/* + * native_window_dequeue_buffer_and_wait(...) + * Dequeue a buffer and wait on the fence associated with that buffer. The + * buffer may safely be accessed immediately upon this function returning. An + * error is returned if either of the dequeue or the wait operations fail. + */ +static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw, + struct ANativeWindowBuffer** anb) { + int fenceFd = -1; + int err = anw->dequeueBuffer(anw, anb, &fenceFd); + if (err == 0 && fenceFd != -1) { + err = sync_wait(fenceFd, UINT_MAX); + if (err == 0) { + close(fenceFd); + } else { + anw->cancelBuffer(anw, *anb, fenceFd); + *anb = NULL; + } + } + return err; +} + __END_DECLS diff --git a/init/Android.mk b/init/Android.mk index 7dae9df..a1c1e7a 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -15,7 +15,8 @@ LOCAL_SRC_FILES:= \ signal_handler.c \ init_parser.c \ ueventd.c \ - ueventd_parser.c + ueventd_parser.c \ + watchdogd.c ifeq ($(strip $(INIT_BOOTCHART)),true) LOCAL_SRC_FILES += bootchart.c @@ -42,8 +43,11 @@ endif include $(BUILD_EXECUTABLE) -# Make a symlink from /sbin/ueventd to /init -SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd +# Make a symlink from /sbin/ueventd and /sbin/watchdogd to /init +SYMLINKS := \ + $(TARGET_ROOT_OUT)/sbin/ueventd \ + $(TARGET_ROOT_OUT)/sbin/watchdogd + $(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE) $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk @echo "Symlink: $@ -> ../$(INIT_BINARY)" diff --git a/init/builtins.c b/init/builtins.c index 5bda7a0..bb963c1 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -322,6 +322,14 @@ int do_mkdir(int nargs, char **args) if (_chown(args[1], uid, gid) < 0) { return -errno; } + + /* chown may have cleared S_ISUID and S_ISGID, chmod again */ + if (mode & (S_ISUID | S_ISGID)) { + ret = _chmod(args[1], mode); + if (ret == -1) { + return -errno; + } + } } return 0; @@ -339,6 +347,12 @@ static struct { { "ro", MS_RDONLY }, { "rw", 0 }, { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, { "defaults", 0 }, { 0, 0 }, }; diff --git a/init/devices.c b/init/devices.c index e43dbaf..c30303f 100644 --- a/init/devices.c +++ b/init/devices.c @@ -51,6 +51,7 @@ #define SYSFS_PREFIX "/sys" #define FIRMWARE_DIR1 "/etc/firmware" #define FIRMWARE_DIR2 "/vendor/firmware" +#define FIRMWARE_DIR3 "/firmware/image" #ifdef HAVE_SELINUX extern struct selabel_handle *sehandle; @@ -638,7 +639,7 @@ static void handle_generic_device_event(struct uevent *uevent) static void handle_device_event(struct uevent *uevent) { - if (!strcmp(uevent->action,"add")) + if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change")) fixup_sys_perms(uevent->path); if (!strncmp(uevent->subsystem, "block", 5)) { @@ -703,7 +704,7 @@ static int is_booting(void) static void process_firmware_event(struct uevent *uevent) { - char *root, *loading, *data, *file1 = NULL, *file2 = NULL; + char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL; int l, loading_fd, data_fd, fw_fd; int booting = is_booting(); @@ -730,6 +731,10 @@ static void process_firmware_event(struct uevent *uevent) if (l == -1) goto data_free_out; + l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware); + if (l == -1) + goto data_free_out; + loading_fd = open(loading, O_WRONLY); if(loading_fd < 0) goto file_free_out; @@ -743,17 +748,20 @@ try_loading_again: if(fw_fd < 0) { fw_fd = open(file2, O_RDONLY); if (fw_fd < 0) { - if (booting) { - /* If we're not fully booted, we may be missing - * filesystems needed for firmware, wait and retry. - */ - usleep(100000); - booting = is_booting(); - goto try_loading_again; + fw_fd = open(file3, O_RDONLY); + if (fw_fd < 0) { + if (booting) { + /* If we're not fully booted, we may be missing + * filesystems needed for firmware, wait and retry. + */ + usleep(100000); + booting = is_booting(); + goto try_loading_again; + } + INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); + write(loading_fd, "-1", 2); + goto data_close_out; } - INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); - write(loading_fd, "-1", 2); - goto data_close_out; } } diff --git a/init/init.c b/init/init.c index b2e39bd..6127fd3 100755 --- a/init/init.c +++ b/init/init.c @@ -58,6 +58,7 @@ #include "init_parser.h" #include "util.h" #include "ueventd.h" +#include "watchdogd.h" #ifdef HAVE_SELINUX struct selabel_handle *sehandle; @@ -831,6 +832,9 @@ int main(int argc, char **argv) if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); + if (!strcmp(basename(argv[0]), "watchdogd")) + return watchdogd_main(argc, argv); + /* clear the umask */ umask(0); diff --git a/init/property_service.c b/init/property_service.c index 5017375..c378aeb 100644..100755 --- a/init/property_service.c +++ b/init/property_service.c @@ -81,6 +81,7 @@ struct { { "sys.", AID_SYSTEM, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, + { "bluetooth.", AID_BLUETOOTH, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, { "debug.", AID_SYSTEM, 0 }, @@ -91,6 +92,7 @@ struct { { "persist.sys.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, { "persist.security.", AID_SYSTEM, 0 }, + { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, { "selinux." , AID_SYSTEM, 0 }, { NULL, 0, 0 } }; diff --git a/init/ueventd.c b/init/ueventd.c index a89e067..a41c31e 100644 --- a/init/ueventd.c +++ b/init/ueventd.c @@ -105,7 +105,7 @@ static int get_android_id(const char *id) for (i = 0; i < ARRAY_SIZE(android_ids); i++) if (!strcmp(id, android_ids[i].name)) return android_ids[i].aid; - return 0; + return -1; } void set_device_permission(int nargs, char **args) diff --git a/init/watchdogd.c b/init/watchdogd.c new file mode 100644 index 0000000..fb53836 --- /dev/null +++ b/init/watchdogd.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include <linux/watchdog.h> + +#include "log.h" +#include "util.h" + +#define DEV_NAME "/dev/watchdog" + +int watchdogd_main(int argc, char **argv) +{ + int fd; + int ret; + int interval = 10; + int margin = 10; + int timeout; + + open_devnull_stdio(); + klog_init(); + + INFO("Starting watchdogd\n"); + + if (argc >= 2) + interval = atoi(argv[1]); + + if (argc >= 3) + margin = atoi(argv[2]); + + timeout = interval + margin; + + fd = open(DEV_NAME, O_RDWR); + if (fd < 0) { + ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno)); + return 1; + } + + ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout); + if (ret) { + ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno)); + ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout); + if (ret) { + ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno)); + } else { + if (timeout > margin) + interval = timeout - margin; + else + interval = 1; + ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n", + timeout, interval, margin); + } + } + + while(1) { + write(fd, "", 1); + sleep(interval); + } +} + diff --git a/init/watchdogd.h b/init/watchdogd.h new file mode 100644 index 0000000..8b48ab8 --- /dev/null +++ b/init/watchdogd.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _INIT_WATCHDOGD_H_ +#define _INIT_WATCHDOGD_H_ + +int watchdogd_main(int argc, char **argv); + +#endif diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk index aace5f8..2786d8f 100644 --- a/libcorkscrew/Android.mk +++ b/libcorkscrew/Android.mk @@ -14,9 +14,7 @@ LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ +generic_src_files := \ backtrace.c \ backtrace-helper.c \ demangle.c \ @@ -24,16 +22,24 @@ LOCAL_SRC_FILES := \ ptrace.c \ symbol_table.c -ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += \ +arm_src_files := \ arch-arm/backtrace-arm.c \ arch-arm/ptrace-arm.c + +x86_src_files := \ + arch-x86/backtrace-x86.c \ + arch-x86/ptrace-x86.c + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(generic_src_files) + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += $(arm_src_files) LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH endif ifeq ($(TARGET_ARCH),x86) -LOCAL_SRC_FILES += \ - arch-x86/backtrace-x86.c \ - arch-x86/ptrace-x86.c +LOCAL_SRC_FILES += $(x86_src_files) LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH endif ifeq ($(TARGET_ARCH),mips) @@ -50,3 +56,38 @@ LOCAL_MODULE := libcorkscrew LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) + +# Build test. +include $(CLEAR_VARS) +LOCAL_SRC_FILES := test.c +LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions +LOCAL_SHARED_LIBRARIES := libcorkscrew +LOCAL_MODULE := libcorkscrew_test +LOCAL_MODULE_TAGS := optional +include $(BUILD_EXECUTABLE) + + +ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86) + +# Build libcorkscrew. +include $(CLEAR_VARS) +LOCAL_SRC_FILES += $(generic_src_files) $(x86_src_files) +LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH +LOCAL_SHARED_LIBRARIES += libgccdemangle +LOCAL_STATIC_LIBRARIES += libcutils +LOCAL_LDLIBS += -ldl -lrt +LOCAL_CFLAGS += -std=gnu99 -Werror +LOCAL_MODULE := libcorkscrew +LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_SHARED_LIBRARY) + +# Build test. +include $(CLEAR_VARS) +LOCAL_SRC_FILES := test.c +LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions +LOCAL_SHARED_LIBRARIES := libcorkscrew +LOCAL_MODULE := libcorkscrew_test +LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_EXECUTABLE) + +endif # linux-x86 diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c index 849029f..6cdb0c8 100644 --- a/libcorkscrew/arch-x86/backtrace-x86.c +++ b/libcorkscrew/arch-x86/backtrace-x86.c @@ -31,40 +31,22 @@ #include <limits.h> #include <errno.h> #include <sys/ptrace.h> -#include <sys/exec_elf.h> #include <cutils/log.h> -/* Machine context at the time a signal was raised. */ -typedef struct ucontext { - uint32_t uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - struct sigcontext { - uint32_t gs; - uint32_t fs; - uint32_t es; - uint32_t ds; - uint32_t edi; - uint32_t esi; - uint32_t ebp; - uint32_t esp; - uint32_t ebx; - uint32_t edx; - uint32_t ecx; - uint32_t eax; - uint32_t trapno; - uint32_t err; - uint32_t eip; - uint32_t cs; - uint32_t efl; - uint32_t uesp; - uint32_t ss; - void* fpregs; - uint32_t oldmask; - uint32_t cr2; - } uc_mcontext; - uint32_t uc_sigmask; -} ucontext_t; +#if defined(__BIONIC__) + +// Bionic offers the Linux kernel headers. +#include <asm/sigcontext.h> +#include <asm/ucontext.h> +typedef struct ucontext ucontext_t; + +#else + +// glibc has its own renaming of the Linux kernel's structures. +#define __USE_GNU // For REG_EBP, REG_ESP, and REG_EIP. +#include <ucontext.h> + +#endif /* Unwind state. */ typedef struct { @@ -114,9 +96,15 @@ ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), const ucontext_t* uc = (const ucontext_t*)sigcontext; unwind_state_t state; +#if defined(__BIONIC__) state.ebp = uc->uc_mcontext.ebp; - state.eip = uc->uc_mcontext.eip; state.esp = uc->uc_mcontext.esp; + state.eip = uc->uc_mcontext.eip; +#else + state.ebp = uc->uc_mcontext.gregs[REG_EBP]; + state.esp = uc->uc_mcontext.gregs[REG_ESP]; + state.eip = uc->uc_mcontext.gregs[REG_EIP]; +#endif memory_t memory; init_memory(&memory, map_info_list); diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c index e57f428..3697d18 100644 --- a/libcorkscrew/backtrace.c +++ b/libcorkscrew/backtrace.c @@ -27,16 +27,41 @@ #include <unistd.h> #include <signal.h> +#include <stdlib.h> +#include <string.h> #include <pthread.h> #include <unwind.h> -#include <sys/exec_elf.h> #include <cutils/log.h> #include <cutils/atomic.h> +#include <elf.h> #if HAVE_DLADDR +#define __USE_GNU // For dladdr(3) in glibc. #include <dlfcn.h> #endif +#if defined(__BIONIC__) + +// Bionic implements and exports gettid but only implements tgkill. +extern int tgkill(int tgid, int tid, int sig); + +#else + +// glibc doesn't implement or export either gettid or tgkill. + +#include <unistd.h> +#include <sys/syscall.h> + +static pid_t gettid() { + return syscall(__NR_gettid); +} + +static int tgkill(int tgid, int tid, int sig) { + return syscall(__NR_tgkill, tgid, tid, sig); +} + +#endif + typedef struct { backtrace_frame_t* backtrace; size_t ignore_depth; @@ -115,8 +140,6 @@ static void unwind_backtrace_thread_signal_handler(int n __attribute__((unused)) } #endif -extern int tgkill(int tgid, int tid, int sig); - ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { if (tid == gettid()) { diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c index f33378f..3c52854 100644 --- a/libcorkscrew/map_info.c +++ b/libcorkscrew/map_info.c @@ -21,6 +21,7 @@ #include <ctype.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <limits.h> #include <pthread.h> diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c index cbea8ca..6496d5e 100644 --- a/libcorkscrew/ptrace.c +++ b/libcorkscrew/ptrace.c @@ -21,6 +21,7 @@ #include <corkscrew/ptrace.h> #include <errno.h> +#include <stdlib.h> #include <sys/ptrace.h> #include <cutils/log.h> diff --git a/libcorkscrew/symbol_table.c b/libcorkscrew/symbol_table.c index 1b97180..29e4a79 100644 --- a/libcorkscrew/symbol_table.c +++ b/libcorkscrew/symbol_table.c @@ -19,14 +19,22 @@ #include <corkscrew/symbol_table.h> +#include <stdbool.h> #include <stdlib.h> +#include <elf.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <sys/mman.h> -#include <sys/exec_elf.h> #include <cutils/log.h> +static bool is_elf(Elf32_Ehdr* e) { + return (e->e_ident[EI_MAG0] == ELFMAG0 && + e->e_ident[EI_MAG1] == ELFMAG1 && + e->e_ident[EI_MAG2] == ELFMAG2 && + e->e_ident[EI_MAG3] == ELFMAG3); +} + // Compare function for qsort static int qcompar(const void *a, const void *b) { const symbol_t* asym = (const symbol_t*)a; @@ -67,7 +75,7 @@ symbol_table_t* load_symbol_table(const char *filename) { // Parse the file header Elf32_Ehdr *hdr = (Elf32_Ehdr*)base; - if (!IS_ELF(*hdr)) { + if (!is_elf(hdr)) { goto out_close; } Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff); diff --git a/libcorkscrew/test.c b/libcorkscrew/test.c new file mode 100644 index 0000000..af34c03 --- /dev/null +++ b/libcorkscrew/test.c @@ -0,0 +1,64 @@ +#include <corkscrew/backtrace.h> +#include <corkscrew/symbol_table.h> +#include <stdio.h> +#include <stdlib.h> + +void do_backtrace() { + const size_t MAX_DEPTH = 32; + backtrace_frame_t* frames = (backtrace_frame_t*) malloc(sizeof(backtrace_frame_t) * MAX_DEPTH); + ssize_t frame_count = unwind_backtrace(frames, 0, MAX_DEPTH); + fprintf(stderr, "frame_count=%d\n", (int) frame_count); + + backtrace_symbol_t* backtrace_symbols = (backtrace_symbol_t*) malloc(sizeof(backtrace_symbol_t) * frame_count); + get_backtrace_symbols(frames, frame_count, backtrace_symbols); + + for (size_t i = 0; i < (size_t) frame_count; ++i) { + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &frames[i], &backtrace_symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + if (backtrace_symbols[i].symbol_name != NULL) { + // get_backtrace_symbols found the symbol's name with dladdr(3). + fprintf(stderr, " %s\n", line); + } else { + // We don't have a symbol. Maybe this is a static symbol, and + // we can look it up? + symbol_table_t* symbols = NULL; + if (backtrace_symbols[i].map_name != NULL) { + symbols = load_symbol_table(backtrace_symbols[i].map_name); + } + const symbol_t* symbol = NULL; + if (symbols != NULL) { + symbol = find_symbol(symbols, frames[i].absolute_pc); + } + if (symbol != NULL) { + uintptr_t offset = frames[i].absolute_pc - symbol->start; + fprintf(stderr, " %s (%s%+d)\n", line, symbol->name, offset); + } else { + fprintf(stderr, " %s (\?\?\?)\n", line); + } + free_symbol_table(symbols); + } + } + + free_backtrace_symbols(backtrace_symbols, frame_count); + free(backtrace_symbols); + free(frames); +} + +__attribute__ ((noinline)) void g() { + fprintf(stderr, "g()\n"); + do_backtrace(); +} + +__attribute__ ((noinline)) int f(int i) { + fprintf(stderr, "f(%i)\n", i); + if (i == 0) { + g(); + return 0; + } + return f(i - 1); +} + +int main() { + return f(5); +} diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 35b5b46..8684e8f 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -124,7 +124,8 @@ LOCAL_SRC_FILES := $(commonSources) \ mq.c \ partition_utils.c \ qtaguid.c \ - uevent.c + uevent.c \ + multiuser.c ifeq ($(TARGET_ARCH),arm) LOCAL_SRC_FILES += arch-arm/memset32.S diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c new file mode 100644 index 0000000..be9304d --- /dev/null +++ b/libcutils/multiuser.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <cutils/multiuser.h> + +userid_t getUserId(uid_t uid) { + return uid / MULTIUSER_APP_PER_USER_RANGE; +} + +appid_t getAppId(uid_t uid) { + return uid % MULTIUSER_APP_PER_USER_RANGE; +} + +uid_t getUid(userid_t userId, appid_t appId) { + return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE); +} diff --git a/libion/ion.c b/libion/ion.c index dbeac23..164cec9 100644 --- a/libion/ion.c +++ b/libion/ion.c @@ -54,13 +54,14 @@ static int ion_ioctl(int fd, int req, void *arg) return ret; } -int ion_alloc(int fd, size_t len, size_t align, unsigned int flags, - struct ion_handle **handle) +int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, struct ion_handle **handle) { int ret; struct ion_allocation_data data = { .len = len, .align = align, + .heap_mask = heap_mask, .flags = flags, }; @@ -120,6 +121,19 @@ int ion_share(int fd, struct ion_handle *handle, int *share_fd) return ret; } +int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, int *handle_fd) { + struct ion_handle *handle; + int ret; + + ret = ion_alloc(fd, len, align, heap_mask, flags, &handle); + if (ret < 0) + return ret; + ret = ion_share(fd, handle, handle_fd); + ion_free(fd, handle); + return ret; +} + int ion_import(int fd, int share_fd, struct ion_handle **handle) { struct ion_fd_data data = { diff --git a/libion/ion_test.c b/libion/ion_test.c index 3f2d7cc..0caaa2a 100644 --- a/libion/ion_test.c +++ b/libion/ion_test.c @@ -19,8 +19,8 @@ size_t len = 1024*1024, align = 0; int prot = PROT_READ | PROT_WRITE; int map_flags = MAP_SHARED; int alloc_flags = 0; +int heap_mask = 1; int test = -1; -size_t width = 1024*1024, height = 1024*1024; size_t stride; int _ion_alloc_test(int *fd, struct ion_handle **handle) @@ -31,7 +31,7 @@ int _ion_alloc_test(int *fd, struct ion_handle **handle) if (*fd < 0) return *fd; - ret = ion_alloc(*fd, len, align, alloc_flags, handle); + ret = ion_alloc(*fd, len, align, heap_mask, alloc_flags, handle); if (ret) printf("%s failed: %s\n", __func__, strerror(ret)); @@ -203,17 +203,16 @@ int main(int argc, char* argv[]) { static struct option opts[] = { {"alloc", no_argument, 0, 'a'}, {"alloc_flags", required_argument, 0, 'f'}, + {"heap_mask", required_argument, 0, 'h'}, {"map", no_argument, 0, 'm'}, {"share", no_argument, 0, 's'}, {"len", required_argument, 0, 'l'}, {"align", required_argument, 0, 'g'}, {"map_flags", required_argument, 0, 'z'}, {"prot", required_argument, 0, 'p'}, - {"width", required_argument, 0, 'w'}, - {"height", required_argument, 0, 'h'}, }; int i = 0; - c = getopt_long(argc, argv, "af:h:l:mr:stw:", opts, &i); + c = getopt_long(argc, argv, "af:h:l:mr:st", opts, &i); if (c == -1) break; @@ -245,6 +244,9 @@ int main(int argc, char* argv[]) { case 'f': alloc_flags = atol(optarg); break; + case 'h': + heap_mask = atol(optarg); + break; case 'a': test = ALLOC_TEST; break; @@ -254,17 +256,11 @@ int main(int argc, char* argv[]) { case 's': test = SHARE_TEST; break; - case 'w': - width = atol(optarg); - break; - case 'h': - height = atol(optarg); - break; } } - printf("test %d, len %u, width %u, height %u align %u, " - "map_flags %d, prot %d, alloc_flags %d\n", test, len, width, - height, align, map_flags, prot, alloc_flags); + printf("test %d, len %u, align %u, map_flags %d, prot %d, heap_mask %d," + " alloc_flags %d\n", test, len, align, map_flags, prot, + heap_mask, alloc_flags); switch (test) { case ALLOC_TEST: ion_alloc_test(); diff --git a/liblog/logd_write.c b/liblog/logd_write.c index a0a753b..b91de52 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -141,6 +141,7 @@ int __android_log_write(int prio, const char *tag, const char *msg) /* XXX: This needs to go! */ if (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || @@ -169,6 +170,7 @@ int __android_log_buf_write(int bufID, int prio, const char *tag, const char *ms /* XXX: This needs to go! */ if (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk index b09a941..f58eab9 100644 --- a/libmincrypt/Android.mk +++ b/libmincrypt/Android.mk @@ -4,13 +4,13 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c index d7124fb..b4ee6af 100644 --- a/libmincrypt/rsa.c +++ b/libmincrypt/rsa.c @@ -1,6 +1,6 @@ /* rsa.c ** -** Copyright 2008, The Android Open Source Project +** Copyright 2012, The Android Open Source Project ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: @@ -13,186 +13,42 @@ ** be used to endorse or promote products derived from this software ** without specific prior written permission. ** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO ** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mincrypt/rsa.h" -#include "mincrypt/sha.h" -/* a[] -= mod */ -static void subM(const RSAPublicKey *key, uint32_t *a) { - int64_t A = 0; - int i; - for (i = 0; i < key->len; ++i) { - A += (uint64_t)a[i] - key->n[i]; - a[i] = (uint32_t)A; - A >>= 32; - } -} - -/* return a[] >= mod */ -static int geM(const RSAPublicKey *key, const uint32_t *a) { - int i; - for (i = key->len; i;) { - --i; - if (a[i] < key->n[i]) return 0; - if (a[i] > key->n[i]) return 1; - } - return 1; /* equal */ -} - -/* montgomery c[] += a * b[] / R % mod */ -static void montMulAdd(const RSAPublicKey *key, - uint32_t* c, - const uint32_t a, - const uint32_t* b) { - uint64_t A = (uint64_t)a * b[0] + c[0]; - uint32_t d0 = (uint32_t)A * key->n0inv; - uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; - int i; - - for (i = 1; i < key->len; ++i) { - A = (A >> 32) + (uint64_t)a * b[i] + c[i]; - B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; - c[i - 1] = (uint32_t)B; - } - - A = (A >> 32) + (B >> 32); - - c[i - 1] = (uint32_t)A; - - if (A >> 32) { - subM(key, c); - } -} - -/* montgomery c[] = a[] * b[] / R % mod */ -static void montMul(const RSAPublicKey *key, - uint32_t* c, - const uint32_t* a, - const uint32_t* b) { - int i; - for (i = 0; i < key->len; ++i) { - c[i] = 0; - } - for (i = 0; i < key->len; ++i) { - montMulAdd(key, c, a[i], b); - } -} - -/* In-place public exponentiation. -** Input and output big-endian byte array in inout. -*/ -static void modpow3(const RSAPublicKey *key, - uint8_t* inout) { - uint32_t a[RSANUMWORDS]; - uint32_t aR[RSANUMWORDS]; - uint32_t aaR[RSANUMWORDS]; - uint32_t *aaa = aR; /* Re-use location. */ - int i; - - /* Convert from big endian byte array to little endian word array. */ - for (i = 0; i < key->len; ++i) { - uint32_t tmp = - (inout[((key->len - 1 - i) * 4) + 0] << 24) | - (inout[((key->len - 1 - i) * 4) + 1] << 16) | - (inout[((key->len - 1 - i) * 4) + 2] << 8) | - (inout[((key->len - 1 - i) * 4) + 3] << 0); - a[i] = tmp; - } +int RSA_e_f4_verify(const RSAPublicKey* key, + const uint8_t* signature, + const int len, + const uint8_t* sha); - montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ - montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ - montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ +int RSA_e_3_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha); - /* Make sure aaa < mod; aaa is at most 1x mod too large. */ - if (geM(key, aaa)) { - subM(key, aaa); - } - - /* Convert to bigendian byte array */ - for (i = key->len - 1; i >= 0; --i) { - uint32_t tmp = aaa[i]; - *inout++ = tmp >> 24; - *inout++ = tmp >> 16; - *inout++ = tmp >> 8; - *inout++ = tmp >> 0; - } -} - -/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. -** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the -** other flavor which omits the optional parameter entirely). This code does not -** accept signatures without the optional parameter. -*/ -static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { - 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, - 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, - 0x04,0x14 -}; - -/* Verify a 2048 bit RSA PKCS1.5 signature against an expected SHA-1 hash. -** Returns 0 on failure, 1 on success. -*/ int RSA_verify(const RSAPublicKey *key, const uint8_t *signature, const int len, const uint8_t *sha) { - uint8_t buf[RSANUMBYTES]; - int i; - - if (key->len != RSANUMWORDS) { - return 0; /* Wrong key passed in. */ - } - - if (len != sizeof(buf)) { - return 0; /* Wrong input length. */ - } - - for (i = 0; i < len; ++i) { - buf[i] = signature[i]; - } - - modpow3(key, buf); - - /* Check pkcs1.5 padding bytes. */ - for (i = 0; i < (int) sizeof(padding); ++i) { - if (buf[i] != padding[i]) { + switch (key->exponent) { + case 3: + return RSA_e_3_verify(key, signature, len, sha); + break; + case 65537: + return RSA_e_f4_verify(key, signature, len, sha); + break; + default: return 0; - } } - - /* Check sha digest matches. */ - for (; i < len; ++i) { - if (buf[i] != *sha++) { - return 0; - } - } - - return 1; } diff --git a/libmincrypt/rsa_e_3.c b/libmincrypt/rsa_e_3.c new file mode 100644 index 0000000..c8c02c4 --- /dev/null +++ b/libmincrypt/rsa_e_3.c @@ -0,0 +1,202 @@ +/* rsa_e_3.c +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +/* a[] -= mod */ +static void subM(const RSAPublicKey *key, uint32_t *a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +/* return a[] >= mod */ +static int geM(const RSAPublicKey *key, const uint32_t *a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; /* equal */ +} + +/* montgomery c[] += a * b[] / R % mod */ +static void montMulAdd(const RSAPublicKey *key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +/* montgomery c[] = a[] * b[] / R % mod */ +static void montMul(const RSAPublicKey *key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +/* In-place public exponentiation. +** Input and output big-endian byte array in inout. +*/ +static void modpow3(const RSAPublicKey *key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t *aaa = aR; /* Re-use location. */ + int i; + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ + + /* Make sure aaa < mod; aaa is at most 1x mod too large. */ + if (geM(key, aaa)) { + subM(key, aaa); + } + + /* Convert to bigendian byte array */ + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +** other flavor which omits the optional parameter entirely). This code does not +** accept signatures without the optional parameter. +*/ +static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { + 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, + 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, + 0x04,0x14 +}; + +/* Verify a 2048 bit RSA e=3 PKCS1.5 signature against an expected SHA-1 hash. +** Returns 0 on failure, 1 on success. +*/ +int RSA_e_3_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; /* Wrong key passed in. */ + } + + if (len != sizeof(buf)) { + return 0; /* Wrong input length. */ + } + + if (key->exponent != 3) { + return 0; // Wrong exponent. + } + + for (i = 0; i < len; ++i) { + buf[i] = signature[i]; + } + + modpow3(key, buf); + + /* Check pkcs1.5 padding bytes. */ + for (i = 0; i < (int) sizeof(padding); ++i) { + if (buf[i] != padding[i]) { + return 0; + } + } + + /* Check sha digest matches. */ + for (; i < len; ++i) { + if (buf[i] != *sha++) { + return 0; + } + } + + return 1; +} diff --git a/libmincrypt/rsa_e_f4.c b/libmincrypt/rsa_e_f4.c new file mode 100644 index 0000000..6701bcc --- /dev/null +++ b/libmincrypt/rsa_e_f4.c @@ -0,0 +1,196 @@ +/* rsa_e_f4.c +** +** Copyright 2012, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +// a[] -= mod +static void subM(const RSAPublicKey* key, + uint32_t* a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +// return a[] >= mod +static int geM(const RSAPublicKey* key, + const uint32_t* a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; // equal +} + +// montgomery c[] += a * b[] / R % mod +static void montMulAdd(const RSAPublicKey* key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +// montgomery c[] = a[] * b[] / R % mod +static void montMul(const RSAPublicKey* key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +// In-place public exponentiation. +// Input and output big-endian byte array in inout. +static void modpowF4(const RSAPublicKey* key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t* aaa = aaR; // Re-use location. + int i; + + // Convert from big endian byte array to little endian word array. + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); // aR = a * RR / R mod M + for (i = 0; i < 16; i += 2) { + montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M + montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M + } + montMul(key, aaa, aR, a); // aaa = aR * a / R mod M + + // Make sure aaa < mod; aaa is at most 1x mod too large. + if (geM(key, aaa)) { + subM(key, aaa); + } + + // Convert to bigendian byte array + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +// other flavor which omits the optional parameter entirely). This code does not +// accept signatures without the optional parameter. +/* +static const uint8_t padding[RSANUMBYTES] = { +0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; +*/ + +// SHA-1 of PKCS1.5 signature padding for 2048 bit, as above. +// At the location of the bytes of the hash all 00 are hashed. +static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = { + 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e, 0x6e, 0xfc, + 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68, 0x7c, 0xfb, 0xf1, 0x67 +}; + +// Verify a 2048 bit RSA e=65537 PKCS1.5 signature against an expected +// SHA-1 hash. Returns 0 on failure, 1 on success. +int RSA_e_f4_verify(const RSAPublicKey* key, + const uint8_t* signature, + const int len, + const uint8_t* sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; // Wrong key passed in. + } + + if (len != sizeof(buf)) { + return 0; // Wrong input length. + } + + if (key->exponent != 65537) { + return 0; // Wrong exponent. + } + + for (i = 0; i < len; ++i) { // Copy input to local workspace. + buf[i] = signature[i]; + } + + modpowF4(key, buf); // In-place exponentiation. + + // Xor sha portion, so it all becomes 00 iff equal. + for (i = len - SHA_DIGEST_SIZE; i < len; ++i) { + buf[i] ^= *sha++; + } + + // Hash resulting buf, in-place. + SHA(buf, len, buf); + + // Compare against expected hash value. + for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + if (buf[i] != kExpectedPadShaRsa2048[i]) { + return 0; + } + } + + return 1; // All checked out OK. +} diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java index d2935e0..b83a757 100644 --- a/libmincrypt/tools/DumpPublicKey.java +++ b/libmincrypt/tools/DumpPublicKey.java @@ -34,27 +34,42 @@ import sun.misc.BASE64Encoder; class DumpPublicKey { /** * @param key to perform sanity checks on + * @return version number of key. Supported versions are: + * 1: 2048-bit key with e=3 + * 2: 2048-bit key with e=65537 * @throws Exception if the key has the wrong size or public exponent + */ - static void check(RSAPublicKey key) throws Exception { + static int check(RSAPublicKey key) throws Exception { BigInteger pubexp = key.getPublicExponent(); BigInteger modulus = key.getModulus(); + int version; + + if (pubexp.equals(BigInteger.valueOf(3))) { + version = 1; + } else if (pubexp.equals(BigInteger.valueOf(65537))) { + version = 2; + } else { + throw new Exception("Public exponent should be 3 or 65537 but is " + + pubexp.toString(10) + "."); + } - if (!pubexp.equals(BigInteger.valueOf(3))) - throw new Exception("Public exponent should be 3 but is " + - pubexp.toString(10) + "."); - - if (modulus.bitLength() != 2048) + if (modulus.bitLength() != 2048) { throw new Exception("Modulus should be 2048 bits long but is " + modulus.bitLength() + " bits."); + } + + return version; } /** * @param key to output - * @return a C initializer representing this public key. + * @return a String representing this public key. If the key is a + * version 1 key, the string will be a C initializer; this is + * not true for newer key versions. */ static String print(RSAPublicKey key) throws Exception { - check(key); + int version = check(key); BigInteger N = key.getModulus(); @@ -62,6 +77,12 @@ class DumpPublicKey { int nwords = N.bitLength() / 32; // # of 32 bit integers in modulus + if (version > 1) { + result.append("v"); + result.append(Integer.toString(version)); + result.append(" "); + } + result.append("{"); result.append(nwords); diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c index 903a1db..d0ca90a 100644 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c @@ -29,6 +29,7 @@ static const char DAEMON_NAME[] = "dhcpcd"; static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd"; static const char HOSTNAME_PROP_NAME[] = "net.hostname"; static const char DHCP_PROP_NAME_PREFIX[] = "dhcp"; +static const char DHCP_CONFIG_PATH[] = "/system/etc/dhcpcd/dhcpcd.conf"; static const int NAP_TIME = 200; /* wait for 200ms at a time */ /* when polling for property values */ static const char DAEMON_NAME_RENEW[] = "iprenew"; @@ -170,7 +171,7 @@ static const char *ipaddr_to_string(in_addr_t addr) * The device init.rc file needs a corresponding entry for this work. * * Example: - * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL + * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL -f dhcpcd.conf */ int dhcp_do_request(const char *interface, char *ipaddr, @@ -185,7 +186,7 @@ int dhcp_do_request(const char *interface, char result_prop_name[PROPERTY_KEY_MAX]; char daemon_prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; - char daemon_cmd[PROPERTY_VALUE_MAX * 2]; + char daemon_cmd[PROPERTY_VALUE_MAX * 2 + sizeof(DHCP_CONFIG_PATH)]; const char *ctrl_prop = "ctl.start"; const char *desired_status = "running"; /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */ @@ -206,10 +207,11 @@ int dhcp_do_request(const char *interface, /* Start the daemon and wait until it's ready */ if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0')) - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-h %s %s", DAEMON_NAME, p2p_interface, - prop_value, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s -h %s %s", DAEMON_NAME, + p2p_interface, DHCP_CONFIG_PATH, prop_value, interface); else - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME, p2p_interface, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s %s", DAEMON_NAME, + DHCP_CONFIG_PATH, p2p_interface, interface); memset(prop_value, '\0', PROPERTY_VALUE_MAX); property_set(ctrl_prop, daemon_cmd); if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) { diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index f19cb41..eb33d06 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -599,10 +599,6 @@ int ifc_disable(const char *ifname) return result; } -#define RESET_IPV4_ADDRESSES 0x01 -#define RESET_IPV6_ADDRESSES 0x02 -#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES) - int ifc_reset_connections(const char *ifname, const int reset_mask) { #ifdef HAVE_ANDROID_OS diff --git a/libnl_2/Android.mk b/libnl_2/Android.mk index 1745f5a..deac9de 100644 --- a/libnl_2/Android.mk +++ b/libnl_2/Android.mk @@ -1,5 +1,11 @@ +####################################### +# * Netlink cache not implemented +# * Library is not thread safe +####################################### + LOCAL_PATH := $(call my-dir) + include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -22,9 +28,10 @@ LOCAL_MODULE := libnl_2 LOCAL_MODULE_TAGS := optional include $(BUILD_STATIC_LIBRARY) -####################################### -# Shared library currently unavailiable -# * Netlink cache not implemented -# * Library is not thread safe -####################################### - +include $(CLEAR_VARS) +LOCAL_SRC_FILES := +LOCAL_WHOLE_STATIC_LIBRARIES:= libnl_2 +LOCAL_SHARED_LIBRARIES:= liblog +LOCAL_MODULE := libnl_2 +LOCAL_MODULE_TAGS := optional +include $(BUILD_SHARED_LIBRARY) diff --git a/libnl_2/attr.c b/libnl_2/attr.c index f3a2b58..2ef7590 100644 --- a/libnl_2/attr.c +++ b/libnl_2/attr.c @@ -160,9 +160,31 @@ int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) } return -EINVAL; +} + +/* Add 8 bit integer attribute to netlink message. */ +int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value) +{ + return nla_put(msg, attrtype, sizeof(uint8_t), &value); +} + +/* Add 16 bit integer attribute to netlink message. */ +int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value) +{ + return nla_put(msg, attrtype, sizeof(uint16_t), &value); +} +/* Add 32 bit integer attribute to netlink message. */ +int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value) +{ + return nla_put(msg, attrtype, sizeof(uint32_t), &value); } +/* Add 64 bit integer attribute to netlink message. */ +int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value) +{ + return nla_put(msg, attrtype, sizeof(uint64_t), &value); +} /* Add nested attributes to netlink message. */ /* Takes the attributes found in the nested message and appends them diff --git a/libnl_2/genl/genl.c b/libnl_2/genl/genl.c index 2442993..05431d6 100644 --- a/libnl_2/genl/genl.c +++ b/libnl_2/genl/genl.c @@ -21,6 +21,8 @@ #include <stdio.h> #include <sys/time.h> #include <linux/netlink.h> +#include <netlink/genl/ctrl.h> +#include <netlink/genl/family.h> #include "netlink-types.h" /* Get head of attribute data. */ @@ -250,7 +252,6 @@ error: struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \ const char *name) { - /* TODO: When will we release this memory ? */ struct genl_family *gf = (struct genl_family *) \ malloc(sizeof(struct genl_family)); if (!gf) @@ -262,7 +263,7 @@ struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \ /* Overriding cache pointer as family id for now */ gf->gf_id = (uint16_t) ((uint32_t) cache); - strcpy(gf->gf_name, "nl80211"); + strncpy(gf->gf_name, name, GENL_NAMSIZ); return gf; fail: @@ -272,15 +273,29 @@ fail: int genl_ctrl_resolve(struct nl_sock *sk, const char *name) { + struct nl_cache *cache = NULL; + struct genl_family *gf = NULL; + int id = -1; + /* Hack to support wpa_supplicant */ if (strcmp(name, "nlctrl") == 0) return NETLINK_GENERIC; - else { - int errsv = errno; - fprintf(stderr, \ - "Only nlctrl supported by genl_ctrl_resolve!\n"); - return -errsv; + + if (strcmp(name, "nl80211") != 0) { + fprintf(stderr, "%s is not supported\n", name); + return id; } -} + if (!genl_ctrl_alloc_cache(sk, &cache)) { + gf = genl_ctrl_search_by_name(cache, name); + if (gf) + id = genl_family_get_id(gf); + } + + if (gf) + genl_family_put(gf); + if (cache) + nl_cache_free(cache); + return id; +} diff --git a/libnl_2/socket.c b/libnl_2/socket.c index d906cac..e94eb9e 100644 --- a/libnl_2/socket.c +++ b/libnl_2/socket.c @@ -127,3 +127,15 @@ int nl_socket_get_fd(struct nl_sock *sk) { return sk->s_fd; } + +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) +{ + nl_cb_put(sk->s_cb); + sk->s_cb = cb; + nl_cb_get(cb); +} + +struct nl_cb *nl_socket_get_cb(struct nl_sock *sk) +{ + return nl_cb_get(sk->s_cb); +} diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c index 2c2aa36..1df8c6a 100644 --- a/libsuspend/autosuspend_earlysuspend.c +++ b/libsuspend/autosuspend_earlysuspend.c @@ -16,6 +16,8 @@ #include <errno.h> #include <fcntl.h> +#include <pthread.h> +#include <stdbool.h> #include <stddef.h> #include <string.h> #include <sys/types.h> @@ -31,11 +33,75 @@ #define EARLYSUSPEND_WAIT_FOR_FB_SLEEP "/sys/power/wait_for_fb_sleep" #define EARLYSUSPEND_WAIT_FOR_FB_WAKE "/sys/power/wait_for_fb_wake" - static int sPowerStatefd; static const char *pwr_state_mem = "mem"; static const char *pwr_state_on = "on"; +static pthread_t earlysuspend_thread; +static pthread_mutex_t earlysuspend_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t earlysuspend_cond = PTHREAD_COND_INITIALIZER; +static bool wait_for_earlysuspend; +static enum { + EARLYSUSPEND_ON, + EARLYSUSPEND_MEM, +} earlysuspend_state = EARLYSUSPEND_ON; + +int wait_for_fb_wake(void) +{ + int err = 0; + char buf; + int fd = open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + ALOGE_IF(err < 0, + "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); + close(fd); + return err < 0 ? err : 0; +} + +static int wait_for_fb_sleep(void) +{ + int err = 0; + char buf; + int fd = open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + ALOGE_IF(err < 0, + "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); + close(fd); + return err < 0 ? err : 0; +} + +static void *earlysuspend_thread_func(void *arg) +{ + char buf[80]; + char wakeup_count[20]; + int wakeup_count_len; + int ret; + while (1) { + if (wait_for_fb_sleep()) { + ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n"); + return NULL; + } + pthread_mutex_lock(&earlysuspend_mutex); + earlysuspend_state = EARLYSUSPEND_MEM; + pthread_cond_signal(&earlysuspend_cond); + pthread_mutex_unlock(&earlysuspend_mutex); + + if (wait_for_fb_wake()) { + ALOGE("Failed reading wait_for_fb_wake, exiting earlysuspend thread\n"); + return NULL; + } + pthread_mutex_lock(&earlysuspend_mutex); + earlysuspend_state = EARLYSUSPEND_ON; + pthread_cond_signal(&earlysuspend_cond); + pthread_mutex_unlock(&earlysuspend_mutex); + } +} static int autosuspend_earlysuspend_enable(void) { char buf[80]; @@ -50,6 +116,14 @@ static int autosuspend_earlysuspend_enable(void) goto err; } + if (wait_for_earlysuspend) { + pthread_mutex_lock(&earlysuspend_mutex); + while (earlysuspend_state != EARLYSUSPEND_MEM) { + pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex); + } + pthread_mutex_unlock(&earlysuspend_mutex); + } + ALOGV("autosuspend_earlysuspend_enable done\n"); return 0; @@ -72,6 +146,14 @@ static int autosuspend_earlysuspend_disable(void) goto err; } + if (wait_for_earlysuspend) { + pthread_mutex_lock(&earlysuspend_mutex); + while (earlysuspend_state != EARLYSUSPEND_ON) { + pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex); + } + pthread_mutex_unlock(&earlysuspend_mutex); + } + ALOGV("autosuspend_earlysuspend_disable done\n"); return 0; @@ -85,29 +167,61 @@ struct autosuspend_ops autosuspend_earlysuspend_ops = { .disable = autosuspend_earlysuspend_disable, }; -struct autosuspend_ops *autosuspend_earlysuspend_init(void) +void start_earlysuspend_thread(void) { char buf[80]; int ret; ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK); if (ret < 0) { - return NULL; + return; } ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK); if (ret < 0) { - return NULL; + return; } + wait_for_fb_wake(); + + ALOGI("Starting early suspend unblocker thread\n"); + ret = pthread_create(&earlysuspend_thread, NULL, earlysuspend_thread_func, NULL); + if (ret) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error creating thread: %s\n", buf); + return; + } + + wait_for_earlysuspend = true; +} + +struct autosuspend_ops *autosuspend_earlysuspend_init(void) +{ + char buf[80]; + int ret; + sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR); if (sPowerStatefd < 0) { strerror_r(errno, buf, sizeof(buf)); - ALOGE("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + ALOGW("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); return NULL; } + ret = write(sPowerStatefd, "on", 2); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + goto err_write; + } + ALOGI("Selected early suspend\n"); + + start_earlysuspend_thread(); + return &autosuspend_earlysuspend_ops; + +err_write: + close(sPowerStatefd); + return NULL; } diff --git a/libsync/sync.c b/libsync/sync.c index 311da14..c20f15e 100644 --- a/libsync/sync.c +++ b/libsync/sync.c @@ -20,12 +20,13 @@ #include <stdint.h> #include <string.h> +#include <linux/sync.h> +#include <linux/sw_sync.h> + #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> -#include <sync/sync.h> - int sync_wait(int fd, unsigned int timeout) { __u32 to = timeout; diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c index 5d261cd..c059b89 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c @@ -49,7 +49,8 @@ #include "usbhost/usbhost.h" -#define USB_FS_DIR "/dev/bus/usb" +#define DEV_DIR "/dev" +#define USB_FS_DIR "/dev/bus/usb" #define USB_FS_ID_SCANNER "/dev/bus/usb/%d/%d" #define USB_FS_ID_FORMAT "/dev/bus/usb/%03d/%03d" @@ -77,39 +78,72 @@ static inline int badname(const char *name) return 0; } +static int find_existing_devices_bus(char *busname, + usb_device_added_cb added_cb, + void *client_data) +{ + char devname[32]; + DIR *devdir; + struct dirent *de; + int done = 0; + + devdir = opendir(busname); + if(devdir == 0) return 0; + + while ((de = readdir(devdir)) && !done) { + if(badname(de->d_name)) continue; + + snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name); + done = added_cb(devname, client_data); + } // end of devdir while + closedir(devdir); + + return done; +} + /* returns true if one of the callbacks indicates we are done */ static int find_existing_devices(usb_device_added_cb added_cb, - usb_device_removed_cb removed_cb, void *client_data) { - char busname[32], devname[32]; - DIR *busdir , *devdir ; + char busname[32]; + DIR *busdir; struct dirent *de; int done = 0; busdir = opendir(USB_FS_DIR); - if(busdir == 0) return 1; + if(busdir == 0) return 0; while ((de = readdir(busdir)) != 0 && !done) { if(badname(de->d_name)) continue; - snprintf(busname, sizeof busname, "%s/%s", USB_FS_DIR, de->d_name); - devdir = opendir(busname); - if(devdir == 0) continue; - - while ((de = readdir(devdir)) && !done) { - if(badname(de->d_name)) continue; - - snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name); - done = added_cb(devname, client_data); - } // end of devdir while - closedir(devdir); - } //end of busdir while + snprintf(busname, sizeof(busname), "%s/%s", USB_FS_DIR, de->d_name); + done = find_existing_devices_bus(busname, added_cb, + client_data); + } closedir(busdir); return done; } +static void watch_existing_subdirs(struct usb_host_context *context, + int *wds, int wd_count) +{ + char path[100]; + int i, ret; + + wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE); + if (wds[0] < 0) + return; + + /* watch existing subdirectories of USB_FS_DIR */ + for (i = 1; i < wd_count; i++) { + snprintf(path, sizeof(path), "%s/%03d", USB_FS_DIR, i); + ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); + if (ret >= 0) + wds[i] = ret; + } +} + struct usb_host_context *usb_host_init() { struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context)); @@ -142,32 +176,28 @@ void usb_host_run(struct usb_host_context *context, char event_buf[512]; char path[100]; int i, ret, done = 0; - int wd, wds[10]; + int wd, wdd, wds[10]; int wd_count = sizeof(wds) / sizeof(wds[0]); D("Created device discovery thread\n"); /* watch for files added and deleted within USB_FS_DIR */ - memset(wds, 0, sizeof(wds)); + for (i = 0; i < wd_count; i++) + wds[i] = -1; + /* watch the root for new subdirectories */ - wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE); - if (wds[0] < 0) { + wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE); + if (wdd < 0) { fprintf(stderr, "inotify_add_watch failed\n"); if (discovery_done_cb) discovery_done_cb(client_data); return; } - /* watch existing subdirectories of USB_FS_DIR */ - for (i = 1; i < wd_count; i++) { - snprintf(path, sizeof(path), "%s/%03d", USB_FS_DIR, i); - ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret > 0) - wds[i] = ret; - } + watch_existing_subdirs(context, wds, wd_count); /* check for existing devices first, after we have inotify set up */ - done = find_existing_devices(added_cb, removed_cb, client_data); + done = find_existing_devices(added_cb, client_data); if (discovery_done_cb) done |= discovery_done_cb(client_data); @@ -176,14 +206,35 @@ void usb_host_run(struct usb_host_context *context, if (ret >= (int)sizeof(struct inotify_event)) { event = (struct inotify_event *)event_buf; wd = event->wd; - if (wd == wds[0]) { + if (wd == wdd) { + if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) { + watch_existing_subdirs(context, wds, wd_count); + done = find_existing_devices(added_cb, client_data); + } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "bus")) { + for (i = 0; i < wd_count; i++) { + if (wds[i] >= 0) { + inotify_rm_watch(context->fd, wds[i]); + wds[i] = -1; + } + } + } + } else if (wd == wds[0]) { i = atoi(event->name); snprintf(path, sizeof(path), "%s/%s", USB_FS_DIR, event->name); - D("new subdirectory %s: index: %d\n", path, i); + D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ? + "new" : "gone", path, i); if (i > 0 && i < wd_count) { - ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret > 0) - wds[i] = ret; + if (event->mask & IN_CREATE) { + ret = inotify_add_watch(context->fd, path, + IN_CREATE | IN_DELETE); + if (ret >= 0) + wds[i] = ret; + done = find_existing_devices_bus(path, added_cb, + client_data); + } else if (event->mask & IN_DELETE) { + inotify_rm_watch(context->fd, wds[i]); + wds[i] = -1; + } } } else { for (i = 1; i < wd_count && !done; i++) { diff --git a/logcat/event.logtags b/logcat/event.logtags index 2e814ff..09640e1 100644 --- a/logcat/event.logtags +++ b/logcat/event.logtags @@ -134,5 +134,21 @@ 70200 aggregation (aggregation time|2|3) 70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2) +# libc failure logging +80100 bionic_event_memcpy_buffer_overflow (uid|1) +80105 bionic_event_strcat_buffer_overflow (uid|1) +80110 bionic_event_memmov_buffer_overflow (uid|1) +80115 bionic_event_strncat_buffer_overflow (uid|1) +80120 bionic_event_strncpy_buffer_overflow (uid|1) +80125 bionic_event_memset_buffer_overflow (uid|1) +80130 bionic_event_strcpy_buffer_overflow (uid|1) + +80200 bionic_event_strcat_integer_overflow (uid|1) +80205 bionic_event_strncat_integer_overflow (uid|1) + +80300 bionic_event_resolver_old_response (uid|1) +80305 bionic_event_resolver_wrong_server (uid|1) +80310 bionic_event_resolver_wrong_query (uid|1) + # NOTE - the range 1000000-2000000 is reserved for partners and others who # want to define their own log tags without conflicting with the core platform. diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index a94cb9c..2c32ce3 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c @@ -109,18 +109,17 @@ int main(int argc, char **argv) int fd; SHA_CTX ctx; uint8_t* sha; + unsigned base = 0x10000000; + unsigned kernel_offset = 0x00008000; + unsigned ramdisk_offset = 0x01000000; + unsigned second_offset = 0x00f00000; + unsigned tags_offset = 0x00000100; argc--; argv++; memset(&hdr, 0, sizeof(hdr)); - /* default load addresses */ - hdr.kernel_addr = 0x10008000; - hdr.ramdisk_addr = 0x11000000; - hdr.second_addr = 0x10F00000; - hdr.tags_addr = 0x10000100; - while(argc > 0){ char *arg = argv[0]; char *val = argv[1]; @@ -140,11 +139,15 @@ int main(int argc, char **argv) } else if(!strcmp(arg, "--cmdline")) { cmdline = val; } else if(!strcmp(arg, "--base")) { - unsigned base = strtoul(val, 0, 16); - hdr.kernel_addr = base + 0x00008000; - hdr.ramdisk_addr = base + 0x01000000; - hdr.second_addr = base + 0x00F00000; - hdr.tags_addr = base + 0x00000100; + base = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--kernel_offset")) { + kernel_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--ramdisk_offset")) { + ramdisk_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--second_offset")) { + second_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--tags_offset")) { + tags_offset = strtoul(val, 0, 16); } else if(!strcmp(arg, "--board")) { board = val; } else if(!strcmp(arg,"--pagesize")) { @@ -159,6 +162,10 @@ int main(int argc, char **argv) } hdr.page_size = pagesize; + hdr.kernel_addr = base + kernel_offset; + hdr.ramdisk_addr = base + ramdisk_offset; + hdr.second_addr = base + second_offset; + hdr.tags_addr = base + tags_offset; if(bootimg == 0) { fprintf(stderr,"error: no output filename specified\n"); diff --git a/rootdir/init.rc b/rootdir/init.rc index 47653e4..4c20ec1 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -150,6 +150,8 @@ on post-fs chmod 0440 /proc/kmsg chown root system /proc/sysrq-trigger chmod 0220 /proc/sysrq-trigger + chown system log /proc/last_kmsg + chmod 0440 /proc/last_kmsg # create the lost+found directories, so as to enforce our permissions mkdir /cache/lost+found 0770 root root @@ -179,7 +181,8 @@ on post-fs-data # create basic filesystem structure mkdir /data/misc 01771 system misc - mkdir /data/misc/bluetoothd 0770 bluetooth bluetooth + mkdir /data/misc/adb 02750 system shell + mkdir /data/misc/bluedroid 0770 bluetooth bluetooth mkdir /data/misc/bluetooth 0770 system system mkdir /data/misc/keystore 0700 keystore keystore mkdir /data/misc/keychain 0771 system system @@ -251,6 +254,7 @@ on boot chown radio system /sys/android_power/acquire_full_wake_lock chown radio system /sys/android_power/acquire_partial_wake_lock chown radio system /sys/android_power/release_wake_lock + chown system system /sys/power/autosleep chown system system /sys/power/state chown system system /sys/power/wakeup_count chown radio system /sys/power/wake_lock @@ -372,6 +376,7 @@ on property:ro.debuggable=1 # adbd is controlled via property triggers in init.<platform>.usb.rc service adbd /sbin/adbd class core + socket adbd stream 660 system system disabled seclabel u:r:adbd:s0 @@ -442,21 +447,6 @@ service bootanim /system/bin/bootanimation disabled oneshot -service dbus /system/bin/dbus-daemon --system --nofork - class main - socket dbus stream 660 bluetooth bluetooth - user bluetooth - group bluetooth net_bt_admin - -service bluetoothd /system/bin/bluetoothd -n - class main - socket bluetooth stream 660 bluetooth bluetooth - socket dbus_bluetooth stream 660 bluetooth bluetooth - # init.rc does not yet support applying capabilities, so run as root and - # let bluetoothd drop uid to bluetooth with the right linux capabilities - group bluetooth net_bt_admin misc - disabled - service installd /system/bin/installd class main socket installd stream 600 system system diff --git a/sdcard/Android.mk b/sdcard/Android.mk index c430ac8..fb04d6d 100644 --- a/sdcard/Android.mk +++ b/sdcard/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= sdcard.c LOCAL_MODULE:= sdcard +LOCAL_CFLAGS := -Wall -Wno-unused-parameter LOCAL_SHARED_LIBRARIES := libc diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index a95513c..877a524 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -25,7 +25,9 @@ #include <sys/statfs.h> #include <sys/uio.h> #include <dirent.h> +#include <limits.h> #include <ctype.h> +#include <pthread.h> #include <private/android_filesystem_config.h> @@ -40,12 +42,10 @@ * permissions at creation, owner, group, and permissions are not * changeable, symlinks and hardlinks are not createable, etc. * - * usage: sdcard <path> <uid> <gid> - * - * It must be run as root, but will change to uid/gid as soon as it - * mounts a filesystem on /storage/sdcard. It will refuse to run if uid or - * gid are zero. + * See usage() for command line options. * + * It must be run as root, but will drop to requested UID/GID as soon as it + * mounts a filesystem. It will refuse to run if requested UID/GID are zero. * * Things I believe to be true: * @@ -55,7 +55,6 @@ * - if an op that returns a fuse_entry fails writing the reply to the * kernel, you must rollback the refcount to reflect the reference the * kernel did not actually acquire - * */ #define FUSE_TRACE 0 @@ -70,30 +69,42 @@ #define FUSE_UNKNOWN_INO 0xffffffff -#define MOUNT_POINT "/storage/sdcard0" +/* Maximum number of bytes to write in one request. */ +#define MAX_WRITE (256 * 1024) + +/* Maximum number of bytes to read in one request. */ +#define MAX_READ (128 * 1024) + +/* Largest possible request. + * The request size is bounded by the maximum size of a FUSE_WRITE request because it has + * the largest possible data payload. */ +#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE) + +/* Default number of threads. */ +#define DEFAULT_NUM_THREADS 2 + +/* Pseudo-error constant used to indicate that no fuse status is needed + * or that a reply has already been written. */ +#define NO_STATUS 1 struct handle { - struct node *node; int fd; }; struct dirhandle { - struct node *node; DIR *d; }; struct node { + __u32 refcount; __u64 nid; __u64 gen; struct node *next; /* per-dir sibling list */ struct node *child; /* first contained file by this dir */ - struct node *all; /* global node list */ struct node *parent; /* containing directory */ - __u32 refcount; - __u32 namelen; - + size_t namelen; char *name; /* If non-null, this is the real name of the file in the underlying storage. * This may differ from the field "name" only by case. @@ -103,98 +114,166 @@ struct node { char *actual_name; }; +/* Global data structure shared by all fuse handlers. */ struct fuse { - __u64 next_generation; - __u64 next_node_id; + pthread_mutex_t lock; + __u64 next_generation; int fd; - - struct node *all; - struct node root; - char rootpath[1024]; + char rootpath[PATH_MAX]; }; -static unsigned uid = -1; -static unsigned gid = -1; +/* Private data used by a single fuse handler. */ +struct fuse_handler { + struct fuse* fuse; + int token; + + /* To save memory, we never use the contents of the request buffer and the read + * buffer at the same time. This allows us to share the underlying storage. */ + union { + __u8 request_buffer[MAX_REQUEST_SIZE]; + __u8 read_buffer[MAX_READ]; + }; +}; -#define PATH_BUFFER_SIZE 1024 +static inline void *id_to_ptr(__u64 nid) +{ + return (void *) (uintptr_t) nid; +} -#define NO_CASE_SENSITIVE_MATCH 0 -#define CASE_SENSITIVE_MATCH 1 +static inline __u64 ptr_to_id(void *ptr) +{ + return (__u64) (uintptr_t) ptr; +} -/* - * Get the real-life absolute path to a node. - * node: start at this node - * buf: storage for returned string - * name: append this string to path if set +static void acquire_node_locked(struct node* node) +{ + node->refcount++; + TRACE("ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount); +} + +static void remove_node_from_parent_locked(struct node* node); + +static void release_node_locked(struct node* node) +{ + TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount); + if (node->refcount > 0) { + node->refcount--; + if (!node->refcount) { + TRACE("DESTROY %p (%s)\n", node, node->name); + remove_node_from_parent_locked(node); + + /* TODO: remove debugging - poison memory */ + memset(node->name, 0xef, node->namelen); + free(node->name); + free(node->actual_name); + memset(node, 0xfc, sizeof(*node)); + free(node); + } + } else { + ERROR("Zero refcnt %p\n", node); + } +} + +static void add_node_to_parent_locked(struct node *node, struct node *parent) { + node->parent = parent; + node->next = parent->child; + parent->child = node; + acquire_node_locked(parent); +} + +static void remove_node_from_parent_locked(struct node* node) +{ + if (node->parent) { + if (node->parent->child == node) { + node->parent->child = node->parent->child->next; + } else { + struct node *node2; + node2 = node->parent->child; + while (node2->next != node) + node2 = node2->next; + node2->next = node->next; + } + release_node_locked(node->parent); + node->parent = NULL; + node->next = NULL; + } +} + +/* Gets the absolute path to a node into the provided buffer. + * + * Populates 'buf' with the path and returns the length of the path on success, + * or returns -1 if the path is too long for the provided buffer. */ -char *do_node_get_path(struct node *node, char *buf, const char *name, int match_case_insensitive) +static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) { - struct node *in_node = node; - const char *in_name = name; - char *out = buf + PATH_BUFFER_SIZE - 1; - int len; - out[0] = 0; - - if (name) { - len = strlen(name); - goto start; - } - - while (node) { - name = (node->actual_name ? node->actual_name : node->name); - len = node->namelen; - node = node->parent; - start: - if ((len + 1) > (out - buf)) - return 0; - out -= len; - memcpy(out, name, len); - /* avoid double slash at beginning of path */ - if (out[0] != '/') { - out --; - out[0] = '/'; + size_t namelen = node->namelen; + if (bufsize < namelen + 1) { + return -1; + } + + ssize_t pathlen = 0; + if (node->parent) { + pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2); + if (pathlen < 0) { + return -1; } + buf[pathlen++] = '/'; } - /* If we are searching for a file within node (rather than computing node's path) - * and fail, then we need to look for a case insensitive match. - */ - if (in_name && match_case_insensitive && access(out, F_OK) != 0) { - char *path, buffer[PATH_BUFFER_SIZE]; - DIR* dir; + const char* name = node->actual_name ? node->actual_name : node->name; + memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */ + return pathlen + namelen; +} + +/* Finds the absolute path of a file within a given directory. + * Performs a case-insensitive search for the file and sets the buffer to the path + * of the first matching file. If 'search' is zero or if no match is found, sets + * the buffer to the path that the file would have, assuming the name were case-sensitive. + * + * Populates 'buf' with the path and returns the actual name (within 'buf') on success, + * or returns NULL if the path is too long for the provided buffer. + */ +static char* find_file_within(const char* path, const char* name, + char* buf, size_t bufsize, int search) +{ + size_t pathlen = strlen(path); + size_t namelen = strlen(name); + size_t childlen = pathlen + namelen + 1; + char* actual; + + if (bufsize <= childlen) { + return NULL; + } + + memcpy(buf, path, pathlen); + buf[pathlen] = '/'; + actual = buf + pathlen + 1; + memcpy(actual, name, namelen + 1); + + if (search && access(buf, F_OK)) { struct dirent* entry; - path = do_node_get_path(in_node, buffer, NULL, NO_CASE_SENSITIVE_MATCH); - dir = opendir(path); + DIR* dir = opendir(path); if (!dir) { ERROR("opendir %s failed: %s", path, strerror(errno)); - return out; + return actual; } - while ((entry = readdir(dir))) { - if (!strcasecmp(entry->d_name, in_name)) { - /* we have a match - replace the name */ - len = strlen(in_name); - memcpy(buf + PATH_BUFFER_SIZE - len - 1, entry->d_name, len); + if (!strcasecmp(entry->d_name, name)) { + /* we have a match - replace the name, don't need to copy the null again */ + memcpy(actual, entry->d_name, namelen); break; } } closedir(dir); } - - return out; + return actual; } -char *node_get_path(struct node *node, char *buf, const char *name) +static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, __u64 nid) { - /* We look for case insensitive matches by default */ - return do_node_get_path(node, buf, name, CASE_SENSITIVE_MATCH); -} - -void attr_from_stat(struct fuse_attr *attr, struct stat *s) -{ - attr->ino = s->st_ino; + attr->ino = nid; attr->size = s->st_size; attr->blocks = s->st_blocks; attr->atime = s->st_atime; @@ -221,759 +300,1023 @@ void attr_from_stat(struct fuse_attr *attr, struct stat *s) attr->gid = AID_SDCARD_RW; } -int node_get_attr(struct node *node, struct fuse_attr *attr) +struct node *create_node_locked(struct fuse* fuse, + struct node *parent, const char *name, const char* actual_name) { - int res; - struct stat s; - char *path, buffer[PATH_BUFFER_SIZE]; + struct node *node; + size_t namelen = strlen(name); - path = node_get_path(node, buffer, 0); - res = lstat(path, &s); - if (res < 0) { - ERROR("lstat('%s') errno %d\n", path, errno); - return -1; + node = calloc(1, sizeof(struct node)); + if (!node) { + return NULL; } - - attr_from_stat(attr, &s); - attr->ino = node->nid; - - return 0; -} - -static void add_node_to_parent(struct node *node, struct node *parent) { - node->parent = parent; - node->next = parent->child; - parent->child = node; - parent->refcount++; + node->name = malloc(namelen + 1); + if (!node->name) { + free(node); + return NULL; + } + memcpy(node->name, name, namelen + 1); + if (strcmp(name, actual_name)) { + node->actual_name = malloc(namelen + 1); + if (!node->actual_name) { + free(node->name); + free(node); + return NULL; + } + memcpy(node->actual_name, actual_name, namelen + 1); + } + node->namelen = namelen; + node->nid = ptr_to_id(node); + node->gen = fuse->next_generation++; + acquire_node_locked(node); + add_node_to_parent_locked(node, parent); + return node; } -/* Check to see if our parent directory already has a file with a name - * that differs only by case. If we find one, store it in the actual_name - * field so node_get_path will map it to this file in the underlying storage. - */ -static void node_find_actual_name(struct node *node) +static int rename_node_locked(struct node *node, const char *name, + const char* actual_name) { - char *path, buffer[PATH_BUFFER_SIZE]; - const char *node_name = node->name; - DIR* dir; - struct dirent* entry; - - if (!node->parent) return; - - path = node_get_path(node->parent, buffer, 0); - dir = opendir(path); - if (!dir) { - ERROR("opendir %s failed: %s", path, strerror(errno)); - return; + size_t namelen = strlen(name); + int need_actual_name = strcmp(name, actual_name); + + /* make the storage bigger without actually changing the name + * in case an error occurs part way */ + if (namelen > node->namelen) { + char* new_name = realloc(node->name, namelen + 1); + if (!new_name) { + return -ENOMEM; + } + node->name = new_name; + if (need_actual_name && node->actual_name) { + char* new_actual_name = realloc(node->actual_name, namelen + 1); + if (!new_actual_name) { + return -ENOMEM; + } + node->actual_name = new_actual_name; + } } - while ((entry = readdir(dir))) { - const char *test_name = entry->d_name; - if (strcmp(test_name, node_name) && !strcasecmp(test_name, node_name)) { - /* we have a match - differs but only by case */ - node->actual_name = strdup(test_name); + /* update the name, taking care to allocate storage before overwriting the old name */ + if (need_actual_name) { + if (!node->actual_name) { + node->actual_name = malloc(namelen + 1); if (!node->actual_name) { - ERROR("strdup failed - out of memory\n"); - exit(1); + return -ENOMEM; } - break; } + memcpy(node->actual_name, actual_name, namelen + 1); + } else { + free(node->actual_name); + node->actual_name = NULL; } - closedir(dir); + memcpy(node->name, name, namelen + 1); + node->namelen = namelen; + return 0; } -struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen) +static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid) { - struct node *node; - int namelen = strlen(name); - - node = calloc(1, sizeof(struct node)); - if (node == 0) { - return 0; - } - node->name = malloc(namelen + 1); - if (node->name == 0) { - free(node); - return 0; + if (nid == FUSE_ROOT_ID) { + return &fuse->root; + } else { + return id_to_ptr(nid); } +} - node->nid = nid; - node->gen = gen; - add_node_to_parent(node, parent); - memcpy(node->name, name, namelen + 1); - node->namelen = namelen; - node_find_actual_name(node); +static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid, + char* buf, size_t bufsize) +{ + struct node* node = lookup_node_by_id_locked(fuse, nid); + if (node && get_node_path_locked(node, buf, bufsize) < 0) { + node = NULL; + } return node; } -static char *rename_node(struct node *node, const char *name) +static struct node *lookup_child_by_name_locked(struct node *node, const char *name) { - node->namelen = strlen(name); - char *newname = realloc(node->name, node->namelen + 1); - if (newname == 0) - return 0; - node->name = newname; - memcpy(node->name, name, node->namelen + 1); - node_find_actual_name(node); - return node->name; + for (node = node->child; node; node = node->next) { + /* use exact string comparison, nodes that differ by case + * must be considered distinct even if they refer to the same + * underlying file as otherwise operations such as "mv x x" + * will not work because the source and target nodes are the same. */ + if (!strcmp(name, node->name)) { + return node; + } + } + return 0; +} + +static struct node* acquire_or_create_child_locked( + struct fuse* fuse, struct node* parent, + const char* name, const char* actual_name) +{ + struct node* child = lookup_child_by_name_locked(parent, name); + if (child) { + acquire_node_locked(child); + } else { + child = create_node_locked(fuse, parent, name, actual_name); + } + return child; } -void fuse_init(struct fuse *fuse, int fd, const char *path) +static void fuse_init(struct fuse *fuse, int fd, const char *source_path) { + pthread_mutex_init(&fuse->lock, NULL); + fuse->fd = fd; - fuse->next_node_id = 2; fuse->next_generation = 0; - fuse->all = &fuse->root; - memset(&fuse->root, 0, sizeof(fuse->root)); fuse->root.nid = FUSE_ROOT_ID; /* 1 */ fuse->root.refcount = 2; - rename_node(&fuse->root, path); + fuse->root.namelen = strlen(source_path); + fuse->root.name = strdup(source_path); } -static inline void *id_to_ptr(__u64 nid) +static void fuse_status(struct fuse *fuse, __u64 unique, int err) { - return (void *) nid; + struct fuse_out_header hdr; + hdr.len = sizeof(hdr); + hdr.error = err; + hdr.unique = unique; + write(fuse->fd, &hdr, sizeof(hdr)); } -static inline __u64 ptr_to_id(void *ptr) +static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len) { - return (__u64) ptr; + struct fuse_out_header hdr; + struct iovec vec[2]; + int res; + + hdr.len = len + sizeof(hdr); + hdr.error = 0; + hdr.unique = unique; + + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = data; + vec[1].iov_len = len; + + res = writev(fuse->fd, vec, 2); + if (res < 0) { + ERROR("*** REPLY FAILED *** %d\n", errno); + } } +static int fuse_reply_entry(struct fuse* fuse, __u64 unique, + struct node* parent, const char* name, const char* actual_name, + const char* path) +{ + struct node* node; + struct fuse_entry_out out; + struct stat s; + + if (lstat(path, &s) < 0) { + return -errno; + } + + pthread_mutex_lock(&fuse->lock); + node = acquire_or_create_child_locked(fuse, parent, name, actual_name); + if (!node) { + pthread_mutex_unlock(&fuse->lock); + return -ENOMEM; + } + memset(&out, 0, sizeof(out)); + attr_from_stat(&out.attr, &s, node->nid); + out.attr_valid = 10; + out.entry_valid = 10; + out.nodeid = node->nid; + out.generation = node->gen; + pthread_mutex_unlock(&fuse->lock); + fuse_reply(fuse, unique, &out, sizeof(out)); + return NO_STATUS; +} -struct node *lookup_by_inode(struct fuse *fuse, __u64 nid) +static int fuse_reply_attr(struct fuse* fuse, __u64 unique, __u64 nid, + const char* path) { - if (nid == FUSE_ROOT_ID) { - return &fuse->root; - } else { - return id_to_ptr(nid); + struct fuse_attr_out out; + struct stat s; + + if (lstat(path, &s) < 0) { + return -errno; } + memset(&out, 0, sizeof(out)); + attr_from_stat(&out.attr, &s, nid); + out.attr_valid = 10; + fuse_reply(fuse, unique, &out, sizeof(out)); + return NO_STATUS; } -struct node *lookup_child_by_name(struct node *node, const char *name) +static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const char* name) { - for (node = node->child; node; node = node->next) { - if (!strcmp(name, node->name)) { - return node; - } + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + const char* actual_name; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] LOOKUP %s @ %llx (%s)\n", handler->token, name, hdr->nodeid, + parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !(actual_name = find_file_within(parent_path, name, + child_path, sizeof(child_path), 1))) { + return -ENOENT; } - return 0; + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); } -struct node *lookup_child_by_inode(struct node *node, __u64 nid) +static int handle_forget(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const struct fuse_forget_in *req) { - for (node = node->child; node; node = node->next) { - if (node->nid == nid) { - return node; + struct node* node; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_by_id_locked(fuse, hdr->nodeid); + TRACE("[%d] FORGET #%lld @ %llx (%s)\n", handler->token, req->nlookup, + hdr->nodeid, node ? node->name : "?"); + if (node) { + __u64 n = req->nlookup; + while (n--) { + release_node_locked(node); } } - return 0; + pthread_mutex_unlock(&fuse->lock); + return NO_STATUS; /* no reply */ } -static void dec_refcount(struct node *node) { - if (node->refcount > 0) { - node->refcount--; - TRACE("dec_refcount %p(%s) -> %d\n", node, node->name, node->refcount); - } else { - ERROR("Zero refcnt %p\n", node); +static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const struct fuse_getattr_in *req) +{ + struct node* node; + char path[PATH_MAX]; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] GETATTR flags=%x fh=%llx @ %llx (%s)\n", handler->token, + req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!node) { + return -ENOENT; } - } + return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path); +} -static struct node *remove_child(struct node *parent, __u64 nid) +static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const struct fuse_setattr_in *req) { - struct node *prev = 0; - struct node *node; + struct node* node; + char path[PATH_MAX]; + struct timespec times[2]; - for (node = parent->child; node; node = node->next) { - if (node->nid == nid) { - if (prev) { - prev->next = node->next; + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] SETATTR fh=%llx valid=%x @ %llx (%s)\n", handler->token, + req->fh, req->valid, hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!node) { + return -ENOENT; + } + + /* XXX: incomplete implementation on purpose. + * chmod/chown should NEVER be implemented.*/ + + if ((req->valid & FATTR_SIZE) && truncate(path, req->size) < 0) { + return -errno; + } + + /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW + * are both set, then set it to the current time. Else, set it to the + * time specified in the request. Same goes for mtime. Use utimensat(2) + * as it allows ATIME and MTIME to be changed independently, and has + * nanosecond resolution which fuse also has. + */ + if (req->valid & (FATTR_ATIME | FATTR_MTIME)) { + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_nsec = UTIME_OMIT; + if (req->valid & FATTR_ATIME) { + if (req->valid & FATTR_ATIME_NOW) { + times[0].tv_nsec = UTIME_NOW; } else { - parent->child = node->next; + times[0].tv_sec = req->atime; + times[0].tv_nsec = req->atimensec; } - node->next = 0; - node->parent = 0; - dec_refcount(parent); - return node; } - prev = node; + if (req->valid & FATTR_MTIME) { + if (req->valid & FATTR_MTIME_NOW) { + times[1].tv_nsec = UTIME_NOW; + } else { + times[1].tv_sec = req->mtime; + times[1].tv_nsec = req->mtimensec; + } + } + TRACE("[%d] Calling utimensat on %s with atime %ld, mtime=%ld\n", + handler->token, path, times[0].tv_sec, times[1].tv_sec); + if (utimensat(-1, path, times, 0) < 0) { + return -errno; + } + } + return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path); +} + +static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + const char* actual_name; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] MKNOD %s 0%o @ %llx (%s)\n", handler->token, + name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !(actual_name = find_file_within(parent_path, name, + child_path, sizeof(child_path), 1))) { + return -ENOENT; + } + __u32 mode = (req->mode & (~0777)) | 0664; + if (mknod(child_path, mode, req->rdev) < 0) { + return -errno; + } + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); +} + +static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + const char* actual_name; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] MKDIR %s 0%o @ %llx (%s)\n", handler->token, + name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !(actual_name = find_file_within(parent_path, name, + child_path, sizeof(child_path), 1))) { + return -ENOENT; + } + __u32 mode = (req->mode & (~0777)) | 0775; + if (mkdir(child_path, mode) < 0) { + return -errno; + } + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); +} + +static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] UNLINK %s @ %llx (%s)\n", handler->token, + name, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !find_file_within(parent_path, name, + child_path, sizeof(child_path), 1)) { + return -ENOENT; + } + if (unlink(child_path) < 0) { + return -errno; } return 0; } -struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name, - struct fuse_attr *attr) +static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const char* name) { + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] RMDIR %s @ %llx (%s)\n", handler->token, + name, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !find_file_within(parent_path, name, + child_path, sizeof(child_path), 1)) { + return -ENOENT; + } + if (rmdir(child_path) < 0) { + return -errno; + } + return 0; +} + +static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_rename_in* req, + const char* old_name, const char* new_name) +{ + struct node* old_parent_node; + struct node* new_parent_node; + struct node* child_node; + char old_parent_path[PATH_MAX]; + char new_parent_path[PATH_MAX]; + char old_child_path[PATH_MAX]; + char new_child_path[PATH_MAX]; + const char* new_actual_name; int res; - struct stat s; - char *path, buffer[PATH_BUFFER_SIZE]; - struct node *node; - path = node_get_path(parent, buffer, name); - /* XXX error? */ + pthread_mutex_lock(&fuse->lock); + old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + old_parent_path, sizeof(old_parent_path)); + new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir, + new_parent_path, sizeof(new_parent_path)); + TRACE("[%d] RENAME %s->%s @ %llx (%s) -> %llx (%s)\n", handler->token, + old_name, new_name, + hdr->nodeid, old_parent_node ? old_parent_node->name : "?", + req->newdir, new_parent_node ? new_parent_node->name : "?"); + if (!old_parent_node || !new_parent_node) { + res = -ENOENT; + goto lookup_error; + } + child_node = lookup_child_by_name_locked(old_parent_node, old_name); + if (!child_node || get_node_path_locked(child_node, + old_child_path, sizeof(old_child_path)) < 0) { + res = -ENOENT; + goto lookup_error; + } + acquire_node_locked(child_node); + pthread_mutex_unlock(&fuse->lock); - res = lstat(path, &s); - if (res < 0) - return 0; - - node = lookup_child_by_name(parent, name); - if (!node) { - node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++); - if (!node) - return 0; - node->nid = ptr_to_id(node); - node->all = fuse->all; - fuse->all = node; + /* Special case for renaming a file where destination is same path + * differing only by case. In this case we don't want to look for a case + * insensitive match. This allows commands like "mv foo FOO" to work as expected. + */ + int search = old_parent_node != new_parent_node + || strcasecmp(old_name, new_name); + if (!(new_actual_name = find_file_within(new_parent_path, new_name, + new_child_path, sizeof(new_child_path), search))) { + res = -ENOENT; + goto io_error; } - attr_from_stat(attr, &s); - attr->ino = node->nid; + TRACE("[%d] RENAME %s->%s\n", handler->token, old_child_path, new_child_path); + res = rename(old_child_path, new_child_path); + if (res < 0) { + res = -errno; + goto io_error; + } - return node; + pthread_mutex_lock(&fuse->lock); + res = rename_node_locked(child_node, new_name, new_actual_name); + if (!res) { + remove_node_from_parent_locked(child_node); + add_node_to_parent_locked(child_node, new_parent_node); + } + goto done; + +io_error: + pthread_mutex_lock(&fuse->lock); +done: + release_node_locked(child_node); +lookup_error: + pthread_mutex_unlock(&fuse->lock); + return res; } -void node_release(struct node *node) +static int handle_open(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_open_in* req) { - TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount); - dec_refcount(node); - if (node->refcount == 0) { - if (node->parent->child == node) { - node->parent->child = node->parent->child->next; - } else { - struct node *node2; + struct node* node; + char path[PATH_MAX]; + struct fuse_open_out out; + struct handle *h; - node2 = node->parent->child; - while (node2->next != node) - node2 = node2->next; - node2->next = node->next; - } + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] OPEN 0%o @ %llx (%s)\n", handler->token, + req->flags, hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); - TRACE("DESTROY %p (%s)\n", node, node->name); + if (!node) { + return -ENOENT; + } + h = malloc(sizeof(*h)); + if (!h) { + return -ENOMEM; + } + TRACE("[%d] OPEN %s\n", handler->token, path); + h->fd = open(path, req->flags); + if (h->fd < 0) { + free(h); + return -errno; + } + out.fh = ptr_to_id(h); + out.open_flags = 0; + out.padding = 0; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} - node_release(node->parent); +static int handle_read(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_read_in* req) +{ + struct handle *h = id_to_ptr(req->fh); + __u64 unique = hdr->unique; + __u32 size = req->size; + __u64 offset = req->offset; + int res; - node->parent = 0; - node->next = 0; + /* Don't access any other fields of hdr or req beyond this point, the read buffer + * overlaps the request buffer and will clobber data in the request. This + * saves us 128KB per request handler thread at the cost of this scary comment. */ - /* TODO: remove debugging - poison memory */ - memset(node->name, 0xef, node->namelen); - free(node->name); - free(node->actual_name); - memset(node, 0xfc, sizeof(*node)); - free(node); + TRACE("[%d] READ %p(%d) %u@%llu\n", handler->token, + h, h->fd, size, offset); + if (size > sizeof(handler->read_buffer)) { + return -EINVAL; + } + res = pread64(h->fd, handler->read_buffer, size, offset); + if (res < 0) { + return -errno; } + fuse_reply(fuse, unique, handler->read_buffer, res); + return NO_STATUS; } -void fuse_status(struct fuse *fuse, __u64 unique, int err) +static int handle_write(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_write_in* req, + const void* buffer) { - struct fuse_out_header hdr; - hdr.len = sizeof(hdr); - hdr.error = err; - hdr.unique = unique; - if (err) { -// ERROR("*** %d ***\n", err); + struct fuse_write_out out; + struct handle *h = id_to_ptr(req->fh); + int res; + + TRACE("[%d] WRITE %p(%d) %u@%llu\n", handler->token, + h, h->fd, req->size, req->offset); + res = pwrite64(h->fd, buffer, req->size, req->offset); + if (res < 0) { + return -errno; } - write(fuse->fd, &hdr, sizeof(hdr)); + out.size = res; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; } -void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len) +static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr) { - struct fuse_out_header hdr; - struct iovec vec[2]; + char path[PATH_MAX]; + struct statfs stat; + struct fuse_statfs_out out; int res; - hdr.len = len + sizeof(hdr); - hdr.error = 0; - hdr.unique = unique; + pthread_mutex_lock(&fuse->lock); + TRACE("[%d] STATFS\n", handler->token); + res = get_node_path_locked(&fuse->root, path, sizeof(path)); + pthread_mutex_unlock(&fuse->lock); + if (res < 0) { + return -ENOENT; + } + if (statfs(fuse->root.name, &stat) < 0) { + return -errno; + } + memset(&out, 0, sizeof(out)); + out.st.blocks = stat.f_blocks; + out.st.bfree = stat.f_bfree; + out.st.bavail = stat.f_bavail; + out.st.files = stat.f_files; + out.st.ffree = stat.f_ffree; + out.st.bsize = stat.f_bsize; + out.st.namelen = stat.f_namelen; + out.st.frsize = stat.f_frsize; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} - vec[0].iov_base = &hdr; - vec[0].iov_len = sizeof(hdr); - vec[1].iov_base = data; - vec[1].iov_len = len; +static int handle_release(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_release_in* req) +{ + struct handle *h = id_to_ptr(req->fh); - res = writev(fuse->fd, vec, 2); + TRACE("[%d] RELEASE %p(%d)\n", handler->token, h, h->fd); + close(h->fd); + free(h); + return 0; +} + +static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_fsync_in* req) +{ + int is_data_sync = req->fsync_flags & 1; + struct handle *h = id_to_ptr(req->fh); + int res; + + TRACE("[%d] FSYNC %p(%d) is_data_sync=%d\n", handler->token, + h, h->fd, is_data_sync); + res = is_data_sync ? fdatasync(h->fd) : fsync(h->fd); if (res < 0) { - ERROR("*** REPLY FAILED *** %d\n", errno); + return -errno; } + return 0; } -void lookup_entry(struct fuse *fuse, struct node *node, - const char *name, __u64 unique) +static int handle_flush(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr) { - struct fuse_entry_out out; - - memset(&out, 0, sizeof(out)); + TRACE("[%d] FLUSH\n", handler->token); + return 0; +} + +static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_open_in* req) +{ + struct node* node; + char path[PATH_MAX]; + struct fuse_open_out out; + struct dirhandle *h; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] OPENDIR @ %llx (%s)\n", handler->token, + hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); - node = node_lookup(fuse, node, name, &out.attr); if (!node) { - fuse_status(fuse, unique, -ENOENT); - return; + return -ENOENT; } - - node->refcount++; -// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount); - out.nodeid = node->nid; - out.generation = node->gen; - out.entry_valid = 10; - out.attr_valid = 10; - - fuse_reply(fuse, unique, &out, sizeof(out)); + h = malloc(sizeof(*h)); + if (!h) { + return -ENOMEM; + } + TRACE("[%d] OPENDIR %s\n", handler->token, path); + h->d = opendir(path); + if (!h->d) { + free(h); + return -errno; + } + out.fh = ptr_to_id(h); + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; } -void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len) +static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_read_in* req) { - struct node *node; - - if ((len < sizeof(*hdr)) || (hdr->len != len)) { - ERROR("malformed header\n"); - return; + char buffer[8192]; + struct fuse_dirent *fde = (struct fuse_dirent*) buffer; + struct dirent *de; + struct dirhandle *h = id_to_ptr(req->fh); + + TRACE("[%d] READDIR %p\n", handler->token, h); + if (req->offset == 0) { + /* rewinddir() might have been called above us, so rewind here too */ + TRACE("[%d] calling rewinddir()\n", handler->token); + rewinddir(h->d); + } + de = readdir(h->d); + if (!de) { + return 0; } + fde->ino = FUSE_UNKNOWN_INO; + /* increment the offset so we can detect when rewinddir() seeks back to the beginning */ + fde->off = req->offset + 1; + fde->type = de->d_type; + fde->namelen = strlen(de->d_name); + memcpy(fde->name, de->d_name, fde->namelen + 1); + fuse_reply(fuse, hdr->unique, fde, + FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); + return NO_STATUS; +} - len -= hdr->len; +static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_release_in* req) +{ + struct dirhandle *h = id_to_ptr(req->fh); - if (hdr->nodeid) { - node = lookup_by_inode(fuse, hdr->nodeid); - if (!node) { - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - } else { - node = 0; - } + TRACE("[%d] RELEASEDIR %p\n", handler->token, h); + closedir(h->d); + free(h); + return 0; +} +static int handle_init(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_init_in* req) +{ + struct fuse_init_out out; + + TRACE("[%d] INIT ver=%d.%d maxread=%d flags=%x\n", + handler->token, req->major, req->minor, req->max_readahead, req->flags); + out.major = FUSE_KERNEL_VERSION; + out.minor = FUSE_KERNEL_MINOR_VERSION; + out.max_readahead = req->max_readahead; + out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; + out.max_background = 32; + out.congestion_threshold = 32; + out.max_write = MAX_WRITE; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} + +static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const void *data, size_t data_len) +{ switch (hdr->opcode) { case FUSE_LOOKUP: { /* bytez[] -> entry_out */ - TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data); - lookup_entry(fuse, node, (char*) data, hdr->unique); - return; + const char* name = data; + return handle_lookup(fuse, handler, hdr, name); } + case FUSE_FORGET: { - struct fuse_forget_in *req = data; - TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup); - /* no reply */ - while (req->nlookup--) - node_release(node); - return; + const struct fuse_forget_in *req = data; + return handle_forget(fuse, handler, hdr, req); } - case FUSE_GETATTR: { /* getattr_in -> attr_out */ - struct fuse_getattr_in *req = data; - struct fuse_attr_out out; - - TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh); - - memset(&out, 0, sizeof(out)); - node_get_attr(node, &out.attr); - out.attr_valid = 10; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_GETATTR: { /* getattr_in -> attr_out */ + const struct fuse_getattr_in *req = data; + return handle_getattr(fuse, handler, hdr, req); } - case FUSE_SETATTR: { /* setattr_in -> attr_out */ - struct fuse_setattr_in *req = data; - struct fuse_attr_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - int res = 0; - struct timespec times[2]; - - TRACE("SETATTR fh=%llx id=%llx valid=%x\n", - req->fh, hdr->nodeid, req->valid); - - /* XXX: incomplete implementation on purpose. chmod/chown - * should NEVER be implemented.*/ - - path = node_get_path(node, buffer, 0); - if (req->valid & FATTR_SIZE) - res = truncate(path, req->size); - if (res) - goto getout; - - /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW - * are both set, then set it to the current time. Else, set it to the - * time specified in the request. Same goes for mtime. Use utimensat(2) - * as it allows ATIME and MTIME to be changed independently, and has - * nanosecond resolution which fuse also has. - */ - if (req->valid & (FATTR_ATIME | FATTR_MTIME)) { - times[0].tv_nsec = UTIME_OMIT; - times[1].tv_nsec = UTIME_OMIT; - if (req->valid & FATTR_ATIME) { - if (req->valid & FATTR_ATIME_NOW) { - times[0].tv_nsec = UTIME_NOW; - } else { - times[0].tv_sec = req->atime; - times[0].tv_nsec = req->atimensec; - } - } - if (req->valid & FATTR_MTIME) { - if (req->valid & FATTR_MTIME_NOW) { - times[1].tv_nsec = UTIME_NOW; - } else { - times[1].tv_sec = req->mtime; - times[1].tv_nsec = req->mtimensec; - } - } - TRACE("Calling utimensat on %s with atime %ld, mtime=%ld\n", path, times[0].tv_sec, times[1].tv_sec); - res = utimensat(-1, path, times, 0); - } - - getout: - memset(&out, 0, sizeof(out)); - node_get_attr(node, &out.attr); - out.attr_valid = 10; - if (res) - fuse_status(fuse, hdr->unique, -errno); - else - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_SETATTR: { /* setattr_in -> attr_out */ + const struct fuse_setattr_in *req = data; + return handle_setattr(fuse, handler, hdr, req); } + // case FUSE_READLINK: // case FUSE_SYMLINK: case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */ - struct fuse_mknod_in *req = data; - char *path, buffer[PATH_BUFFER_SIZE]; - char *name = ((char*) data) + sizeof(*req); - int res; - - TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid); - path = node_get_path(node, buffer, name); - - req->mode = (req->mode & (~0777)) | 0664; - res = mknod(path, req->mode, req->rdev); /* XXX perm?*/ - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - } else { - lookup_entry(fuse, node, name, hdr->unique); - } - return; + const struct fuse_mknod_in *req = data; + const char *name = ((const char*) data) + sizeof(*req); + return handle_mknod(fuse, handler, hdr, req, name); } + case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */ - struct fuse_mkdir_in *req = data; - struct fuse_entry_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - char *name = ((char*) data) + sizeof(*req); - int res; - - TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode); - path = node_get_path(node, buffer, name); - - req->mode = (req->mode & (~0777)) | 0775; - res = mkdir(path, req->mode); - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - } else { - lookup_entry(fuse, node, name, hdr->unique); - } - return; + const struct fuse_mkdir_in *req = data; + const char *name = ((const char*) data) + sizeof(*req); + return handle_mkdir(fuse, handler, hdr, req, name); } + case FUSE_UNLINK: { /* bytez[] -> */ - char *path, buffer[PATH_BUFFER_SIZE]; - int res; - TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid); - path = node_get_path(node, buffer, (char*) data); - res = unlink(path); - fuse_status(fuse, hdr->unique, res ? -errno : 0); - return; + const char* name = data; + return handle_unlink(fuse, handler, hdr, name); } + case FUSE_RMDIR: { /* bytez[] -> */ - char *path, buffer[PATH_BUFFER_SIZE]; - int res; - TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid); - path = node_get_path(node, buffer, (char*) data); - res = rmdir(path); - fuse_status(fuse, hdr->unique, res ? -errno : 0); - return; + const char* name = data; + return handle_rmdir(fuse, handler, hdr, name); } - case FUSE_RENAME: { /* rename_in, oldname, newname -> */ - struct fuse_rename_in *req = data; - char *oldname = ((char*) data) + sizeof(*req); - char *newname = oldname + strlen(oldname) + 1; - char *oldpath, oldbuffer[PATH_BUFFER_SIZE]; - char *newpath, newbuffer[PATH_BUFFER_SIZE]; - struct node *target; - struct node *newparent; - int res; - - TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid); - - target = lookup_child_by_name(node, oldname); - if (!target) { - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - oldpath = node_get_path(node, oldbuffer, oldname); - - newparent = lookup_by_inode(fuse, req->newdir); - if (!newparent) { - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - if (newparent == node) { - /* Special case for renaming a file where destination - * is same path differing only by case. - * In this case we don't want to look for a case insensitive match. - * This allows commands like "mv foo FOO" to work as expected. - */ - newpath = do_node_get_path(newparent, newbuffer, newname, NO_CASE_SENSITIVE_MATCH); - } else { - newpath = node_get_path(newparent, newbuffer, newname); - } - - if (!remove_child(node, target->nid)) { - ERROR("RENAME remove_child not found"); - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - if (!rename_node(target, newname)) { - fuse_status(fuse, hdr->unique, -ENOMEM); - return; - } - add_node_to_parent(target, newparent); - res = rename(oldpath, newpath); - TRACE("RENAME result %d\n", res); - - fuse_status(fuse, hdr->unique, res ? -errno : 0); - return; + case FUSE_RENAME: { /* rename_in, oldname, newname -> */ + const struct fuse_rename_in *req = data; + const char *old_name = ((const char*) data) + sizeof(*req); + const char *new_name = old_name + strlen(old_name) + 1; + return handle_rename(fuse, handler, hdr, req, old_name, new_name); } -// case FUSE_LINK: - case FUSE_OPEN: { /* open_in -> open_out */ - struct fuse_open_in *req = data; - struct fuse_open_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - struct handle *h; - - h = malloc(sizeof(*h)); - if (!h) { - fuse_status(fuse, hdr->unique, -ENOMEM); - return; - } - path = node_get_path(node, buffer, 0); - TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h); - h->fd = open(path, req->flags); - if (h->fd < 0) { - ERROR("ERROR\n"); - fuse_status(fuse, hdr->unique, -errno); - free(h); - return; - } - out.fh = ptr_to_id(h); - out.open_flags = 0; - out.padding = 0; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; +// case FUSE_LINK: + case FUSE_OPEN: { /* open_in -> open_out */ + const struct fuse_open_in *req = data; + return handle_open(fuse, handler, hdr, req); } + case FUSE_READ: { /* read_in -> byte[] */ - char buffer[128 * 1024]; - struct fuse_read_in *req = data; - struct handle *h = id_to_ptr(req->fh); - int res; - TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); - if (req->size > sizeof(buffer)) { - fuse_status(fuse, hdr->unique, -EINVAL); - return; - } - res = pread64(h->fd, buffer, req->size, req->offset); - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - return; - } - fuse_reply(fuse, hdr->unique, buffer, res); - return; + const struct fuse_read_in *req = data; + return handle_read(fuse, handler, hdr, req); } + case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */ - struct fuse_write_in *req = data; - struct fuse_write_out out; - struct handle *h = id_to_ptr(req->fh); - int res; - TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); - res = pwrite64(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset); - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - return; - } - out.size = res; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - goto oops; + const struct fuse_write_in *req = data; + const void* buffer = (const __u8*)data + sizeof(*req); + return handle_write(fuse, handler, hdr, req, buffer); } - case FUSE_STATFS: { /* getattr_in -> attr_out */ - struct statfs stat; - struct fuse_statfs_out out; - int res; - - TRACE("STATFS\n"); - - if (statfs(fuse->root.name, &stat)) { - fuse_status(fuse, hdr->unique, -errno); - return; - } - memset(&out, 0, sizeof(out)); - out.st.blocks = stat.f_blocks; - out.st.bfree = stat.f_bfree; - out.st.bavail = stat.f_bavail; - out.st.files = stat.f_files; - out.st.ffree = stat.f_ffree; - out.st.bsize = stat.f_bsize; - out.st.namelen = stat.f_namelen; - out.st.frsize = stat.f_frsize; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_STATFS: { /* getattr_in -> attr_out */ + return handle_statfs(fuse, handler, hdr); } + case FUSE_RELEASE: { /* release_in -> */ - struct fuse_release_in *req = data; - struct handle *h = id_to_ptr(req->fh); - TRACE("RELEASE %p(%d)\n", h, h->fd); - close(h->fd); - free(h); - fuse_status(fuse, hdr->unique, 0); - return; + const struct fuse_release_in *req = data; + return handle_release(fuse, handler, hdr, req); } -// case FUSE_FSYNC: + + case FUSE_FSYNC: { + const struct fuse_fsync_in *req = data; + return handle_fsync(fuse, handler, hdr, req); + } + // case FUSE_SETXATTR: // case FUSE_GETXATTR: // case FUSE_LISTXATTR: // case FUSE_REMOVEXATTR: - case FUSE_FLUSH: - fuse_status(fuse, hdr->unique, 0); - return; - case FUSE_OPENDIR: { /* open_in -> open_out */ - struct fuse_open_in *req = data; - struct fuse_open_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - struct dirhandle *h; - - h = malloc(sizeof(*h)); - if (!h) { - fuse_status(fuse, hdr->unique, -ENOMEM); - return; - } + case FUSE_FLUSH: { + return handle_flush(fuse, handler, hdr); + } - path = node_get_path(node, buffer, 0); - TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path); - h->d = opendir(path); - if (h->d == 0) { - ERROR("ERROR\n"); - fuse_status(fuse, hdr->unique, -errno); - free(h); - return; - } - out.fh = ptr_to_id(h); - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_OPENDIR: { /* open_in -> open_out */ + const struct fuse_open_in *req = data; + return handle_opendir(fuse, handler, hdr, req); } + case FUSE_READDIR: { - struct fuse_read_in *req = data; - char buffer[8192]; - struct fuse_dirent *fde = (struct fuse_dirent*) buffer; - struct dirent *de; - struct dirhandle *h = id_to_ptr(req->fh); - TRACE("READDIR %p\n", h); - if (req->offset == 0) { - /* rewinddir() might have been called above us, so rewind here too */ - TRACE("calling rewinddir()\n"); - rewinddir(h->d); - } - de = readdir(h->d); - if (!de) { - fuse_status(fuse, hdr->unique, 0); - return; - } - fde->ino = FUSE_UNKNOWN_INO; - /* increment the offset so we can detect when rewinddir() seeks back to the beginning */ - fde->off = req->offset + 1; - fde->type = de->d_type; - fde->namelen = strlen(de->d_name); - memcpy(fde->name, de->d_name, fde->namelen + 1); - fuse_reply(fuse, hdr->unique, fde, - FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); - return; + const struct fuse_read_in *req = data; + return handle_readdir(fuse, handler, hdr, req); } + case FUSE_RELEASEDIR: { /* release_in -> */ - struct fuse_release_in *req = data; - struct dirhandle *h = id_to_ptr(req->fh); - TRACE("RELEASEDIR %p\n",h); - closedir(h->d); - free(h); - fuse_status(fuse, hdr->unique, 0); - return; + const struct fuse_release_in *req = data; + return handle_releasedir(fuse, handler, hdr, req); } + // case FUSE_FSYNCDIR: case FUSE_INIT: { /* init_in -> init_out */ - struct fuse_init_in *req = data; - struct fuse_init_out out; - - TRACE("INIT ver=%d.%d maxread=%d flags=%x\n", - req->major, req->minor, req->max_readahead, req->flags); - - out.major = FUSE_KERNEL_VERSION; - out.minor = FUSE_KERNEL_MINOR_VERSION; - out.max_readahead = req->max_readahead; - out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; - out.max_background = 32; - out.congestion_threshold = 32; - out.max_write = 256 * 1024; - - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + const struct fuse_init_in *req = data; + return handle_init(fuse, handler, hdr, req); } + default: { - struct fuse_out_header h; - ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n", - hdr->opcode, hdr->unique, hdr->nodeid); - - oops: - h.len = sizeof(h); - h.error = -ENOSYS; - h.unique = hdr->unique; - write(fuse->fd, &h, sizeof(h)); - break; - } - } + TRACE("[%d] NOTIMPL op=%d uniq=%llx nid=%llx\n", + handler->token, hdr->opcode, hdr->unique, hdr->nodeid); + return -ENOSYS; + } + } } -void handle_fuse_requests(struct fuse *fuse) +static void handle_fuse_requests(struct fuse_handler* handler) { - unsigned char req[256 * 1024 + 128]; - int len; - + struct fuse* fuse = handler->fuse; for (;;) { - len = read(fuse->fd, req, sizeof(req)); + ssize_t len = read(fuse->fd, + handler->request_buffer, sizeof(handler->request_buffer)); if (len < 0) { - if (errno == EINTR) - continue; - ERROR("handle_fuse_requests: errno=%d\n", errno); - return; + if (errno != EINTR) { + ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno); + } + continue; + } + + if ((size_t)len < sizeof(struct fuse_in_header)) { + ERROR("[%d] request too short: len=%zu\n", handler->token, (size_t)len); + continue; + } + + const struct fuse_in_header *hdr = (void*)handler->request_buffer; + if (hdr->len != (size_t)len) { + ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n", + handler->token, (size_t)len, hdr->len); + continue; + } + + const void *data = handler->request_buffer + sizeof(struct fuse_in_header); + size_t data_len = len - sizeof(struct fuse_in_header); + __u64 unique = hdr->unique; + int res = handle_fuse_request(fuse, handler, hdr, data, data_len); + + /* We do not access the request again after this point because the underlying + * buffer storage may have been reused while processing the request. */ + + if (res != NO_STATUS) { + if (res) { + TRACE("[%d] ERROR %d\n", handler->token, res); + } + fuse_status(fuse, unique, res); + } + } +} + +static void* start_handler(void* data) +{ + struct fuse_handler* handler = data; + handle_fuse_requests(handler); + return NULL; +} + +static int ignite_fuse(struct fuse* fuse, int num_threads) +{ + struct fuse_handler* handlers; + int i; + + handlers = malloc(num_threads * sizeof(struct fuse_handler)); + if (!handlers) { + ERROR("cannot allocate storage for threads"); + return -ENOMEM; + } + + for (i = 0; i < num_threads; i++) { + handlers[i].fuse = fuse; + handlers[i].token = i; + } + + for (i = 1; i < num_threads; i++) { + pthread_t thread; + int res = pthread_create(&thread, NULL, start_handler, &handlers[i]); + if (res) { + ERROR("failed to start thread #%d, error=%d", i, res); + goto quit; } - handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len); } + handle_fuse_requests(&handlers[0]); + ERROR("terminated prematurely"); + + /* don't bother killing all of the other threads or freeing anything, + * should never get here anyhow */ +quit: + exit(1); } static int usage() { - ERROR("usage: sdcard [-l -f] <path> <uid> <gid>\n\n\t-l force file names to lower case when creating new files\n\t-f fix up file system before starting (repairs bad file name case and group ownership)\n"); - return -1; + ERROR("usage: sdcard [-t<threads>] <source_path> <dest_path> <uid> <gid>\n" + " -t<threads>: specify number of threads to use, default -t%d\n" + "\n", DEFAULT_NUM_THREADS); + return 1; +} + +static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t gid, + int num_threads) { + int fd; + char opts[256]; + int res; + struct fuse fuse; + + /* cleanup from previous instance, if necessary */ + umount2(dest_path, 2); + + fd = open("/dev/fuse", O_RDWR); + if (fd < 0){ + ERROR("cannot open fuse device (error %d)\n", errno); + return -1; + } + + snprintf(opts, sizeof(opts), + "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d", + fd, uid, gid); + + res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV, opts); + if (res < 0) { + ERROR("cannot mount fuse filesystem (error %d)\n", errno); + goto error; + } + + res = setgid(gid); + if (res < 0) { + ERROR("cannot setgid (error %d)\n", errno); + goto error; + } + + res = setuid(uid); + if (res < 0) { + ERROR("cannot setuid (error %d)\n", errno); + goto error; + } + + fuse_init(&fuse, fd, source_path); + + umask(0); + res = ignite_fuse(&fuse, num_threads); + + /* we do not attempt to umount the file system here because we are no longer + * running as the root user */ + +error: + close(fd); + return res; } int main(int argc, char **argv) { - struct fuse fuse; - char opts[256]; - int fd; int res; - const char *path = NULL; + const char *source_path = NULL; + const char *dest_path = NULL; + uid_t uid = 0; + gid_t gid = 0; + int num_threads = DEFAULT_NUM_THREADS; int i; for (i = 1; i < argc; i++) { char* arg = argv[i]; - if (!path) - path = arg; - else if (uid == -1) + if (!strncmp(arg, "-t", 2)) + num_threads = strtoul(arg + 2, 0, 10); + else if (!source_path) + source_path = arg; + else if (!dest_path) + dest_path = arg; + else if (!uid) uid = strtoul(arg, 0, 10); - else if (gid == -1) + else if (!gid) gid = strtoul(arg, 0, 10); else { ERROR("too many arguments\n"); @@ -981,46 +1324,23 @@ int main(int argc, char **argv) } } - if (!path) { - ERROR("no path specified\n"); + if (!source_path) { + ERROR("no source path specified\n"); return usage(); } - if (uid <= 0 || gid <= 0) { - ERROR("uid and gid must be nonzero\n"); + if (!dest_path) { + ERROR("no dest path specified\n"); return usage(); } - - /* cleanup from previous instance, if necessary */ - umount2(MOUNT_POINT, 2); - - fd = open("/dev/fuse", O_RDWR); - if (fd < 0){ - ERROR("cannot open fuse device (%d)\n", errno); - return -1; - } - - sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other," - "user_id=%d,group_id=%d", fd, uid, gid); - - res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts); - if (res < 0) { - ERROR("cannot mount fuse filesystem (%d)\n", errno); - return -1; - } - - if (setgid(gid) < 0) { - ERROR("cannot setgid!\n"); - return -1; + if (!uid || !gid) { + ERROR("uid and gid must be nonzero\n"); + return usage(); } - if (setuid(uid) < 0) { - ERROR("cannot setuid!\n"); - return -1; + if (num_threads < 1) { + ERROR("number of threads must be at least 1\n"); + return usage(); } - fuse_init(&fuse, fd, path); - - umask(0); - handle_fuse_requests(&fuse); - - return 0; + res = run(source_path, dest_path, uid, gid, num_threads); + return res < 0 ? 1 : 0; } diff --git a/toolbox/Android.mk b/toolbox/Android.mk index be95e7c..086ba0d 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -56,6 +56,7 @@ TOOLS := \ ionice \ touch \ lsof \ + du \ md5 ifeq ($(HAVE_SELINUX),true) @@ -77,10 +78,17 @@ ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TOOLS += r endif -LOCAL_SRC_FILES:= \ +ALL_TOOLS = $(TOOLS) +ALL_TOOLS += \ + cp \ + grep + +LOCAL_SRC_FILES := \ dynarray.c \ toolbox.c \ - $(patsubst %,%.c,$(TOOLS)) + $(patsubst %,%.c,$(TOOLS)) \ + cp/cp.c cp/utils.c \ + grep/grep.c grep/fastgrep.c grep/file.c grep/queue.c grep/util.c LOCAL_SHARED_LIBRARIES := libcutils libc libusbhost @@ -94,7 +102,7 @@ LOCAL_C_INCLUDES += external/libselinux/include endif -LOCAL_MODULE:= toolbox +LOCAL_MODULE := toolbox # Including this will define $(intermediates). # @@ -103,7 +111,7 @@ include $(BUILD_EXECUTABLE) $(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h TOOLS_H := $(intermediates)/tools.h -$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS) +$(TOOLS_H): PRIVATE_TOOLS := $(ALL_TOOLS) $(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done $(TOOLS_H): $(LOCAL_PATH)/Android.mk $(TOOLS_H): @@ -111,7 +119,7 @@ $(TOOLS_H): # Make #!/system/bin/toolbox launchers for each tool. # -SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS)) +SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(ALL_TOOLS)) $(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE) $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk @echo "Symlink: $@ -> $(TOOLBOX_BINARY)" diff --git a/toolbox/cp/cp.c b/toolbox/cp/cp.c new file mode 100644 index 0000000..bd3c70e --- /dev/null +++ b/toolbox/cp/cp.c @@ -0,0 +1,552 @@ +/* $NetBSD: cp.c,v 1.58 2012/01/04 15:58:37 christos Exp $ */ + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT( +"@(#) Copyright (c) 1988, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cp.c 8.5 (Berkeley) 4/29/95"; +#else +__RCSID("$NetBSD: cp.c,v 1.58 2012/01/04 15:58:37 christos Exp $"); +#endif +#endif /* not lint */ + +/* + * Cp copies source files to target files. + * + * The global PATH_T structure "to" always contains the path to the + * current target file. Since fts(3) does not change directories, + * this path can be either absolute or dot-relative. + * + * The basic algorithm is to initialize "to" and use fts(3) to traverse + * the file hierarchy rooted in the argument list. A trivial case is the + * case of 'cp file1 file2'. The more interesting case is the case of + * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the + * path (relative to the root of the traversal) is appended to dir (stored + * in "to") to form the final target path. + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <locale.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ + *--(p).p_end = '\0'; \ +} + +static char empty[] = ""; +PATH_T to = { .p_end = to.p_path, .target_end = empty }; + +uid_t myuid; +int Hflag, Lflag, Rflag, Pflag, fflag, iflag, lflag, pflag, rflag, vflag, Nflag; +mode_t myumask; +sig_atomic_t pinfo; + +enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; + +static int copy(char *[], enum op, int); + +static void +progress(int sig __unused) +{ + + pinfo++; +} + +int +cp_main(int argc, char *argv[]) +{ + struct stat to_stat, tmp_stat; + enum op type; + int ch, fts_options, r, have_trailing_slash; + char *target, **src; + +#ifndef ANDROID + setprogname(argv[0]); +#endif + (void)setlocale(LC_ALL, ""); + + Hflag = Lflag = Pflag = Rflag = 0; + while ((ch = getopt(argc, argv, "HLNPRfailprv")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'N': + Nflag = 1; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'a': + Pflag = 1; + pflag = 1; + Rflag = 1; + Hflag = Lflag = 0; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + iflag = isatty(fileno(stdin)); + fflag = 0; + break; + case 'l': + lflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + cp_usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc < 2) + cp_usage(); + + fts_options = FTS_NOCHDIR | FTS_PHYSICAL; + if (rflag) { + if (Rflag) { + errx(EXIT_FAILURE, + "the -R and -r options may not be specified together."); + /* NOTREACHED */ + } + if (Hflag || Lflag || Pflag) { + errx(EXIT_FAILURE, + "the -H, -L, and -P options may not be specified with the -r option."); + /* NOTREACHED */ + } + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else if (!Pflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL | FTS_COMFOLLOW; + } + + myuid = getuid(); + + /* Copy the umask for explicit mode setting. */ + myumask = umask(0); + (void)umask(myumask); + + /* Save the target base in "to". */ + target = argv[--argc]; + if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path)) + errx(EXIT_FAILURE, "%s: name too long", target); + to.p_end = to.p_path + strlen(to.p_path); + have_trailing_slash = (to.p_end[-1] == '/'); + if (have_trailing_slash) + STRIP_TRAILING_SLASH(to); + to.target_end = to.p_end; + + /* Set end of argument list for fts(3). */ + argv[argc] = NULL; + +#ifndef ANDROID + (void)signal(SIGINFO, progress); +#endif + + /* + * Cp has two distinct cases: + * + * cp [-R] source target + * cp [-R] source1 ... sourceN directory + * + * In both cases, source can be either a file or a directory. + * + * In (1), the target becomes a copy of the source. That is, if the + * source is a file, the target will be a file, and likewise for + * directories. + * + * In (2), the real target is not directory, but "directory/source". + */ + if (Pflag) + r = lstat(to.p_path, &to_stat); + else + r = stat(to.p_path, &to_stat); + if (r == -1 && errno != ENOENT) { + err(EXIT_FAILURE, "%s", to.p_path); + /* NOTREACHED */ + } + if (r == -1 || !S_ISDIR(to_stat.st_mode)) { + /* + * Case (1). Target is not a directory. + */ + if (argc > 1) + cp_usage(); + /* + * Need to detect the case: + * cp -R dir foo + * Where dir is a directory and foo does not exist, where + * we want pathname concatenations turned on but not for + * the initial mkdir(). + */ + if (r == -1) { + if (rflag || (Rflag && (Lflag || Hflag))) + r = stat(*argv, &tmp_stat); + else + r = lstat(*argv, &tmp_stat); + if (r == -1) { + err(EXIT_FAILURE, "%s", *argv); + /* NOTREACHED */ + } + + if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag)) + type = DIR_TO_DNE; + else + type = FILE_TO_FILE; + } else + type = FILE_TO_FILE; + + if (have_trailing_slash && type == FILE_TO_FILE) { + if (r == -1) + errx(1, "directory %s does not exist", + to.p_path); + else + errx(1, "%s is not a directory", to.p_path); + } + } else { + /* + * Case (2). Target is a directory. + */ + type = FILE_TO_DIR; + } + + /* + * make "cp -rp src/ dst" behave like "cp -rp src dst" not + * like "cp -rp src/. dst" + */ + for (src = argv; *src; src++) { + size_t len = strlen(*src); + while (len-- > 1 && (*src)[len] == '/') + (*src)[len] = '\0'; + } + + exit(copy(argv, type, fts_options)); + /* NOTREACHED */ +} + +static int dnestack[MAXPATHLEN]; /* unlikely we'll have more nested dirs */ +static ssize_t dnesp; +static void +pushdne(int dne) +{ + + dnestack[dnesp++] = dne; + assert(dnesp < MAXPATHLEN); +} + +static int +popdne(void) +{ + int rv; + + rv = dnestack[--dnesp]; + assert(dnesp >= 0); + return rv; +} + +static int +copy(char *argv[], enum op type, int fts_options) +{ + struct stat to_stat; + FTS *ftsp; + FTSENT *curr; + int base, dne, sval; + int this_failed, any_failed; + size_t nlen; + char *p, *target_mid; + + base = 0; /* XXX gcc -Wuninitialized (see comment below) */ + + if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) + err(EXIT_FAILURE, "%s", argv[0]); + /* NOTREACHED */ + for (any_failed = 0; (curr = fts_read(ftsp)) != NULL;) { + this_failed = 0; + switch (curr->fts_info) { + case FTS_NS: + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", curr->fts_path, + strerror(curr->fts_errno)); + this_failed = any_failed = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + this_failed = any_failed = 1; + continue; + } + + /* + * If we are in case (2) or (3) above, we need to append the + * source name to the target name. + */ + if (type != FILE_TO_FILE) { + if ((curr->fts_namelen + + to.target_end - to.p_path + 1) > MAXPATHLEN) { + warnx("%s/%s: name too long (not copied)", + to.p_path, curr->fts_name); + this_failed = any_failed = 1; + continue; + } + + /* + * Need to remember the roots of traversals to create + * correct pathnames. If there's a directory being + * copied to a non-existent directory, e.g. + * cp -R a/dir noexist + * the resulting path name should be noexist/foo, not + * noexist/dir/foo (where foo is a file in dir), which + * is the case where the target exists. + * + * Also, check for "..". This is for correct path + * concatentation for paths ending in "..", e.g. + * cp -R .. /tmp + * Paths ending in ".." are changed to ".". This is + * tricky, but seems the easiest way to fix the problem. + * + * XXX + * Since the first level MUST be FTS_ROOTLEVEL, base + * is always initialized. + */ + if (curr->fts_level == FTS_ROOTLEVEL) { + if (type != DIR_TO_DNE) { + p = strrchr(curr->fts_path, '/'); + base = (p == NULL) ? 0 : + (int)(p - curr->fts_path + 1); + + if (!strcmp(&curr->fts_path[base], + "..")) + base += 1; + } else + base = curr->fts_pathlen; + } + + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + target_mid = to.target_end; + if (*p != '/' && target_mid[-1] != '/') + *target_mid++ = '/'; + *target_mid = 0; + + if (target_mid - to.p_path + nlen >= PATH_MAX) { + warnx("%s%s: name too long (not copied)", + to.p_path, p); + this_failed = any_failed = 1; + continue; + } + (void)strncat(target_mid, p, nlen); + to.p_end = target_mid + nlen; + *to.p_end = 0; + STRIP_TRAILING_SLASH(to); + } + + sval = Pflag ? lstat(to.p_path, &to_stat) : stat(to.p_path, &to_stat); + /* Not an error but need to remember it happened */ + if (sval == -1) + dne = 1; + else { + if (to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + this_failed = any_failed = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + if (!S_ISDIR(curr->fts_statp->st_mode) && + S_ISDIR(to_stat.st_mode)) { + warnx("cannot overwrite directory %s with non-directory %s", + to.p_path, curr->fts_path); + this_failed = any_failed = 1; + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + /* Catch special case of a non dangling symlink */ + if((fts_options & FTS_LOGICAL) || + ((fts_options & FTS_COMFOLLOW) && curr->fts_level == 0)) { + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + } else { + if (copy_link(curr, !dne)) + this_failed = any_failed = 1; + } + break; + case S_IFDIR: + if (!Rflag && !rflag) { + if (curr->fts_info == FTS_D) + warnx("%s is a directory (not copied).", + curr->fts_path); + (void)fts_set(ftsp, curr, FTS_SKIP); + this_failed = any_failed = 1; + break; + } + + /* + * Directories get noticed twice: + * In the first pass, create it if needed. + * In the second pass, after the children have been copied, set the permissions. + */ + if (curr->fts_info == FTS_D) /* First pass */ + { + /* + * If the directory doesn't exist, create the new + * one with the from file mode plus owner RWX bits, + * modified by the umask. Trade-off between being + * able to write the directory (if from directory is + * 555) and not causing a permissions race. If the + * umask blocks owner writes, we fail.. + */ + pushdne(dne); + if (dne) { + if (mkdir(to.p_path, + curr->fts_statp->st_mode | S_IRWXU) < 0) + err(EXIT_FAILURE, "%s", + to.p_path); + /* NOTREACHED */ + } else if (!S_ISDIR(to_stat.st_mode)) { + errno = ENOTDIR; + err(EXIT_FAILURE, "%s", + to.p_path); + /* NOTREACHED */ + } + } + else if (curr->fts_info == FTS_DP) /* Second pass */ + { + /* + * If not -p and directory didn't exist, set it to be + * the same as the from directory, umodified by the + * umask; arguably wrong, but it's been that way + * forever. + */ + if (pflag && setfile(curr->fts_statp, 0)) + this_failed = any_failed = 1; + else if ((dne = popdne())) + (void)chmod(to.p_path, + curr->fts_statp->st_mode); + } + else + { + warnx("directory %s encountered when not expected.", + curr->fts_path); + this_failed = any_failed = 1; + break; + } + + break; + case S_IFBLK: + case S_IFCHR: + if (Rflag) { + if (copy_special(curr->fts_statp, !dne)) + this_failed = any_failed = 1; + } else + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + break; + case S_IFIFO: + if (Rflag) { + if (copy_fifo(curr->fts_statp, !dne)) + this_failed = any_failed = 1; + } else + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + break; + default: + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + break; + } + if (vflag && !this_failed) + (void)printf("%s -> %s\n", curr->fts_path, to.p_path); + } + if (errno) { + err(EXIT_FAILURE, "fts_read"); + /* NOTREACHED */ + } + (void)fts_close(ftsp); + return (any_failed); +} diff --git a/toolbox/cp/extern.h b/toolbox/cp/extern.h new file mode 100644 index 0000000..ffbadf7 --- /dev/null +++ b/toolbox/cp/extern.h @@ -0,0 +1,61 @@ +/* $NetBSD: extern.h,v 1.17 2012/01/04 15:58:37 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.2 (Berkeley) 4/1/94 + */ + +#ifndef _EXTERN_H_ +#define _EXTERN_H_ + +typedef struct { + char *p_end; /* pointer to NULL at end of path */ + char *target_end; /* pointer to end of target base */ + char p_path[MAXPATHLEN + 1]; /* pointer to the start of a path */ +} PATH_T; + +extern PATH_T to; +extern uid_t myuid; +extern int Rflag, rflag, Hflag, Lflag, Pflag, fflag, iflag, lflag, pflag, Nflag; +extern mode_t myumask; +extern sig_atomic_t pinfo; + +#include <sys/cdefs.h> + +__BEGIN_DECLS +int copy_fifo(struct stat *, int); +int copy_file(FTSENT *, int); +int copy_link(FTSENT *, int); +int copy_special(struct stat *, int); +int set_utimes(const char *, struct stat *); +int setfile(struct stat *, int); +void cp_usage(void) __attribute__((__noreturn__)); +__END_DECLS + +#endif /* !_EXTERN_H_ */ diff --git a/toolbox/cp/utils.c b/toolbox/cp/utils.c new file mode 100644 index 0000000..b682bbe --- /dev/null +++ b/toolbox/cp/utils.c @@ -0,0 +1,446 @@ +/* $NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; +#else +__RCSID("$NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $"); +#endif +#endif /* not lint */ + +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#ifndef ANDROID +#include <sys/extattr.h> +#endif + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#ifdef ANDROID +#define MAXBSIZE 65536 +#endif + +#define MMAP_MAX_SIZE (8 * 1048576) +#define MMAP_MAX_WRITE (64 * 1024) + +int +set_utimes(const char *file, struct stat *fs) +{ + static struct timeval tv[2]; + +#ifdef ANDROID + tv[0].tv_sec = fs->st_atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = fs->st_mtime; + tv[1].tv_usec = 0; + + if (utimes(file, tv)) { + warn("utimes: %s", file); + return 1; + } +#else + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + + if (lutimes(file, tv)) { + warn("lutimes: %s", file); + return (1); + } +#endif + return (0); +} + +struct finfo { + const char *from; + const char *to; + size_t size; +}; + +static void +progress(const struct finfo *fi, size_t written) +{ + int pcent = (int)((100.0 * written) / fi->size); + + pinfo = 0; + (void)fprintf(stderr, "%s => %s %zu/%zu bytes %d%% written\n", + fi->from, fi->to, written, fi->size, pcent); +} + +int +copy_file(FTSENT *entp, int dne) +{ + static char buf[MAXBSIZE]; + struct stat to_stat, *fs; + int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount; + char *p; + size_t ptotal = 0; + + if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { + warn("%s", entp->fts_path); + return (1); + } + + to_fd = -1; + fs = entp->fts_statp; + tolnk = ((Rflag && !(Lflag || Hflag)) || Pflag); + + /* + * If the file exists and we're interactive, verify with the user. + * If the file DNE, set the mode to be the from file, minus setuid + * bits, modified by the umask; arguably wrong, but it makes copying + * executables work right and it's been that way forever. (The + * other choice is 666 or'ed with the execute bits on the from file + * modified by the umask.) + */ + if (!dne) { + struct stat sb; + int sval; + + if (iflag) { + (void)fprintf(stderr, "overwrite %s? ", to.p_path); + checkch = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (checkch != 'y' && checkch != 'Y') { + (void)close(from_fd); + return (0); + } + } + + sval = tolnk ? + lstat(to.p_path, &sb) : stat(to.p_path, &sb); + if (sval == -1) { + warn("stat: %s", to.p_path); + (void)close(from_fd); + return (1); + } + + if (!(tolnk && S_ISLNK(sb.st_mode))) + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + } else + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + + if (to_fd == -1 && (fflag || tolnk)) { + /* + * attempt to remove existing destination file name and + * create a new file + */ + (void)unlink(to.p_path); + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } + + if (to_fd == -1) { + warn("%s", to.p_path); + (void)close(from_fd); + return (1); + } + + rval = 0; + + /* if hard linking then simply close the open fds, link and return */ + if (lflag) { + (void)close(from_fd); + (void)close(to_fd); + (void)unlink(to.p_path); + if (link(entp->fts_path, to.p_path)) { + warn("%s", to.p_path); + return (1); + } + return (0); + } + /* NOTREACHED */ + + /* + * There's no reason to do anything other than close the file + * now if it's empty, so let's not bother. + */ + if (fs->st_size > 0) { + struct finfo fi; + + fi.from = entp->fts_path; + fi.to = to.p_path; + fi.size = (size_t)fs->st_size; + + /* + * Mmap and write if less than 8M (the limit is so + * we don't totally trash memory on big files). + * This is really a minor hack, but it wins some CPU back. + */ + bool use_read; + + use_read = true; + if (fs->st_size <= MMAP_MAX_SIZE) { + size_t fsize = (size_t)fs->st_size; + p = mmap(NULL, fsize, PROT_READ, MAP_FILE|MAP_SHARED, + from_fd, (off_t)0); + if (p != MAP_FAILED) { + size_t remainder; + + use_read = false; + + (void) madvise(p, (size_t)fs->st_size, + MADV_SEQUENTIAL); + + /* + * Write out the data in small chunks to + * avoid locking the output file for a + * long time if the reading the data from + * the source is slow. + */ + remainder = fsize; + do { + ssize_t chunk; + + chunk = (remainder > MMAP_MAX_WRITE) ? + MMAP_MAX_WRITE : remainder; + if (write(to_fd, &p[fsize - remainder], + chunk) != chunk) { + warn("%s", to.p_path); + rval = 1; + break; + } + remainder -= chunk; + ptotal += chunk; + if (pinfo) + progress(&fi, ptotal); + } while (remainder > 0); + + if (munmap(p, fsize) < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } + + if (use_read) { + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { + wcount = write(to_fd, buf, (size_t)rcount); + if (rcount != wcount || wcount == -1) { + warn("%s", to.p_path); + rval = 1; + break; + } + ptotal += wcount; + if (pinfo) + progress(&fi, ptotal); + } + if (rcount < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } + +#ifndef ANDROID + if (pflag && (fcpxattr(from_fd, to_fd) != 0)) + warn("%s: error copying extended attributes", to.p_path); +#endif + + (void)close(from_fd); + + if (rval == 1) { + (void)close(to_fd); + return (1); + } + + if (pflag && setfile(fs, to_fd)) + rval = 1; + /* + * If the source was setuid or setgid, lose the bits unless the + * copy is owned by the same user and group. + */ +#define RETAINBITS \ + (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) + if (!pflag && dne + && fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) { + if (fstat(to_fd, &to_stat)) { + warn("%s", to.p_path); + rval = 1; + } else if (fs->st_gid == to_stat.st_gid && + fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { + warn("%s", to.p_path); + rval = 1; + } + } + if (close(to_fd)) { + warn("%s", to.p_path); + rval = 1; + } + /* set the mod/access times now after close of the fd */ + if (pflag && set_utimes(to.p_path, fs)) { + rval = 1; + } + return (rval); +} + +int +copy_link(FTSENT *p, int exists) +{ + int len; + char target[MAXPATHLEN]; + + if ((len = readlink(p->fts_path, target, sizeof(target)-1)) == -1) { + warn("readlink: %s", p->fts_path); + return (1); + } + target[len] = '\0'; + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (symlink(target, to.p_path)) { + warn("symlink: %s", target); + return (1); + } + return (pflag ? setfile(p->fts_statp, 0) : 0); +} + +int +copy_fifo(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mkfifo(to.p_path, from_stat->st_mode)) { + warn("mkfifo: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + +int +copy_special(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { + warn("mknod: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + + +/* + * Function: setfile + * + * Purpose: + * Set the owner/group/permissions for the "to" file to the information + * in the stat structure. If fd is zero, also call set_utimes() to set + * the mod/access times. If fd is non-zero, the caller must do a utimes + * itself after close(fd). + */ +int +setfile(struct stat *fs, int fd) +{ + int rval, islink; + + rval = 0; + islink = S_ISLNK(fs->st_mode); + fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; + + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : + lchown(to.p_path, fs->st_uid, fs->st_gid)) { + if (errno != EPERM) { + warn("chown: %s", to.p_path); + rval = 1; + } + fs->st_mode &= ~(S_ISUID | S_ISGID); + } +#ifdef ANDROID + if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { +#else + if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) { +#endif + warn("chmod: %s", to.p_path); + rval = 1; + } + +#ifndef ANDROID + if (!islink && !Nflag) { + unsigned long fflags = fs->st_flags; + /* + * XXX + * NFS doesn't support chflags; ignore errors unless + * there's reason to believe we're losing bits. + * (Note, this still won't be right if the server + * supports flags and we were trying to *remove* flags + * on a file that we copied, i.e., that we didn't create.) + */ + errno = 0; + if ((fd ? fchflags(fd, fflags) : + chflags(to.p_path, fflags)) == -1) + if (errno != EOPNOTSUPP || fs->st_flags != 0) { + warn("chflags: %s", to.p_path); + rval = 1; + } + } +#endif + /* if fd is non-zero, caller must call set_utimes() after close() */ + if (fd == 0 && set_utimes(to.p_path, fs)) + rval = 1; + return (rval); +} + +void +cp_usage(void) +{ + (void)fprintf(stderr, + "usage: cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src target\n" + " cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src1 ... srcN directory\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/toolbox/dd.c b/toolbox/dd.c index c6af3ea..350f1d2 100644 --- a/toolbox/dd.c +++ b/toolbox/dd.c @@ -64,7 +64,7 @@ __RCSID("$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $"); #include "dd.h" -#define NO_CONV +//#define NO_CONV //#include "extern.h" void block(void); @@ -91,13 +91,11 @@ extern u_int ddflags; extern u_int files_cnt; extern int progress; extern const u_char *ctab; -extern const u_char a2e_32V[], a2e_POSIX[]; -extern const u_char e2a_32V[], e2a_POSIX[]; -extern const u_char a2ibm_32V[], a2ibm_POSIX[]; -extern u_char casetab[]; +#define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define DEFFILEMODE (S_IRUSR | S_IWUSR) static void dd_close(void); static void dd_in(void); @@ -188,14 +186,14 @@ setup(void) } else { #define OFLAGS \ (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) - out.fd = open(out.name, O_RDWR | OFLAGS /*, DEFFILEMODE */); + out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); /* * May not have read access, so try again with write only. * Without read we may have a problem if output also does * not support seeks. */ if (out.fd < 0) { - out.fd = open(out.name, O_WRONLY | OFLAGS /*, DEFFILEMODE */); + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); out.flags |= NOREAD; } if (out.fd < 0) { @@ -243,42 +241,6 @@ setup(void) if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK)) (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); - /* - * If converting case at the same time as another conversion, build a - * table that does both at once. If just converting case, use the - * built-in tables. - */ - if (ddflags & (C_LCASE|C_UCASE)) { -#ifdef NO_CONV - /* Should not get here, but just in case... */ - fprintf(stderr, "case conv and -DNO_CONV\n"); - exit(1); - /* NOTREACHED */ -#else /* NO_CONV */ - u_int cnt; - - if (ddflags & C_ASCII || ddflags & C_EBCDIC) { - if (ddflags & C_LCASE) { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = tolower(ctab[cnt]); - } else { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = toupper(ctab[cnt]); - } - } else { - if (ddflags & C_LCASE) { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = tolower(cnt); - } else { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = toupper(cnt); - } - } - - ctab = casetab; -#endif /* NO_CONV */ - } - (void)gettimeofday(&st.start, NULL); /* Statistics timestamp. */ } @@ -796,6 +758,9 @@ def(void) void def_close(void) { + if (ddflags & C_FDATASYNC) { + fdatasync(out.fd); + } /* Just update the count, everything is already in the buffer. */ if (in.dbcnt) @@ -1301,21 +1266,14 @@ static const struct conv { u_int set, noset; const u_char *ctab; } clist[] = { - { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, { "block", C_BLOCK, C_UNBLOCK, NULL }, - { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, - { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, - { "lcase", C_LCASE, C_UCASE, NULL }, + { "fdatasync", C_FDATASYNC, 0, NULL }, { "noerror", C_NOERROR, 0, NULL }, { "notrunc", C_NOTRUNC, 0, NULL }, - { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, - { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, - { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, { "osync", C_OSYNC, C_BS, NULL }, { "sparse", C_SPARSE, 0, NULL }, { "swab", C_SWAB, 0, NULL }, { "sync", C_SYNC, 0, NULL }, - { "ucase", C_UCASE, C_LCASE, NULL }, { "unblock", C_UNBLOCK, C_BLOCK, NULL }, /* If you add items to this table, be sure to add the * conversions to the C_BS check in the jcl routine above. diff --git a/toolbox/dd.h b/toolbox/dd.h index cca1024..89f2833 100644 --- a/toolbox/dd.h +++ b/toolbox/dd.h @@ -91,3 +91,4 @@ typedef struct { #define C_UNBLOCK 0x80000 #define C_OSYNC 0x100000 #define C_SPARSE 0x200000 +#define C_FDATASYNC 0x400000 diff --git a/toolbox/du.c b/toolbox/du.c new file mode 100644 index 0000000..06374a4 --- /dev/null +++ b/toolbox/du.c @@ -0,0 +1,322 @@ +/* $NetBSD: du.c,v 1.33 2008/07/30 22:03:40 dsl Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Newcomb. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: du.c,v 1.33 2008/07/30 22:03:40 dsl Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <util.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +int linkchk(dev_t, ino_t); +void prstat(const char *, int64_t); +void usage(void); + +long blocksize; + +#define howmany(x, y) (((x)+((y)-1))/(y)) + +int +du_main(int argc, char *argv[]) +{ + FTS *fts; + FTSENT *p; + int64_t totalblocks; + int ftsoptions, listfiles; + int depth; + int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag; + const char *noargv[2]; + + Hflag = Lflag = aflag = cflag = dflag = gkmflag = sflag = 0; + totalblocks = 0; + ftsoptions = FTS_PHYSICAL; + depth = INT_MAX; + while ((ch = getopt(argc, argv, "HLPacd:ghkmnrsx")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + break; + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + depth = atoi(optarg); + if (depth < 0 || depth > SHRT_MAX) { + warnx("invalid argument to option d: %s", + optarg); + usage(); + } + break; + case 'g': + blocksize = 1024 * 1024 * 1024; + gkmflag = 1; + break; + case 'k': + blocksize = 1024; + gkmflag = 1; + break; + case 'm': + blocksize = 1024 * 1024; + gkmflag = 1; + break; + case 'r': + break; + case 's': + sflag = 1; + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * XXX + * Because of the way that fts(3) works, logical walks will not count + * the blocks actually used by symbolic links. We rationalize this by + * noting that users computing logical sizes are likely to do logical + * copies, so not counting the links is correct. The real reason is + * that we'd have to re-implement the kernel's symbolic link traversing + * algorithm to get this right. If, for example, you have relative + * symbolic links referencing other relative symbolic links, it gets + * very nasty, very fast. The bottom line is that it's documented in + * the man page, so it's a feature. + */ + if (Hflag) + ftsoptions |= FTS_COMFOLLOW; + if (Lflag) { + ftsoptions &= ~FTS_PHYSICAL; + ftsoptions |= FTS_LOGICAL; + } + + listfiles = 0; + if (aflag) { + if (sflag || dflag) + usage(); + listfiles = 1; + } else if (sflag) { + if (dflag) + usage(); + depth = 0; + } + + if (!*argv) { + noargv[0] = "."; + noargv[1] = NULL; + argv = __UNCONST(noargv); + } + + if (!gkmflag) + blocksize = 512; + blocksize /= 512; + + if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) + err(1, "fts_open `%s'", *argv); + + for (rval = 0; (p = fts_read(fts)) != NULL;) { + switch (p->fts_info) { + case FTS_D: /* Ignore. */ + break; + case FTS_DP: + p->fts_parent->fts_number += + p->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + /* + * If listing each directory, or not listing files + * or directories and this is post-order of the + * root of a traversal, display the total. + */ + if (p->fts_level <= depth + || (!listfiles && !p->fts_level)) + prstat(p->fts_path, p->fts_number); + break; + case FTS_DC: /* Ignore. */ + break; + case FTS_DNR: /* Warn, continue. */ + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + default: + if (p->fts_statp->st_nlink > 1 && + linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino)) + break; + /* + * If listing each file, or a non-directory file was + * the root of a traversal, display the total. + */ + if (listfiles || !p->fts_level) + prstat(p->fts_path, p->fts_statp->st_blocks); + p->fts_parent->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + } + } + if (errno) + err(1, "fts_read"); + if (cflag) + prstat("total", totalblocks); + exit(rval); +} + +void +prstat(const char *fname, int64_t blocks) +{ + (void)printf("%lld\t%s\n", + (long long)howmany(blocks, (int64_t)blocksize), + fname); +} + +int +linkchk(dev_t dev, ino_t ino) +{ + static struct entry { + dev_t dev; + ino_t ino; + } *htable; + static int htshift; /* log(allocated size) */ + static int htmask; /* allocated size - 1 */ + static int htused; /* 2*number of insertions */ + static int sawzero; /* Whether zero is in table or not */ + int h, h2; + uint64_t tmp; + /* this constant is (1<<64)/((1+sqrt(5))/2) + * aka (word size)/(golden ratio) + */ + const uint64_t HTCONST = 11400714819323198485ULL; + const int HTBITS = CHAR_BIT * sizeof(tmp); + + /* Never store zero in hashtable */ + if (dev == 0 && ino == 0) { + h = sawzero; + sawzero = 1; + return h; + } + + /* Extend hash table if necessary, keep load under 0.5 */ + if (htused<<1 >= htmask) { + struct entry *ohtable; + + if (!htable) + htshift = 10; /* starting hashtable size */ + else + htshift++; /* exponential hashtable growth */ + + htmask = (1 << htshift) - 1; + htused = 0; + + ohtable = htable; + htable = calloc(htmask+1, sizeof(*htable)); + if (!htable) + err(1, "calloc"); + + /* populate newly allocated hashtable */ + if (ohtable) { + int i; + for (i = 0; i <= htmask>>1; i++) + if (ohtable[i].ino || ohtable[i].dev) + linkchk(ohtable[i].dev, ohtable[i].ino); + free(ohtable); + } + } + + /* multiplicative hashing */ + tmp = dev; + tmp <<= HTBITS>>1; + tmp |= ino; + tmp *= HTCONST; + h = tmp >> (HTBITS - htshift); + h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ + + /* open address hashtable search with double hash probing */ + while (htable[h].ino || htable[h].dev) { + if ((htable[h].ino == ino) && (htable[h].dev == dev)) + return 1; + h = (h + h2) & htmask; + } + + /* Insert the current entry into hashtable */ + htable[h].dev = dev; + htable[h].ino = ino; + htused++; + return 0; +} + +void +usage(void) +{ + + (void)fprintf(stderr, + "usage: du [-H | -L | -P] [-a | -d depth | -s] [-cgkmrx] [file ...]\n"); + exit(1); +} diff --git a/toolbox/grep/fastgrep.c b/toolbox/grep/fastgrep.c new file mode 100644 index 0000000..2fcd864 --- /dev/null +++ b/toolbox/grep/fastgrep.c @@ -0,0 +1,336 @@ +/* $OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $ */ +/* $FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * XXX: This file is a speed up for grep to cover the defects of the + * regex library. These optimizations should practically be implemented + * there keeping this code clean. This is a future TODO, but for the + * meantime, we need to use this workaround. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $"); + +#include <limits.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> + +#include "grep.h" + +static inline int grep_cmp(const unsigned char *, const unsigned char *, size_t); +static inline void grep_revstr(unsigned char *, int); + +void +fgrepcomp(fastgrep_t *fg, const char *pat) +{ + unsigned int i; + + /* Initialize. */ + fg->len = strlen(pat); + fg->bol = false; + fg->eol = false; + fg->reversed = false; + + fg->pattern = (unsigned char *)grep_strdup(pat); + + /* Preprocess pattern. */ + for (i = 0; i <= UCHAR_MAX; i++) + fg->qsBc[i] = fg->len; + for (i = 1; i < fg->len; i++) + fg->qsBc[fg->pattern[i]] = fg->len - i; +} + +/* + * Returns: -1 on failure, 0 on success + */ +int +fastcomp(fastgrep_t *fg, const char *pat) +{ + unsigned int i; + int firstHalfDot = -1; + int firstLastHalfDot = -1; + int hasDot = 0; + int lastHalfDot = 0; + int shiftPatternLen; + + /* Initialize. */ + fg->len = strlen(pat); + fg->bol = false; + fg->eol = false; + fg->reversed = false; + fg->word = wflag; + + /* Remove end-of-line character ('$'). */ + if (fg->len > 0 && pat[fg->len - 1] == '$') { + fg->eol = true; + fg->len--; + } + + /* Remove beginning-of-line character ('^'). */ + if (pat[0] == '^') { + fg->bol = true; + fg->len--; + pat++; + } + + if (fg->len >= 14 && + memcmp(pat, "[[:<:]]", 7) == 0 && + memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) { + fg->len -= 14; + pat += 7; + /* Word boundary is handled separately in util.c */ + fg->word = true; + } + + /* + * pat has been adjusted earlier to not include '^', '$' or + * the word match character classes at the beginning and ending + * of the string respectively. + */ + fg->pattern = grep_malloc(fg->len + 1); + memcpy(fg->pattern, pat, fg->len); + fg->pattern[fg->len] = '\0'; + + /* Look for ways to cheat...er...avoid the full regex engine. */ + for (i = 0; i < fg->len; i++) { + /* Can still cheat? */ + if (fg->pattern[i] == '.') { + hasDot = i; + if (i < fg->len / 2) { + if (firstHalfDot < 0) + /* Closest dot to the beginning */ + firstHalfDot = i; + } else { + /* Closest dot to the end of the pattern. */ + lastHalfDot = i; + if (firstLastHalfDot < 0) + firstLastHalfDot = i; + } + } else { + /* Free memory and let others know this is empty. */ + free(fg->pattern); + fg->pattern = NULL; + return (-1); + } + } + + /* + * Determine if a reverse search would be faster based on the placement + * of the dots. + */ + if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) && + ((lastHalfDot) && ((firstHalfDot < 0) || + ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) && + !oflag && !color) { + fg->reversed = true; + hasDot = fg->len - (firstHalfDot < 0 ? + firstLastHalfDot : firstHalfDot) - 1; + grep_revstr(fg->pattern, fg->len); + } + + /* + * Normal Quick Search would require a shift based on the position the + * next character after the comparison is within the pattern. With + * wildcards, the position of the last dot effects the maximum shift + * distance. + * The closer to the end the wild card is the slower the search. A + * reverse version of this algorithm would be useful for wildcards near + * the end of the string. + * + * Examples: + * Pattern Max shift + * ------- --------- + * this 5 + * .his 4 + * t.is 3 + * th.s 2 + * thi. 1 + */ + + /* Adjust the shift based on location of the last dot ('.'). */ + shiftPatternLen = fg->len - hasDot; + + /* Preprocess pattern. */ + for (i = 0; i <= (signed)UCHAR_MAX; i++) + fg->qsBc[i] = shiftPatternLen; + for (i = hasDot + 1; i < fg->len; i++) { + fg->qsBc[fg->pattern[i]] = fg->len - i; + } + + /* + * Put pattern back to normal after pre-processing to allow for easy + * comparisons later. + */ + if (fg->reversed) + grep_revstr(fg->pattern, fg->len); + + return (0); +} + +int +grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch) +{ + unsigned int j; + int ret = REG_NOMATCH; + + if (pmatch->rm_so == (ssize_t)len) + return (ret); + + if (fg->bol && pmatch->rm_so != 0) { + pmatch->rm_so = len; + pmatch->rm_eo = len; + return (ret); + } + + /* No point in going farther if we do not have enough data. */ + if (len < fg->len) + return (ret); + + /* Only try once at the beginning or ending of the line. */ + if (fg->bol || fg->eol) { + /* Simple text comparison. */ + /* Verify data is >= pattern length before searching on it. */ + if (len >= fg->len) { + /* Determine where in data to start search at. */ + j = fg->eol ? len - fg->len : 0; + if (!((fg->bol && fg->eol) && (len != fg->len))) + if (grep_cmp(fg->pattern, data + j, + fg->len) == -1) { + pmatch->rm_so = j; + pmatch->rm_eo = j + fg->len; + ret = 0; + } + } + } else if (fg->reversed) { + /* Quick Search algorithm. */ + j = len; + do { + if (grep_cmp(fg->pattern, data + j - fg->len, + fg->len) == -1) { + pmatch->rm_so = j - fg->len; + pmatch->rm_eo = j; + ret = 0; + break; + } + /* Shift if within bounds, otherwise, we are done. */ + if (j == fg->len) + break; + j -= fg->qsBc[data[j - fg->len - 1]]; + } while (j >= fg->len); + } else { + /* Quick Search algorithm. */ + j = pmatch->rm_so; + do { + if (grep_cmp(fg->pattern, data + j, fg->len) == -1) { + pmatch->rm_so = j; + pmatch->rm_eo = j + fg->len; + ret = 0; + break; + } + + /* Shift if within bounds, otherwise, we are done. */ + if (j + fg->len == len) + break; + else + j += fg->qsBc[data[j + fg->len]]; + } while (j <= (len - fg->len)); + } + + return (ret); +} + +/* + * Returns: i >= 0 on failure (position that it failed) + * -1 on success + */ +static inline int +grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len) +{ + size_t size; + wchar_t *wdata, *wpat; + unsigned int i; + + if (iflag) { + if ((size = mbstowcs(NULL, (const char *)data, 0)) == + ((size_t) - 1)) + return (-1); + + wdata = grep_malloc(size * sizeof(wint_t)); + + if (mbstowcs(wdata, (const char *)data, size) == + ((size_t) - 1)) + return (-1); + + if ((size = mbstowcs(NULL, (const char *)pat, 0)) == + ((size_t) - 1)) + return (-1); + + wpat = grep_malloc(size * sizeof(wint_t)); + + if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1)) + return (-1); + for (i = 0; i < len; i++) { + if ((towlower(wpat[i]) == towlower(wdata[i])) || + ((grepbehave != GREP_FIXED) && wpat[i] == L'.')) + continue; + free(wpat); + free(wdata); + return (i); + } + } else { + for (i = 0; i < len; i++) { + if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) && + pat[i] == '.')) + continue; + return (i); + } + } + return (-1); +} + +static inline void +grep_revstr(unsigned char *str, int len) +{ + int i; + char c; + + for (i = 0; i < len / 2; i++) { + c = str[i]; + str[i] = str[len - i - 1]; + str[len - i - 1] = c; + } +} diff --git a/toolbox/grep/file.c b/toolbox/grep/file.c new file mode 100644 index 0000000..86b7658 --- /dev/null +++ b/toolbox/grep/file.c @@ -0,0 +1,269 @@ +/* $NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $ */ +/* $FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $ */ +/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org> + * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifndef ANDROID +#include <bzlib.h> +#endif +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> +#ifndef ANDROID +#include <zlib.h> +#endif + +#include "grep.h" + +#define MAXBUFSIZ (32 * 1024) +#define LNBUFBUMP 80 + +#ifndef ANDROID +static gzFile gzbufdesc; +static BZFILE* bzbufdesc; +#endif + +static unsigned char buffer[MAXBUFSIZ]; +static unsigned char *bufpos; +static size_t bufrem; + +static unsigned char *lnbuf; +static size_t lnbuflen; + +static inline int +grep_refill(struct file *f) +{ + ssize_t nr; + int bzerr; + + bufpos = buffer; + bufrem = 0; + +#ifndef ANDROID + if (filebehave == FILE_GZIP) + nr = gzread(gzbufdesc, buffer, MAXBUFSIZ); + else if (filebehave == FILE_BZIP && bzbufdesc != NULL) { + nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ); + switch (bzerr) { + case BZ_OK: + case BZ_STREAM_END: + /* No problem, nr will be okay */ + break; + case BZ_DATA_ERROR_MAGIC: + /* + * As opposed to gzread(), which simply returns the + * plain file data, if it is not in the correct + * compressed format, BZ2_bzRead() instead aborts. + * + * So, just restart at the beginning of the file again, + * and use plain reads from now on. + */ + BZ2_bzReadClose(&bzerr, bzbufdesc); + bzbufdesc = NULL; + if (lseek(f->fd, 0, SEEK_SET) == -1) + return (-1); + nr = read(f->fd, buffer, MAXBUFSIZ); + break; + default: + /* Make sure we exit with an error */ + nr = -1; + } + } else +#endif + nr = read(f->fd, buffer, MAXBUFSIZ); + + if (nr < 0) + return (-1); + + bufrem = nr; + return (0); +} + +static inline int +grep_lnbufgrow(size_t newlen) +{ + + if (lnbuflen < newlen) { + lnbuf = grep_realloc(lnbuf, newlen); + lnbuflen = newlen; + } + + return (0); +} + +char * +grep_fgetln(struct file *f, size_t *lenp) +{ + unsigned char *p; + char *ret; + size_t len; + size_t off; + ptrdiff_t diff; + + /* Fill the buffer, if necessary */ + if (bufrem == 0 && grep_refill(f) != 0) + goto error; + + if (bufrem == 0) { + /* Return zero length to indicate EOF */ + *lenp = 0; + return ((char *)bufpos); + } + + /* Look for a newline in the remaining part of the buffer */ + if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) { + ++p; /* advance over newline */ + ret = (char *)bufpos; + len = p - bufpos; + bufrem -= len; + bufpos = p; + *lenp = len; + return (ret); + } + + /* We have to copy the current buffered data to the line buffer */ + for (len = bufrem, off = 0; ; len += bufrem) { + /* Make sure there is room for more data */ + if (grep_lnbufgrow(len + LNBUFBUMP)) + goto error; + memcpy(lnbuf + off, bufpos, len - off); + off = len; + if (grep_refill(f) != 0) + goto error; + if (bufrem == 0) + /* EOF: return partial line */ + break; + if ((p = memchr(bufpos, line_sep, bufrem)) == NULL) + continue; + /* got it: finish up the line (like code above) */ + ++p; + diff = p - bufpos; + len += diff; + if (grep_lnbufgrow(len)) + goto error; + memcpy(lnbuf + off, bufpos, diff); + bufrem -= diff; + bufpos = p; + break; + } + *lenp = len; + return ((char *)lnbuf); + +error: + *lenp = 0; + return (NULL); +} + +static inline struct file * +grep_file_init(struct file *f) +{ + +#ifndef ANDROID + if (filebehave == FILE_GZIP && + (gzbufdesc = gzdopen(f->fd, "r")) == NULL) + goto error; + + if (filebehave == FILE_BZIP && + (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL) + goto error; +#endif + + /* Fill read buffer, also catches errors early */ + if (grep_refill(f) != 0) + goto error; + + /* Check for binary stuff, if necessary */ + if (!nulldataflag && binbehave != BINFILE_TEXT && + memchr(bufpos, '\0', bufrem) != NULL) + f->binary = true; + + return (f); +error: + close(f->fd); + free(f); + return (NULL); +} + +/* + * Opens a file for processing. + */ +struct file * +grep_open(const char *path) +{ + struct file *f; + + f = grep_malloc(sizeof *f); + memset(f, 0, sizeof *f); + if (path == NULL) { + /* Processing stdin implies --line-buffered. */ + lbflag = true; + f->fd = STDIN_FILENO; + } else if ((f->fd = open(path, O_RDONLY)) == -1) { + free(f); + return (NULL); + } + + return (grep_file_init(f)); +} + +/* + * Closes a file. + */ +void +grep_close(struct file *f) +{ + + close(f->fd); + + /* Reset read buffer and line buffer */ + bufpos = buffer; + bufrem = 0; + + free(lnbuf); + lnbuf = NULL; + lnbuflen = 0; +} diff --git a/toolbox/grep/grep.c b/toolbox/grep/grep.c new file mode 100644 index 0000000..b5bb2ef --- /dev/null +++ b/toolbox/grep/grep.c @@ -0,0 +1,710 @@ +/* $NetBSD: grep.c,v 1.11 2012/05/06 22:27:00 joerg Exp $ */ +/* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */ +/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: grep.c,v 1.11 2012/05/06 22:27:00 joerg Exp $"); + +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <limits.h> +#include <libgen.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "grep.h" + +#ifndef WITHOUT_NLS +#include <nl_types.h> +nl_catd catalog; +#endif + +/* + * Default messags to use when NLS is disabled or no catalogue + * is found. + */ +const char *errstr[] = { + "", +/* 1*/ "(standard input)", +/* 2*/ "cannot read bzip2 compressed file", +/* 3*/ "unknown %s option", +/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n", +/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", +/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", +/* 7*/ "\t[pattern] [file ...]\n", +/* 8*/ "Binary file %s matches\n", +/* 9*/ "%s (BSD grep) %s\n", +}; + +/* Flags passed to regcomp() and regexec() */ +int cflags = 0; +int eflags = REG_STARTEND; + +/* Searching patterns */ +unsigned int patterns, pattern_sz; +char **pattern; +regex_t *r_pattern; +fastgrep_t *fg_pattern; + +/* Filename exclusion/inclusion patterns */ +unsigned int fpatterns, fpattern_sz; +unsigned int dpatterns, dpattern_sz; +struct epat *dpattern, *fpattern; + +/* For regex errors */ +char re_error[RE_ERROR_BUF + 1]; + +/* Command-line flags */ +unsigned long long Aflag; /* -A x: print x lines trailing each match */ +unsigned long long Bflag; /* -B x: print x lines leading each match */ +bool Hflag; /* -H: always print file name */ +bool Lflag; /* -L: only show names of files with no matches */ +bool bflag; /* -b: show block numbers for each match */ +bool cflag; /* -c: only show a count of matching lines */ +bool hflag; /* -h: don't print filename headers */ +bool iflag; /* -i: ignore case */ +bool lflag; /* -l: only show names of files with matches */ +bool mflag; /* -m x: stop reading the files after x matches */ +unsigned long long mcount; /* count for -m */ +bool nflag; /* -n: show line numbers in front of matching lines */ +bool oflag; /* -o: print only matching part */ +bool qflag; /* -q: quiet mode (don't output anything) */ +bool sflag; /* -s: silent mode (ignore errors) */ +bool vflag; /* -v: only show non-matching lines */ +bool wflag; /* -w: pattern must start and end on word boundaries */ +bool xflag; /* -x: pattern must match entire line */ +bool lbflag; /* --line-buffered */ +bool nullflag; /* --null */ +bool nulldataflag; /* --null-data */ +unsigned char line_sep = '\n'; /* 0 for --null-data */ +char *label; /* --label */ +const char *color; /* --color */ +int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ +int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ +int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ +int devbehave = DEV_READ; /* -D: handling of devices */ +int dirbehave = DIR_READ; /* -dRr: handling of directories */ +int linkbehave = LINK_READ; /* -OpS: handling of symlinks */ + +bool dexclude, dinclude; /* --exclude-dir and --include-dir */ +bool fexclude, finclude; /* --exclude and --include */ + +enum { + BIN_OPT = CHAR_MAX + 1, + COLOR_OPT, + DECOMPRESS_OPT, + HELP_OPT, + MMAP_OPT, + LINEBUF_OPT, + LABEL_OPT, + R_EXCLUDE_OPT, + R_INCLUDE_OPT, + R_DEXCLUDE_OPT, + R_DINCLUDE_OPT +}; + +static inline const char *init_color(const char *); + +/* Housekeeping */ +int tail; /* lines left to print */ +bool notfound; /* file not found */ + +extern char *__progname; + +/* + * Prints usage information and returns 2. + */ +__dead static void +usage(void) +{ + fprintf(stderr, getstr(4), __progname); + fprintf(stderr, "%s", getstr(5)); + fprintf(stderr, "%s", getstr(5)); + fprintf(stderr, "%s", getstr(6)); + fprintf(stderr, "%s", getstr(7)); + exit(2); +} + +static const char optstr[] = + "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz"; + +struct option long_options[] = +{ + {"binary-files", required_argument, NULL, BIN_OPT}, + {"decompress", no_argument, NULL, DECOMPRESS_OPT}, + {"help", no_argument, NULL, HELP_OPT}, + {"mmap", no_argument, NULL, MMAP_OPT}, + {"line-buffered", no_argument, NULL, LINEBUF_OPT}, + {"label", required_argument, NULL, LABEL_OPT}, + {"color", optional_argument, NULL, COLOR_OPT}, + {"colour", optional_argument, NULL, COLOR_OPT}, + {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, + {"include", required_argument, NULL, R_INCLUDE_OPT}, + {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, + {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, + {"after-context", required_argument, NULL, 'A'}, + {"text", no_argument, NULL, 'a'}, + {"before-context", required_argument, NULL, 'B'}, + {"byte-offset", no_argument, NULL, 'b'}, + {"context", optional_argument, NULL, 'C'}, + {"count", no_argument, NULL, 'c'}, + {"devices", required_argument, NULL, 'D'}, + {"directories", required_argument, NULL, 'd'}, + {"extended-regexp", no_argument, NULL, 'E'}, + {"regexp", required_argument, NULL, 'e'}, + {"fixed-strings", no_argument, NULL, 'F'}, + {"file", required_argument, NULL, 'f'}, + {"basic-regexp", no_argument, NULL, 'G'}, + {"no-filename", no_argument, NULL, 'h'}, + {"with-filename", no_argument, NULL, 'H'}, + {"ignore-case", no_argument, NULL, 'i'}, + {"bz2decompress", no_argument, NULL, 'J'}, + {"files-with-matches", no_argument, NULL, 'l'}, + {"files-without-match", no_argument, NULL, 'L'}, + {"max-count", required_argument, NULL, 'm'}, + {"line-number", no_argument, NULL, 'n'}, + {"only-matching", no_argument, NULL, 'o'}, + {"quiet", no_argument, NULL, 'q'}, + {"silent", no_argument, NULL, 'q'}, + {"recursive", no_argument, NULL, 'r'}, + {"no-messages", no_argument, NULL, 's'}, + {"binary", no_argument, NULL, 'U'}, + {"unix-byte-offsets", no_argument, NULL, 'u'}, + {"invert-match", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"word-regexp", no_argument, NULL, 'w'}, + {"line-regexp", no_argument, NULL, 'x'}, + {"null", no_argument, NULL, 'Z'}, + {"null-data", no_argument, NULL, 'z'}, + {NULL, no_argument, NULL, 0} +}; + +/* + * Adds a searching pattern to the internal array. + */ +static void +add_pattern(char *pat, size_t len) +{ + + /* TODO: Check for empty patterns and shortcut */ + + /* Increase size if necessary */ + if (patterns == pattern_sz) { + pattern_sz *= 2; + pattern = grep_realloc(pattern, ++pattern_sz * + sizeof(*pattern)); + } + if (len > 0 && pat[len - 1] == '\n') + --len; + /* pat may not be NUL-terminated */ + pattern[patterns] = grep_malloc(len + 1); + memcpy(pattern[patterns], pat, len); + pattern[patterns][len] = '\0'; + ++patterns; +} + +/* + * Adds a file include/exclude pattern to the internal array. + */ +static void +add_fpattern(const char *pat, int mode) +{ + + /* Increase size if necessary */ + if (fpatterns == fpattern_sz) { + fpattern_sz *= 2; + fpattern = grep_realloc(fpattern, ++fpattern_sz * + sizeof(struct epat)); + } + fpattern[fpatterns].pat = grep_strdup(pat); + fpattern[fpatterns].mode = mode; + ++fpatterns; +} + +/* + * Adds a directory include/exclude pattern to the internal array. + */ +static void +add_dpattern(const char *pat, int mode) +{ + + /* Increase size if necessary */ + if (dpatterns == dpattern_sz) { + dpattern_sz *= 2; + dpattern = grep_realloc(dpattern, ++dpattern_sz * + sizeof(struct epat)); + } + dpattern[dpatterns].pat = grep_strdup(pat); + dpattern[dpatterns].mode = mode; + ++dpatterns; +} + +/* + * Reads searching patterns from a file and adds them with add_pattern(). + */ +static void +read_patterns(const char *fn) +{ + FILE *f; + char *line; + size_t len; + ssize_t rlen; + + if ((f = fopen(fn, "r")) == NULL) + err(2, "%s", fn); + line = NULL; + len = 0; +#ifndef ANDROID + while ((rlen = getline(&line, &len, f)) != -1) + add_pattern(line, *line == '\n' ? 0 : (size_t)rlen); +#endif + free(line); + if (ferror(f)) + err(2, "%s", fn); + fclose(f); +} + +static inline const char * +init_color(const char *d) +{ + char *c; + + c = getenv("GREP_COLOR"); + return (c != NULL ? c : d); +} + +int +grep_main(int argc, char *argv[]) +{ + char **aargv, **eargv, *eopts; + char *ep; + unsigned long long l; + unsigned int aargc, eargc, i, j; + int c, lastc, needpattern, newarg, prevoptind; + + setlocale(LC_ALL, ""); + +#ifndef WITHOUT_NLS + catalog = catopen("grep", NL_CAT_LOCALE); +#endif + + /* Check what is the program name of the binary. In this + way we can have all the funcionalities in one binary + without the need of scripting and using ugly hacks. */ + switch (__progname[0]) { + case 'e': + grepbehave = GREP_EXTENDED; + break; + case 'f': + grepbehave = GREP_FIXED; + break; + case 'g': + grepbehave = GREP_BASIC; + break; + case 'z': + filebehave = FILE_GZIP; + switch(__progname[1]) { + case 'e': + grepbehave = GREP_EXTENDED; + break; + case 'f': + grepbehave = GREP_FIXED; + break; + case 'g': + grepbehave = GREP_BASIC; + break; + } + break; + } + + lastc = '\0'; + newarg = 1; + prevoptind = 1; + needpattern = 1; + + eopts = getenv("GREP_OPTIONS"); + + /* support for extra arguments in GREP_OPTIONS */ + eargc = 0; + if (eopts != NULL) { + char *str; + + /* make an estimation of how many extra arguments we have */ + for (j = 0; j < strlen(eopts); j++) + if (eopts[j] == ' ') + eargc++; + + eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); + + eargc = 0; + /* parse extra arguments */ + while ((str = strsep(&eopts, " ")) != NULL) + eargv[eargc++] = grep_strdup(str); + + aargv = (char **)grep_calloc(eargc + argc + 1, + sizeof(char *)); + + aargv[0] = argv[0]; + for (i = 0; i < eargc; i++) + aargv[i + 1] = eargv[i]; + for (j = 1; j < (unsigned int)argc; j++, i++) + aargv[i + 1] = argv[j]; + + aargc = eargc + argc; + } else { + aargv = argv; + aargc = argc; + } + + while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != + -1)) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (newarg || !isdigit(lastc)) + Aflag = 0; + else if (Aflag > LLONG_MAX / 10) { + errno = ERANGE; + err(2, NULL); + } + Aflag = Bflag = (Aflag * 10) + (c - '0'); + break; + case 'C': + if (optarg == NULL) { + Aflag = Bflag = 2; + break; + } + /* FALLTHROUGH */ + case 'A': + /* FALLTHROUGH */ + case 'B': + errno = 0; + l = strtoull(optarg, &ep, 10); + if (((errno == ERANGE) && (l == ULLONG_MAX)) || + ((errno == EINVAL) && (l == 0))) + err(2, NULL); + else if (ep[0] != '\0') { + errno = EINVAL; + err(2, NULL); + } + if (c == 'A') + Aflag = l; + else if (c == 'B') + Bflag = l; + else + Aflag = Bflag = l; + break; + case 'a': + binbehave = BINFILE_TEXT; + break; + case 'b': + bflag = true; + break; + case 'c': + cflag = true; + break; + case 'D': + if (strcasecmp(optarg, "skip") == 0) + devbehave = DEV_SKIP; + else if (strcasecmp(optarg, "read") == 0) + devbehave = DEV_READ; + else + errx(2, getstr(3), "--devices"); + break; + case 'd': + if (strcasecmp("recurse", optarg) == 0) { + Hflag = true; + dirbehave = DIR_RECURSE; + } else if (strcasecmp("skip", optarg) == 0) + dirbehave = DIR_SKIP; + else if (strcasecmp("read", optarg) == 0) + dirbehave = DIR_READ; + else + errx(2, getstr(3), "--directories"); + break; + case 'E': + grepbehave = GREP_EXTENDED; + break; + case 'e': + add_pattern(optarg, strlen(optarg)); + needpattern = 0; + break; + case 'F': + grepbehave = GREP_FIXED; + break; + case 'f': + read_patterns(optarg); + needpattern = 0; + break; + case 'G': + grepbehave = GREP_BASIC; + break; + case 'H': + Hflag = true; + break; + case 'h': + Hflag = false; + hflag = true; + break; + case 'I': + binbehave = BINFILE_SKIP; + break; + case 'i': + case 'y': + iflag = true; + cflags |= REG_ICASE; + break; + case 'J': + filebehave = FILE_BZIP; + break; + case 'L': + lflag = false; + Lflag = true; + break; + case 'l': + Lflag = false; + lflag = true; + break; + case 'm': + mflag = true; + errno = 0; + mcount = strtoull(optarg, &ep, 10); + if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || + ((errno == EINVAL) && (mcount == 0))) + err(2, NULL); + else if (ep[0] != '\0') { + errno = EINVAL; + err(2, NULL); + } + break; + case 'n': + nflag = true; + break; + case 'O': + linkbehave = LINK_EXPLICIT; + break; + case 'o': + oflag = true; + break; + case 'p': + linkbehave = LINK_SKIP; + break; + case 'q': + qflag = true; + break; + case 'S': + linkbehave = LINK_READ; + break; + case 'R': + case 'r': + dirbehave = DIR_RECURSE; + Hflag = true; + break; + case 's': + sflag = true; + break; + case 'U': + binbehave = BINFILE_BIN; + break; + case 'u': + case MMAP_OPT: + /* noop, compatibility */ + break; + case 'V': + printf(getstr(9), __progname, VERSION); + exit(0); + case 'v': + vflag = true; + break; + case 'w': + wflag = true; + break; + case 'x': + xflag = true; + break; + case 'Z': + nullflag = true; + break; + case 'z': + nulldataflag = true; + line_sep = '\0'; + break; + case BIN_OPT: + if (strcasecmp("binary", optarg) == 0) + binbehave = BINFILE_BIN; + else if (strcasecmp("without-match", optarg) == 0) + binbehave = BINFILE_SKIP; + else if (strcasecmp("text", optarg) == 0) + binbehave = BINFILE_TEXT; + else + errx(2, getstr(3), "--binary-files"); + break; + case COLOR_OPT: + color = NULL; + if (optarg == NULL || strcasecmp("auto", optarg) == 0 || + strcasecmp("tty", optarg) == 0 || + strcasecmp("if-tty", optarg) == 0) { + char *term; + + term = getenv("TERM"); + if (isatty(STDOUT_FILENO) && term != NULL && + strcasecmp(term, "dumb") != 0) + color = init_color("01;31"); + } else if (strcasecmp("always", optarg) == 0 || + strcasecmp("yes", optarg) == 0 || + strcasecmp("force", optarg) == 0) { + color = init_color("01;31"); + } else if (strcasecmp("never", optarg) != 0 && + strcasecmp("none", optarg) != 0 && + strcasecmp("no", optarg) != 0) + errx(2, getstr(3), "--color"); + break; + case DECOMPRESS_OPT: + filebehave = FILE_GZIP; + break; + case LABEL_OPT: + label = optarg; + break; + case LINEBUF_OPT: + lbflag = true; + break; + case R_INCLUDE_OPT: + finclude = true; + add_fpattern(optarg, INCL_PAT); + break; + case R_EXCLUDE_OPT: + fexclude = true; + add_fpattern(optarg, EXCL_PAT); + break; + case R_DINCLUDE_OPT: + dinclude = true; + add_dpattern(optarg, INCL_PAT); + break; + case R_DEXCLUDE_OPT: + dexclude = true; + add_dpattern(optarg, EXCL_PAT); + break; + case HELP_OPT: + default: + usage(); + } + lastc = c; + newarg = optind != prevoptind; + prevoptind = optind; + } + aargc -= optind; + aargv += optind; + + /* Fail if we don't have any pattern */ + if (aargc == 0 && needpattern) + usage(); + + /* Process patterns from command line */ + if (aargc != 0 && needpattern) { + add_pattern(*aargv, strlen(*aargv)); + --aargc; + ++aargv; + } + + switch (grepbehave) { + case GREP_FIXED: + case GREP_BASIC: + break; + case GREP_EXTENDED: + cflags |= REG_EXTENDED; + break; + default: + /* NOTREACHED */ + usage(); + } + + fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); + r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); +/* + * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. + * Optimizations should be done there. + */ + /* Check if cheating is allowed (always is for fgrep). */ + if (grepbehave == GREP_FIXED) { + for (i = 0; i < patterns; ++i) + fgrepcomp(&fg_pattern[i], pattern[i]); + } else { + for (i = 0; i < patterns; ++i) { + if (fastcomp(&fg_pattern[i], pattern[i])) { + /* Fall back to full regex library */ + c = regcomp(&r_pattern[i], pattern[i], cflags); + if (c != 0) { + regerror(c, &r_pattern[i], re_error, + RE_ERROR_BUF); + errx(2, "%s", re_error); + } + } + } + } + + if (lbflag) + setlinebuf(stdout); + + if ((aargc == 0 || aargc == 1) && !Hflag) + hflag = true; + + if (aargc == 0) + exit(!procfile("-")); + + if (dirbehave == DIR_RECURSE) + c = grep_tree(aargv); + else + for (c = 0; aargc--; ++aargv) { + if ((finclude || fexclude) && !file_matching(*aargv)) + continue; + c+= procfile(*aargv); + } + +#ifndef WITHOUT_NLS + catclose(catalog); +#endif + + /* Find out the correct return value according to the + results and the command line option. */ + exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); +} diff --git a/toolbox/grep/grep.h b/toolbox/grep/grep.h new file mode 100644 index 0000000..6454f93 --- /dev/null +++ b/toolbox/grep/grep.h @@ -0,0 +1,166 @@ +/* $NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $ */ +/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */ +/* $FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef ANDROID +#define WITHOUT_NLS +#endif + +#ifndef ANDROID +#include <bzlib.h> +#endif +#include <limits.h> +#include <regex.h> +#include <stdbool.h> +#include <stdio.h> +#ifndef ANDROID +#include <zlib.h> +#endif + +#ifdef WITHOUT_NLS +#define getstr(n) errstr[n] +#else +#include <nl_types.h> + +extern nl_catd catalog; +#define getstr(n) catgets(catalog, 1, n, errstr[n]) +#endif + +extern const char *errstr[]; + +#define VERSION "2.5.1-FreeBSD" + +#define GREP_FIXED 0 +#define GREP_BASIC 1 +#define GREP_EXTENDED 2 + +#define BINFILE_BIN 0 +#define BINFILE_SKIP 1 +#define BINFILE_TEXT 2 + +#define FILE_STDIO 0 +#define FILE_GZIP 1 +#define FILE_BZIP 2 + +#define DIR_READ 0 +#define DIR_SKIP 1 +#define DIR_RECURSE 2 + +#define DEV_READ 0 +#define DEV_SKIP 1 + +#define LINK_READ 0 +#define LINK_EXPLICIT 1 +#define LINK_SKIP 2 + +#define EXCL_PAT 0 +#define INCL_PAT 1 + +#define MAX_LINE_MATCHES 32 + +struct file { + int fd; + bool binary; +}; + +struct str { + off_t off; + size_t len; + char *dat; + char *file; + int line_no; +}; + +struct epat { + char *pat; + int mode; +}; + +typedef struct { + size_t len; + unsigned char *pattern; + int qsBc[UCHAR_MAX + 1]; + /* flags */ + bool bol; + bool eol; + bool reversed; + bool word; +} fastgrep_t; + +/* Flags passed to regcomp() and regexec() */ +extern int cflags, eflags; + +/* Command line flags */ +extern bool Eflag, Fflag, Gflag, Hflag, Lflag, + bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag, + qflag, sflag, vflag, wflag, xflag; +extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag; +extern unsigned char line_sep; +extern unsigned long long Aflag, Bflag, mcount; +extern char *label; +extern const char *color; +extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave; + +extern bool notfound; +extern int tail; +extern unsigned int dpatterns, fpatterns, patterns; +extern char **pattern; +extern struct epat *dpattern, *fpattern; +extern regex_t *er_pattern, *r_pattern; +extern fastgrep_t *fg_pattern; + +/* For regex errors */ +#define RE_ERROR_BUF 512 +extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */ + +/* util.c */ +bool file_matching(const char *fname); +int procfile(const char *fn); +int grep_tree(char **argv); +void *grep_malloc(size_t size); +void *grep_calloc(size_t nmemb, size_t size); +void *grep_realloc(void *ptr, size_t size); +char *grep_strdup(const char *str); +void printline(struct str *line, int sep, regmatch_t *matches, int m); + +/* queue.c */ +void enqueue(struct str *x); +void printqueue(void); +void clearqueue(void); + +/* file.c */ +void grep_close(struct file *f); +struct file *grep_open(const char *path); +char *grep_fgetln(struct file *f, size_t *len); + +/* fastgrep.c */ +int fastcomp(fastgrep_t *, const char *); +void fgrepcomp(fastgrep_t *, const char *); +int grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *); diff --git a/toolbox/grep/queue.c b/toolbox/grep/queue.c new file mode 100644 index 0000000..e3c6be1 --- /dev/null +++ b/toolbox/grep/queue.c @@ -0,0 +1,116 @@ +/* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */ +/* $FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $ */ +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * A really poor man's queue. It does only what it has to and gets out of + * Dodge. It is used in place of <sys/queue.h> to get a better performance. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $"); + +#include <sys/param.h> +#include <sys/queue.h> + +#include <stdlib.h> +#include <string.h> + +#include "grep.h" + +struct qentry { + STAILQ_ENTRY(qentry) list; + struct str data; +}; + +static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue); +static unsigned long long count; + +static struct qentry *dequeue(void); + +void +enqueue(struct str *x) +{ + struct qentry *item; + + item = grep_malloc(sizeof(struct qentry)); + item->data.dat = grep_malloc(sizeof(char) * x->len); + item->data.len = x->len; + item->data.line_no = x->line_no; + item->data.off = x->off; + memcpy(item->data.dat, x->dat, x->len); + item->data.file = x->file; + + STAILQ_INSERT_TAIL(&queue, item, list); + + if (++count > Bflag) { + item = dequeue(); + free(item->data.dat); + free(item); + } +} + +static struct qentry * +dequeue(void) +{ + struct qentry *item; + + item = STAILQ_FIRST(&queue); + if (item == NULL) + return (NULL); + + STAILQ_REMOVE_HEAD(&queue, list); + --count; + return (item); +} + +void +printqueue(void) +{ + struct qentry *item; + + while ((item = dequeue()) != NULL) { + printline(&item->data, '-', NULL, 0); + free(item->data.dat); + free(item); + } +} + +void +clearqueue(void) +{ + struct qentry *item; + + while ((item = dequeue()) != NULL) { + free(item->data.dat); + free(item); + } +} diff --git a/toolbox/grep/util.c b/toolbox/grep/util.c new file mode 100644 index 0000000..497db06 --- /dev/null +++ b/toolbox/grep/util.c @@ -0,0 +1,498 @@ +/* $NetBSD: util.c,v 1.16 2012/05/06 22:32:05 joerg Exp $ */ +/* $FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $ */ +/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: util.c,v 1.16 2012/05/06 22:32:05 joerg Exp $"); + +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fnmatch.h> +#include <fts.h> +#include <libgen.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> + +#include "grep.h" + +static bool first, first_global = true; +static unsigned long long since_printed; + +static int procline(struct str *l, int); + +bool +file_matching(const char *fname) +{ + char *fname_base, *fname_copy; + unsigned int i; + bool ret; + + ret = finclude ? false : true; + fname_copy = grep_strdup(fname); + fname_base = basename(fname_copy); + + for (i = 0; i < fpatterns; ++i) { + if (fnmatch(fpattern[i].pat, fname, 0) == 0 || + fnmatch(fpattern[i].pat, fname_base, 0) == 0) { + if (fpattern[i].mode == EXCL_PAT) + return (false); + else + ret = true; + } + } + free(fname_copy); + return (ret); +} + +static inline bool +dir_matching(const char *dname) +{ + unsigned int i; + bool ret; + + ret = dinclude ? false : true; + + for (i = 0; i < dpatterns; ++i) { + if (dname != NULL && + fnmatch(dname, dpattern[i].pat, 0) == 0) { + if (dpattern[i].mode == EXCL_PAT) + return (false); + else + ret = true; + } + } + return (ret); +} + +/* + * Processes a directory when a recursive search is performed with + * the -R option. Each appropriate file is passed to procfile(). + */ +int +grep_tree(char **argv) +{ + FTS *fts; + FTSENT *p; + char *d, *dir = NULL; + int c, fts_flags; + bool ok; + + c = fts_flags = 0; + + switch(linkbehave) { + case LINK_EXPLICIT: + fts_flags = FTS_COMFOLLOW; + break; + case LINK_SKIP: + fts_flags = FTS_PHYSICAL; + break; + default: + fts_flags = FTS_LOGICAL; + + } + + fts_flags |= FTS_NOSTAT | FTS_NOCHDIR; + + if (!(fts = fts_open(argv, fts_flags, NULL))) + err(2, "fts_open"); + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_DNR: + /* FALLTHROUGH */ + case FTS_ERR: + errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno)); + break; + case FTS_D: + /* FALLTHROUGH */ + case FTS_DP: + break; + case FTS_DC: + /* Print a warning for recursive directory loop */ + warnx("warning: %s: recursive directory loop", + p->fts_path); + break; + default: + /* Check for file exclusion/inclusion */ + ok = true; + if (dexclude || dinclude) { + if ((d = strrchr(p->fts_path, '/')) != NULL) { + dir = grep_malloc(sizeof(char) * + (d - p->fts_path + 1)); + memcpy(dir, p->fts_path, + d - p->fts_path); + dir[d - p->fts_path] = '\0'; + } + ok = dir_matching(dir); + free(dir); + dir = NULL; + } + if (fexclude || finclude) + ok &= file_matching(p->fts_path); + + if (ok) + c += procfile(p->fts_path); + break; + } + } + + fts_close(fts); + return (c); +} + +/* + * Opens a file and processes it. Each file is processed line-by-line + * passing the lines to procline(). + */ +int +procfile(const char *fn) +{ + struct file *f; + struct stat sb; + struct str ln; + mode_t s; + int c, t; + + if (mflag && (mcount <= 0)) + return (0); + + if (strcmp(fn, "-") == 0) { + fn = label != NULL ? label : getstr(1); + f = grep_open(NULL); + } else { + if (!stat(fn, &sb)) { + /* Check if we need to process the file */ + s = sb.st_mode & S_IFMT; + if (s == S_IFDIR && dirbehave == DIR_SKIP) + return (0); + if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK + || s == S_IFSOCK) && devbehave == DEV_SKIP) + return (0); + } + f = grep_open(fn); + } + if (f == NULL) { + if (!sflag) + warn("%s", fn); + if (errno == ENOENT) + notfound = true; + return (0); + } + + ln.file = grep_malloc(strlen(fn) + 1); + strcpy(ln.file, fn); + ln.line_no = 0; + ln.len = 0; + tail = 0; + ln.off = -1; + + for (first = true, c = 0; c == 0 || !(lflag || qflag); ) { + ln.off += ln.len + 1; + if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0) + break; + if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep) + --ln.len; + ln.line_no++; + + /* Return if we need to skip a binary file */ + if (f->binary && binbehave == BINFILE_SKIP) { + grep_close(f); + free(ln.file); + free(f); + return (0); + } + /* Process the file line-by-line */ + t = procline(&ln, f->binary); + c += t; + + /* Count the matches if we have a match limit */ + if (mflag) { + mcount -= t; + if (mcount <= 0) + break; + } + } + if (Bflag > 0) + clearqueue(); + grep_close(f); + + if (cflag) { + if (!hflag) + printf("%s:", ln.file); + printf("%u%c", c, line_sep); + } + if (lflag && !qflag && c != 0) + printf("%s%c", fn, line_sep); + if (Lflag && !qflag && c == 0) + printf("%s%c", fn, line_sep); + if (c && !cflag && !lflag && !Lflag && + binbehave == BINFILE_BIN && f->binary && !qflag) + printf(getstr(8), fn); + + free(ln.file); + free(f); + return (c); +} + +#define iswword(x) (iswalnum((x)) || (x) == L'_') + +/* + * Processes a line comparing it with the specified patterns. Each pattern + * is looped to be compared along with the full string, saving each and every + * match, which is necessary to colorize the output and to count the + * matches. The matching lines are passed to printline() to display the + * appropriate output. + */ +static int +procline(struct str *l, int nottext) +{ + regmatch_t matches[MAX_LINE_MATCHES]; + regmatch_t pmatch; + size_t st = 0; + unsigned int i; + int c = 0, m = 0, r = 0; + + /* Loop to process the whole line */ + while (st <= l->len) { + pmatch.rm_so = st; + pmatch.rm_eo = l->len; + + /* Loop to compare with all the patterns */ + for (i = 0; i < patterns; i++) { +/* + * XXX: grep_search() is a workaround for speed up and should be + * removed in the future. See fastgrep.c. + */ + if (fg_pattern[i].pattern) { + r = grep_search(&fg_pattern[i], + (unsigned char *)l->dat, + l->len, &pmatch); + r = (r == 0) ? 0 : REG_NOMATCH; + st = pmatch.rm_eo; + } else { + r = regexec(&r_pattern[i], l->dat, 1, + &pmatch, eflags); + r = (r == 0) ? 0 : REG_NOMATCH; + st = pmatch.rm_eo; + } + if (r == REG_NOMATCH) + continue; + /* Check for full match */ + if (xflag && + (pmatch.rm_so != 0 || + (size_t)pmatch.rm_eo != l->len)) + continue; + /* Check for whole word match */ + if (fg_pattern[i].word && pmatch.rm_so != 0) { + wint_t wbegin, wend; + + wbegin = wend = L' '; + if (pmatch.rm_so != 0 && + sscanf(&l->dat[pmatch.rm_so - 1], + "%lc", &wbegin) != 1) + continue; + if ((size_t)pmatch.rm_eo != l->len && + sscanf(&l->dat[pmatch.rm_eo], + "%lc", &wend) != 1) + continue; + if (iswword(wbegin) || iswword(wend)) + continue; + } + c = 1; + if (m < MAX_LINE_MATCHES) + matches[m++] = pmatch; + /* matches - skip further patterns */ + if ((color != NULL && !oflag) || qflag || lflag) + break; + } + + if (vflag) { + c = !c; + break; + } + /* One pass if we are not recording matches */ + if ((color != NULL && !oflag) || qflag || lflag) + break; + + if (st == (size_t)pmatch.rm_so) + break; /* No matches */ + } + + if (c && binbehave == BINFILE_BIN && nottext) + return (c); /* Binary file */ + + /* Dealing with the context */ + if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) { + if (c) { + if ((Aflag || Bflag) && !first_global && + (first || since_printed > Bflag)) + printf("--\n"); + tail = Aflag; + if (Bflag > 0) + printqueue(); + printline(l, ':', matches, m); + } else { + printline(l, '-', matches, m); + tail--; + } + first = false; + first_global = false; + since_printed = 0; + } else { + if (Bflag) + enqueue(l); + since_printed++; + } + return (c); +} + +/* + * Safe malloc() for internal use. + */ +void * +grep_malloc(size_t size) +{ + void *ptr; + + if ((ptr = malloc(size)) == NULL) + err(2, "malloc"); + return (ptr); +} + +/* + * Safe calloc() for internal use. + */ +void * +grep_calloc(size_t nmemb, size_t size) +{ + void *ptr; + + if ((ptr = calloc(nmemb, size)) == NULL) + err(2, "calloc"); + return (ptr); +} + +/* + * Safe realloc() for internal use. + */ +void * +grep_realloc(void *ptr, size_t size) +{ + + if ((ptr = realloc(ptr, size)) == NULL) + err(2, "realloc"); + return (ptr); +} + +/* + * Safe strdup() for internal use. + */ +char * +grep_strdup(const char *str) +{ + char *ret; + + if ((ret = strdup(str)) == NULL) + err(2, "strdup"); + return (ret); +} + +/* + * Prints a matching line according to the command line options. + */ +void +printline(struct str *line, int sep, regmatch_t *matches, int m) +{ + size_t a = 0; + int i, n = 0; + + if (!hflag) { + if (nullflag == 0) + fputs(line->file, stdout); + else { + printf("%s", line->file); + putchar(0); + } + ++n; + } + if (nflag) { + if (n > 0) + putchar(sep); + printf("%d", line->line_no); + ++n; + } + if (bflag) { + if (n > 0) + putchar(sep); + printf("%lld", (long long)line->off); + ++n; + } + if (n) + putchar(sep); + /* --color and -o */ + if ((oflag || color) && m > 0) { + for (i = 0; i < m; i++) { + if (!oflag) + fwrite(line->dat + a, matches[i].rm_so - a, 1, + stdout); + if (color) + fprintf(stdout, "\33[%sm\33[K", color); + + fwrite(line->dat + matches[i].rm_so, + matches[i].rm_eo - matches[i].rm_so, 1, + stdout); + if (color) + fprintf(stdout, "\33[m\33[K"); + a = matches[i].rm_eo; + if (oflag) + putchar('\n'); + } + if (!oflag) { + if (line->len - a > 0) + fwrite(line->dat + a, line->len - a, 1, stdout); + putchar(line_sep); + } + } else { + fwrite(line->dat, line->len, 1, stdout); + putchar(line_sep); + } +} |
