Merge commit 'de016eef' into mit
* commit 'de016eef':
Add telephony-common and mms-common to BOOTCLASSPATH
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 @@
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 @@
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.
diff --git a/adb/adb.c b/adb/adb.c
index f4ee448..6ec4f7a 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -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 @@
int HOST = 0;
+#if !ADB_HOST
static const char *adb_device_banner = "device";
+#endif
void fatal(const char *fmt, ...)
{
@@ -269,6 +274,36 @@
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 @@
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 @@
}
}
+/* 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")){
@@ -1025,7 +1086,8 @@
if (sscanf(value, "%d", &port) == 1 && port > 0) {
// listen on TCP port specified by service.adb.tcp.port property
local_init(port);
- } else if (access("/dev/android_adb", F_OK) == 0) {
+ } else if (access(USB_ADB_PATH, F_OK) == 0 ||
+ access(USB_FFS_ADB_EP0, F_OK) == 0) {
// listen on USB
usb_init();
} else {
@@ -1067,7 +1129,7 @@
strncpy(hostbuf, host, sizeof(hostbuf) - 1);
if (portstr) {
- if (portstr - host >= sizeof(hostbuf)) {
+ if (portstr - host >= (ptrdiff_t)sizeof(hostbuf)) {
snprintf(buffer, buffer_size, "bad host name %s", host);
return;
}
@@ -1201,16 +1263,19 @@
}
// 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 +1341,16 @@
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);
diff --git a/adb/adb.h b/adb/adb.h
index 03a7393..df88896 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -190,6 +190,9 @@
/* 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 @@
** 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 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 @@
#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 @@
" 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 @@
" 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 @@
" 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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
}
}
-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 @@
/* 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 @@
#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 @@
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 @@
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 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 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 @@
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 @@
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 @@
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 @@
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 */
};
-void usb_cleanup()
-{
- // nothing to do here
-}
+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,
+ },
+ },
+};
-static void *usb_open_thread(void *x)
+#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_,
+ },
+};
+
+
+
+static void *usb_adb_open_thread(void *x)
{
struct usb_handle *usb = (struct usb_handle *)x;
int fd;
@@ -72,14 +176,14 @@
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 @@
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 @@
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 @@
}
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)
-{
- D("usb_kick\n");
- adb_mutex_lock(&h->lock);
- adb_close(h->fd);
- h->fd = -1;
- // notify usb_open_thread that we are disconnected
+static void init_functionfs(struct usb_handle *h)
+{
+ 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->control);
+ adb_close(h->bulk_out);
+ adb_close(h->bulk_in);
+ h->control = h->bulk_out = h->bulk_in = -1;
+
+ // 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 @@
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 @@
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 @@
}
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_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 @@
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 46b0828..f5215cf 100644
--- a/fastboot/engine.c
+++ b/fastboot/engine.c
@@ -542,6 +542,8 @@
int status = 0;
a = action_list;
+ if (!a)
+ return status;
resp[FB_RESPONSE_SZ] = 0;
double start = -1;
@@ -578,3 +580,8 @@
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 848cea3..544893b 100644
--- a/fastboot/fastboot.c
+++ b/fastboot/fastboot.c
@@ -58,6 +58,7 @@
static const char *cmdline = 0;
static int wipe_data = 0;
static unsigned short vendor_id = 0;
+static int long_listing = 0;
static unsigned base_addr = 0x10000000;
@@ -150,6 +151,11 @@
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
@@ -167,15 +173,16 @@
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"
@@ -184,7 +191,13 @@
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;
@@ -238,7 +251,9 @@
"\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"
@@ -571,6 +586,7 @@
int wants_wipe = 0;
int wants_reboot = 0;
int wants_reboot_bootloader = 0;
+ int wants_device_list = 0;
void *data;
unsigned sz;
unsigned page_size = 2048;
@@ -582,11 +598,6 @@
return 1;
}
- if (!strcmp(*argv, "devices")) {
- list_devices();
- return 0;
- }
-
if (!strcmp(*argv, "help")) {
usage();
return 0;
@@ -612,6 +623,9 @@
require(2);
serial = argv[1];
skip(2);
+ } else if(!strcmp(*argv, "-l")) {
+ long_listing = 1;
+ skip(1);
} else if(!strcmp(*argv, "-p")) {
require(2);
product = argv[1];
@@ -630,6 +644,9 @@
die("invalid vendor id '%s'", argv[1]);
vendor_id = (unsigned short)val;
skip(2);
+ } else if (!strcmp(*argv, "devices")) {
+ skip(1);
+ wants_device_list = 1;
} else if(!strcmp(*argv, "getvar")) {
require(2);
fb_queue_display(argv[1], argv[1]);
@@ -725,6 +742,9 @@
}
}
+ if (wants_device_list)
+ list_devices();
+
if (wants_wipe) {
fb_queue_erase("userdata");
fb_queue_format("userdata", 1);
@@ -737,6 +757,9 @@
fb_queue_command("reboot-bootloader", "rebooting into bootloader");
}
+ if (fb_queue_is_empty())
+ return 0;
+
usb = open_device();
status = fb_execute_queue(usb);
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 1d3e2b8..90d8a6a 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -53,6 +53,7 @@
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 @@
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 @@
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 @@
// 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 @@
}
}
+ /* 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 @@
SInt32 score;
HRESULT result;
UInt8 serialIndex;
+ UInt32 locationId;
// Create an intermediate plugin.
kr = IOCreatePlugInInterfaceForService(device,
@@ -322,6 +323,13 @@
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 @@
info.serial_number[0] = 0;
}
+ info.device_path[0] = 0;
+
if (callback(&info) == 0) {
return 1;
}
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 172e348..0040c22 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/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 @@
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/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/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 @@
*/
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 @@
// ---------------------------------------------------------------------------
+// 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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);
- void* reserved_proc[2];
+ /*
+ * 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);
+
+ /*
+ * 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 @@
/*
* 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 @@
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 @@
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 @@
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/init.c b/init/init.c
index 4f57144..48d9c9a 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;
@@ -863,6 +864,9 @@
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/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 433f93c..9019986 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 @@
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
@@ -44,3 +50,38 @@
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 24fadcb..01d9ed5 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 @@
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 fa21574..eec53a2 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 @@
}
#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 @@
// 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/libion/ion.c b/libion/ion.c
index dbeac23..164cec9 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -54,13 +54,14 @@
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 @@
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 @@
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 @@
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 @@
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 @@
case 'f':
alloc_flags = atol(optarg);
break;
+ case 'h':
+ heap_mask = atol(optarg);
+ break;
case 'a':
test = ALLOC_TEST;
break;
@@ -254,17 +256,11 @@
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/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 @@
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 @@
/* 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 @@
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 @@
{
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 @@
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 @@
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 @@
.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/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/rootdir/init.rc b/rootdir/init.rc
index 63e0ee9..492158a 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -241,6 +241,7 @@
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
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 @@
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..316588c 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>
@@ -72,28 +74,42 @@
#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 +119,166 @@
char *actual_name;
};
+/* Global data structure shared by all fuse handlers. */
struct fuse {
+ pthread_mutex_t lock;
+
__u64 next_generation;
- __u64 next_node_id;
-
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;
-#define PATH_BUFFER_SIZE 1024
+ /* 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 NO_CASE_SENSITIVE_MATCH 0
-#define CASE_SENSITIVE_MATCH 1
-
-/*
- * 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
- */
-char *do_node_get_path(struct node *node, char *buf, const char *name, int match_case_insensitive)
+static inline void *id_to_ptr(__u64 nid)
{
- struct node *in_node = node;
- const char *in_name = name;
- char *out = buf + PATH_BUFFER_SIZE - 1;
- int len;
- out[0] = 0;
+ return (void *) (uintptr_t) nid;
+}
- if (name) {
- len = strlen(name);
- goto start;
- }
+static inline __u64 ptr_to_id(void *ptr)
+{
+ return (__u64) (uintptr_t) ptr;
+}
- 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] = '/';
+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.
+ */
+static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize)
+{
+ size_t namelen = node->namelen;
+ if (bufsize < namelen + 1) {
+ return -1;
}
- /* 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;
+ 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++] = '/';
+ }
+
+ 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,129 +305,81 @@
attr->gid = AID_SDCARD_RW;
}
-int node_get_attr(struct node *node, struct fuse_attr *attr)
-{
- int res;
- struct stat s;
- char *path, buffer[PATH_BUFFER_SIZE];
-
- path = node_get_path(node, buffer, 0);
- res = lstat(path, &s);
- if (res < 0) {
- ERROR("lstat('%s') errno %d\n", path, errno);
- return -1;
- }
-
- 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++;
-}
-
-/* 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)
-{
- 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;
- }
-
- 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);
- if (!node->actual_name) {
- ERROR("strdup failed - out of memory\n");
- exit(1);
- }
- break;
- }
- }
- closedir(dir);
-}
-
-struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen)
+struct node *create_node_locked(struct fuse* fuse,
+ struct node *parent, const char *name, const char* actual_name)
{
struct node *node;
- int namelen = strlen(name);
+ size_t namelen = strlen(name);
node = calloc(1, sizeof(struct node));
- if (node == 0) {
- return 0;
+ if (!node) {
+ return NULL;
}
node->name = malloc(namelen + 1);
- if (node->name == 0) {
+ if (!node->name) {
free(node);
- return 0;
+ return NULL;
}
-
- node->nid = nid;
- node->gen = gen;
- add_node_to_parent(node, parent);
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_find_actual_name(node);
+ node->nid = ptr_to_id(node);
+ node->gen = fuse->next_generation++;
+ acquire_node_locked(node);
+ add_node_to_parent_locked(node, parent);
return node;
}
-static char *rename_node(struct node *node, const char *name)
+static int rename_node_locked(struct node *node, const char *name,
+ const char* actual_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;
+ 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;
+ }
+ }
+
+ /* 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) {
+ return -ENOMEM;
+ }
+ }
+ memcpy(node->actual_name, actual_name, namelen + 1);
+ } else {
+ free(node->actual_name);
+ node->actual_name = NULL;
+ }
+ memcpy(node->name, name, namelen + 1);
+ node->namelen = namelen;
+ return 0;
}
-void fuse_init(struct fuse *fuse, int fd, const char *path)
-{
- 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);
-}
-
-static inline void *id_to_ptr(__u64 nid)
-{
- return (void *) nid;
-}
-
-static inline __u64 ptr_to_id(void *ptr)
-{
- return (__u64) ptr;
-}
-
-
-struct node *lookup_by_inode(struct fuse *fuse, __u64 nid)
+static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid)
{
if (nid == FUSE_ROOT_ID) {
return &fuse->root;
@@ -352,9 +388,23 @@
}
}
-struct node *lookup_child_by_name(struct node *node, const char *name)
+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 struct node *lookup_child_by_name_locked(struct node *node, const char *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;
}
@@ -362,123 +412,43 @@
return 0;
}
-struct node *lookup_child_by_inode(struct node *node, __u64 nid)
+static struct node* acquire_or_create_child_locked(
+ struct fuse* fuse, struct node* parent,
+ const char* name, const char* actual_name)
{
- for (node = node->child; node; node = node->next) {
- if (node->nid == nid) {
- return node;
- }
- }
- return 0;
-}
-
-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);
+ struct node* child = lookup_child_by_name_locked(parent, name);
+ if (child) {
+ acquire_node_locked(child);
} else {
- ERROR("Zero refcnt %p\n", node);
+ child = create_node_locked(fuse, parent, name, actual_name);
}
- }
-
-static struct node *remove_child(struct node *parent, __u64 nid)
-{
- struct node *prev = 0;
- struct node *node;
-
- for (node = parent->child; node; node = node->next) {
- if (node->nid == nid) {
- if (prev) {
- prev->next = node->next;
- } else {
- parent->child = node->next;
- }
- node->next = 0;
- node->parent = 0;
- dec_refcount(parent);
- return node;
- }
- prev = node;
- }
- return 0;
+ return child;
}
-struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name,
- struct fuse_attr *attr)
+static void fuse_init(struct fuse *fuse, int fd, const char *path)
{
- int res;
- struct stat s;
- char *path, buffer[PATH_BUFFER_SIZE];
- struct node *node;
+ pthread_mutex_init(&fuse->lock, NULL);
- path = node_get_path(parent, buffer, name);
- /* XXX error? */
+ fuse->fd = fd;
+ fuse->next_generation = 0;
- 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;
- }
-
- attr_from_stat(attr, &s);
- attr->ino = node->nid;
-
- return node;
+ memset(&fuse->root, 0, sizeof(fuse->root));
+ fuse->root.nid = FUSE_ROOT_ID; /* 1 */
+ fuse->root.refcount = 2;
+ fuse->root.namelen = strlen(path);
+ fuse->root.name = strdup(path);
}
-void node_release(struct node *node)
-{
- 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;
-
- node2 = node->parent->child;
- while (node2->next != node)
- node2 = node2->next;
- node2->next = node->next;
- }
-
- TRACE("DESTROY %p (%s)\n", node, node->name);
-
- node_release(node->parent);
-
- node->parent = 0;
- node->next = 0;
-
- /* 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);
- }
-}
-
-void fuse_status(struct fuse *fuse, __u64 unique, int err)
+static void fuse_status(struct fuse *fuse, __u64 unique, int err)
{
struct fuse_out_header hdr;
hdr.len = sizeof(hdr);
hdr.error = err;
hdr.unique = unique;
- if (err) {
-// ERROR("*** %d ***\n", err);
- }
write(fuse->fd, &hdr, sizeof(hdr));
}
-void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
+static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
{
struct fuse_out_header hdr;
struct iovec vec[2];
@@ -499,481 +469,856 @@
}
}
-void lookup_entry(struct fuse *fuse, struct node *node,
- const char *name, __u64 unique)
+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;
-
- memset(&out, 0, sizeof(out));
+ struct stat s;
- node = node_lookup(fuse, node, name, &out.attr);
- if (!node) {
- fuse_status(fuse, unique, -ENOENT);
- return;
+ if (lstat(path, &s) < 0) {
+ return -errno;
}
-
- node->refcount++;
-// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
+
+ 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;
- out.entry_valid = 10;
- out.attr_valid = 10;
-
+ pthread_mutex_unlock(&fuse->lock);
fuse_reply(fuse, 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 fuse_reply_attr(struct fuse* fuse, __u64 unique, __u64 nid,
+ const char* path)
{
- struct node *node;
+ struct fuse_attr_out out;
+ struct stat s;
- if ((len < sizeof(*hdr)) || (hdr->len != len)) {
- ERROR("malformed header\n");
- return;
+ 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;
+}
- len -= hdr->len;
+static int handle_lookup(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];
+ const char* actual_name;
- if (hdr->nodeid) {
- node = lookup_by_inode(fuse, hdr->nodeid);
- if (!node) {
- fuse_status(fuse, hdr->unique, -ENOENT);
- return;
+ 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 fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
+}
+
+static int handle_forget(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const struct fuse_forget_in *req)
+{
+ 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);
}
- } else {
- node = 0;
+ }
+ pthread_mutex_unlock(&fuse->lock);
+ return NO_STATUS; /* no reply */
+}
+
+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 int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
+{
+ struct node* node;
+ char path[PATH_MAX];
+ struct timespec times[2];
+
+ 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 {
+ 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("[%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;
+}
+
+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;
+
+ 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);
+
+ /* 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;
+ }
+
+ 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;
+ }
+
+ 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;
+}
+
+static int handle_open(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 handle *h;
+
+ 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);
+
+ 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;
+}
+
+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;
+
+ /* 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. */
+
+ 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;
+}
+
+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_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;
+ }
+ out.size = res;
+ fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr)
+{
+ char path[PATH_MAX];
+ struct statfs stat;
+ struct fuse_statfs_out out;
+ int res;
+
+ 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;
+}
+
+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);
+
+ 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) {
+ return -errno;
+ }
+ return 0;
+}
+
+static int handle_flush(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr)
+{
+ 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);
+
+ if (!node) {
+ return -ENOENT;
+ }
+ 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;
+}
+
+static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler,
+ const struct fuse_in_header* hdr, const struct fuse_read_in* req)
+{
+ 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;
+}
+
+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);
+
+ 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;
+ 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;
+ 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;
+ 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_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;
+ 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;
+ 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_FLUSH: {
+ return handle_flush(fuse, handler, hdr);
+ }
+
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;
- }
-
- 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;
+ 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;
}
- handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len);
+
+ 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_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>] <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* 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(MOUNT_POINT, 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", MOUNT_POINT, "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, 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;
+ 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)
+ if (!strncmp(arg, "-t", 2))
+ num_threads = strtoul(arg + 2, 0, 10);
+ else if (!path)
path = arg;
- else if (uid == -1)
+ 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");
@@ -985,42 +1330,15 @@
ERROR("no path specified\n");
return usage();
}
- if (uid <= 0 || gid <= 0) {
+ if (!uid || !gid) {
ERROR("uid and gid must be nonzero\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;
+ if (num_threads < 1) {
+ ERROR("number of threads must be at least 1\n");
+ return usage();
}
- 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 (setuid(uid) < 0) {
- ERROR("cannot setuid!\n");
- return -1;
- }
-
- fuse_init(&fuse, fd, path);
-
- umask(0);
- handle_fuse_requests(&fuse);
-
- return 0;
+ res = run(path, uid, gid, num_threads);
+ return res < 0 ? 1 : 0;
}
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 @@
#include "dd.h"
-#define NO_CONV
+//#define NO_CONV
//#include "extern.h"
void block(void);
@@ -91,13 +91,11 @@
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 @@
} 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 @@
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 @@
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 @@
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 @@
#define C_UNBLOCK 0x80000
#define C_OSYNC 0x100000
#define C_SPARSE 0x200000
+#define C_FDATASYNC 0x400000