summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJorge Lucangeli Obes2016-07-12 14:13:05 -0500
committerJorge Lucangeli Obes2016-07-13 09:57:29 -0500
commitc255f25ccb700880483c73d9ff823bf9540dd616 (patch)
tree6e8f3cb9fec579fe476f051346c6aa42bfa212a1
parentf76ca2a43bb2f6f21b11738a8de80ea60c93a1ea (diff)
downloadplatform-system-core-c255f25ccb700880483c73d9ff823bf9540dd616.tar.gz
platform-system-core-c255f25ccb700880483c73d9ff823bf9540dd616.tar.xz
platform-system-core-c255f25ccb700880483c73d9ff823bf9540dd616.zip
Extract the FUSE implementation from the main sdcard.c file.
sdcard.c is a *really* big file. This makes it hard to do things like improving priv dropping or adding more sandboxing. Extract all FUSE-related code to a separate unit, fuse.{h|c}, which exports only two functions. Convert the rest of sdcard.c to C++ as sdcard.cpp. fuse.c is kept as C (at least for now) since interacting with the FUSE API is realistically easier from C. Bug: 30110940 Change-Id: I188bfdc21c184742117e07539adb09090d4d747c
-rw-r--r--sdcard/Android.mk2
-rw-r--r--sdcard/fuse.c (renamed from sdcard/sdcard.c)566
-rw-r--r--sdcard/fuse.h203
-rw-r--r--sdcard/sdcard.cpp418
4 files changed, 625 insertions, 564 deletions
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index ac5faa795..b12f3ee9f 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH := $(call my-dir)
2 2
3include $(CLEAR_VARS) 3include $(CLEAR_VARS)
4 4
5LOCAL_SRC_FILES := sdcard.c 5LOCAL_SRC_FILES := sdcard.cpp fuse.c
6LOCAL_MODULE := sdcard 6LOCAL_MODULE := sdcard
7LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror 7LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
8LOCAL_SHARED_LIBRARIES := liblog libcutils libpackagelistparser 8LOCAL_SHARED_LIBRARIES := liblog libcutils libpackagelistparser
diff --git a/sdcard/sdcard.c b/sdcard/fuse.c
index f08c9d8b3..bbf72846b 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/fuse.c
@@ -16,238 +16,14 @@
16 16
17#define LOG_TAG "sdcard" 17#define LOG_TAG "sdcard"
18 18
19#include <ctype.h> 19#include "fuse.h"
20#include <dirent.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <inttypes.h>
24#include <limits.h>
25#include <linux/fuse.h>
26#include <pthread.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/inotify.h>
32#include <sys/mount.h>
33#include <sys/param.h>
34#include <sys/resource.h>
35#include <sys/stat.h>
36#include <sys/statfs.h>
37#include <sys/time.h>
38#include <sys/types.h>
39#include <sys/uio.h>
40#include <unistd.h>
41
42#include <cutils/fs.h>
43#include <cutils/hashmap.h>
44#include <cutils/log.h>
45#include <cutils/multiuser.h>
46#include <packagelistparser/packagelistparser.h>
47
48#include <private/android_filesystem_config.h>
49
50/* README
51 *
52 * What is this?
53 *
54 * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
55 * directory permissions (all files are given fixed owner, group, and
56 * permissions at creation, owner, group, and permissions are not
57 * changeable, symlinks and hardlinks are not createable, etc.
58 *
59 * See usage() for command line options.
60 *
61 * It must be run as root, but will drop to requested UID/GID as soon as it
62 * mounts a filesystem. It will refuse to run if requested UID/GID are zero.
63 *
64 * Things I believe to be true:
65 *
66 * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
67 * CREAT) must bump that node's refcount
68 * - don't forget that FORGET can forget multiple references (req->nlookup)
69 * - if an op that returns a fuse_entry fails writing the reply to the
70 * kernel, you must rollback the refcount to reflect the reference the
71 * kernel did not actually acquire
72 *
73 * This daemon can also derive custom filesystem permissions based on directory
74 * structure when requested. These custom permissions support several features:
75 *
76 * - Apps can access their own files in /Android/data/com.example/ without
77 * requiring any additional GIDs.
78 * - Separate permissions for protecting directories like Pictures and Music.
79 * - Multi-user separation on the same physical device.
80 */
81
82#define FUSE_TRACE 0
83
84#if FUSE_TRACE
85#define TRACE(x...) ALOGD(x)
86#else
87#define TRACE(x...) do {} while (0)
88#endif
89
90#define ERROR(x...) ALOGE(x)
91 20
92#define FUSE_UNKNOWN_INO 0xffffffff 21#define FUSE_UNKNOWN_INO 0xffffffff
93 22
94/* Maximum number of bytes to write in one request. */
95#define MAX_WRITE (256 * 1024)
96
97/* Maximum number of bytes to read in one request. */
98#define MAX_READ (128 * 1024)
99
100/* Largest possible request.
101 * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
102 * the largest possible data payload. */
103#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
104
105/* Pseudo-error constant used to indicate that no fuse status is needed 23/* Pseudo-error constant used to indicate that no fuse status is needed
106 * or that a reply has already been written. */ 24 * or that a reply has already been written. */
107#define NO_STATUS 1 25#define NO_STATUS 1
108 26
109/* Supplementary groups to execute with */
110static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
111
112/* Permission mode for a specific node. Controls how file permissions
113 * are derived for children nodes. */
114typedef enum {
115 /* Nothing special; this node should just inherit from its parent. */
116 PERM_INHERIT,
117 /* This node is one level above a normal root; used for legacy layouts
118 * which use the first level to represent user_id. */
119 PERM_PRE_ROOT,
120 /* This node is "/" */
121 PERM_ROOT,
122 /* This node is "/Android" */
123 PERM_ANDROID,
124 /* This node is "/Android/data" */
125 PERM_ANDROID_DATA,
126 /* This node is "/Android/obb" */
127 PERM_ANDROID_OBB,
128 /* This node is "/Android/media" */
129 PERM_ANDROID_MEDIA,
130} perm_t;
131
132struct handle {
133 int fd;
134};
135
136struct dirhandle {
137 DIR *d;
138};
139
140struct node {
141 __u32 refcount;
142 __u64 nid;
143 __u64 gen;
144 /*
145 * The inode number for this FUSE node. Note that this isn't stable across
146 * multiple invocations of the FUSE daemon.
147 */
148 __u32 ino;
149
150 /* State derived based on current position in hierarchy. */
151 perm_t perm;
152 userid_t userid;
153 uid_t uid;
154 bool under_android;
155
156 struct node *next; /* per-dir sibling list */
157 struct node *child; /* first contained file by this dir */
158 struct node *parent; /* containing directory */
159
160 size_t namelen;
161 char *name;
162 /* If non-null, this is the real name of the file in the underlying storage.
163 * This may differ from the field "name" only by case.
164 * strlen(actual_name) will always equal strlen(name), so it is safe to use
165 * namelen for both fields.
166 */
167 char *actual_name;
168
169 /* If non-null, an exact underlying path that should be grafted into this
170 * position. Used to support things like OBB. */
171 char* graft_path;
172 size_t graft_pathlen;
173
174 bool deleted;
175};
176
177static int str_hash(void *key) {
178 return hashmapHash(key, strlen(key));
179}
180
181/** Test if two string keys are equal ignoring case */
182static bool str_icase_equals(void *keyA, void *keyB) {
183 return strcasecmp(keyA, keyB) == 0;
184}
185
186/* Global data for all FUSE mounts */
187struct fuse_global {
188 pthread_mutex_t lock;
189
190 uid_t uid;
191 gid_t gid;
192 bool multi_user;
193
194 char source_path[PATH_MAX];
195 char obb_path[PATH_MAX];
196
197 Hashmap* package_to_appid;
198
199 __u64 next_generation;
200 struct node root;
201
202 /* Used to allocate unique inode numbers for fuse nodes. We use
203 * a simple counter based scheme where inode numbers from deleted
204 * nodes aren't reused. Note that inode allocations are not stable
205 * across multiple invocation of the sdcard daemon, but that shouldn't
206 * be a huge problem in practice.
207 *
208 * Note that we restrict inodes to 32 bit unsigned integers to prevent
209 * truncation on 32 bit processes when unsigned long long stat.st_ino is
210 * assigned to an unsigned long ino_t type in an LP32 process.
211 *
212 * Also note that fuse_attr and fuse_dirent inode values are 64 bits wide
213 * on both LP32 and LP64, but the fuse kernel code doesn't squash 64 bit
214 * inode numbers into 32 bit values on 64 bit kernels (see fuse_squash_ino
215 * in fs/fuse/inode.c).
216 *
217 * Accesses must be guarded by |lock|.
218 */
219 __u32 inode_ctr;
220
221 struct fuse* fuse_default;
222 struct fuse* fuse_read;
223 struct fuse* fuse_write;
224};
225
226/* Single FUSE mount */
227struct fuse {
228 struct fuse_global* global;
229
230 char dest_path[PATH_MAX];
231
232 int fd;
233
234 gid_t gid;
235 mode_t mask;
236};
237
238/* Private data used by a single FUSE handler */
239struct fuse_handler {
240 struct fuse* fuse;
241 int token;
242
243 /* To save memory, we never use the contents of the request buffer and the read
244 * buffer at the same time. This allows us to share the underlying storage. */
245 union {
246 __u8 request_buffer[MAX_REQUEST_SIZE];
247 __u8 read_buffer[MAX_READ + PAGE_SIZE];
248 };
249};
250
251static inline void *id_to_ptr(__u64 nid) 27static inline void *id_to_ptr(__u64 nid)
252{ 28{
253 return (void *) (uintptr_t) nid; 29 return (void *) (uintptr_t) nid;
@@ -507,7 +283,7 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
507 } 283 }
508} 284}
509 285
510static void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent) { 286void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent) {
511 struct node *node; 287 struct node *node;
512 for (node = parent->child; node; node = node->next) { 288 for (node = parent->child; node; node = node->next) {
513 derive_permissions_locked(fuse, parent, node); 289 derive_permissions_locked(fuse, parent, node);
@@ -1608,7 +1384,7 @@ static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
1608 } 1384 }
1609} 1385}
1610 1386
1611static void handle_fuse_requests(struct fuse_handler* handler) 1387void handle_fuse_requests(struct fuse_handler* handler)
1612{ 1388{
1613 struct fuse* fuse = handler->fuse; 1389 struct fuse* fuse = handler->fuse;
1614 for (;;) { 1390 for (;;) {
@@ -1651,339 +1427,3 @@ static void handle_fuse_requests(struct fuse_handler* handler)
1651 } 1427 }
1652 } 1428 }
1653} 1429}
1654
1655static void* start_handler(void* data)
1656{
1657 struct fuse_handler* handler = data;
1658 handle_fuse_requests(handler);
1659 return NULL;
1660}
1661
1662static bool remove_str_to_int(void *key, void *value, void *context) {
1663 Hashmap* map = context;
1664 hashmapRemove(map, key);
1665 free(key);
1666 return true;
1667}
1668
1669static bool package_parse_callback(pkg_info *info, void *userdata) {
1670 struct fuse_global *global = (struct fuse_global *)userdata;
1671
1672 char* name = strdup(info->name);
1673 hashmapPut(global->package_to_appid, name, (void*) (uintptr_t) info->uid);
1674 packagelist_free(info);
1675 return true;
1676}
1677
1678static bool read_package_list(struct fuse_global* global) {
1679 pthread_mutex_lock(&global->lock);
1680
1681 hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid);
1682
1683 bool rc = packagelist_parse(package_parse_callback, global);
1684 TRACE("read_package_list: found %zu packages\n",
1685 hashmapSize(global->package_to_appid));
1686
1687 /* Regenerate ownership details using newly loaded mapping */
1688 derive_permissions_recursive_locked(global->fuse_default, &global->root);
1689
1690 pthread_mutex_unlock(&global->lock);
1691
1692 return rc;
1693}
1694
1695static void watch_package_list(struct fuse_global* global) {
1696 struct inotify_event *event;
1697 char event_buf[512];
1698
1699 int nfd = inotify_init();
1700 if (nfd < 0) {
1701 ERROR("inotify_init failed: %s\n", strerror(errno));
1702 return;
1703 }
1704
1705 bool active = false;
1706 while (1) {
1707 if (!active) {
1708 int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
1709 if (res == -1) {
1710 if (errno == ENOENT || errno == EACCES) {
1711 /* Framework may not have created yet, sleep and retry */
1712 ERROR("missing \"%s\"; retrying\n", PACKAGES_LIST_FILE);
1713 sleep(3);
1714 continue;
1715 } else {
1716 ERROR("inotify_add_watch failed: %s\n", strerror(errno));
1717 return;
1718 }
1719 }
1720
1721 /* Watch above will tell us about any future changes, so
1722 * read the current state. */
1723 if (read_package_list(global) == false) {
1724 ERROR("read_package_list failed\n");
1725 return;
1726 }
1727 active = true;
1728 }
1729
1730 int event_pos = 0;
1731 int res = read(nfd, event_buf, sizeof(event_buf));
1732 if (res < (int) sizeof(*event)) {
1733 if (errno == EINTR)
1734 continue;
1735 ERROR("failed to read inotify event: %s\n", strerror(errno));
1736 return;
1737 }
1738
1739 while (res >= (int) sizeof(*event)) {
1740 int event_size;
1741 event = (struct inotify_event *) (event_buf + event_pos);
1742
1743 TRACE("inotify event: %08x\n", event->mask);
1744 if ((event->mask & IN_IGNORED) == IN_IGNORED) {
1745 /* Previously watched file was deleted, probably due to move
1746 * that swapped in new data; re-arm the watch and read. */
1747 active = false;
1748 }
1749
1750 event_size = sizeof(*event) + event->len;
1751 res -= event_size;
1752 event_pos += event_size;
1753 }
1754 }
1755}
1756
1757static int usage() {
1758 ERROR("usage: sdcard [OPTIONS] <source_path> <label>\n"
1759 " -u: specify UID to run as\n"
1760 " -g: specify GID to run as\n"
1761 " -U: specify user ID that owns device\n"
1762 " -m: source_path is multi-user\n"
1763 " -w: runtime write mount has full write access\n"
1764 "\n");
1765 return 1;
1766}
1767
1768static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) {
1769 char opts[256];
1770
1771 fuse->fd = open("/dev/fuse", O_RDWR);
1772 if (fuse->fd == -1) {
1773 ERROR("failed to open fuse device: %s\n", strerror(errno));
1774 return -1;
1775 }
1776
1777 umount2(fuse->dest_path, MNT_DETACH);
1778
1779 snprintf(opts, sizeof(opts),
1780 "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
1781 fuse->fd, fuse->global->uid, fuse->global->gid);
1782 if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC |
1783 MS_NOATIME, opts) != 0) {
1784 ERROR("failed to mount fuse filesystem: %s\n", strerror(errno));
1785 return -1;
1786 }
1787
1788 fuse->gid = gid;
1789 fuse->mask = mask;
1790
1791 return 0;
1792}
1793
1794static void run(const char* source_path, const char* label, uid_t uid,
1795 gid_t gid, userid_t userid, bool multi_user, bool full_write) {
1796 struct fuse_global global;
1797 struct fuse fuse_default;
1798 struct fuse fuse_read;
1799 struct fuse fuse_write;
1800 struct fuse_handler handler_default;
1801 struct fuse_handler handler_read;
1802 struct fuse_handler handler_write;
1803 pthread_t thread_default;
1804 pthread_t thread_read;
1805 pthread_t thread_write;
1806
1807 memset(&global, 0, sizeof(global));
1808 memset(&fuse_default, 0, sizeof(fuse_default));
1809 memset(&fuse_read, 0, sizeof(fuse_read));
1810 memset(&fuse_write, 0, sizeof(fuse_write));
1811 memset(&handler_default, 0, sizeof(handler_default));
1812 memset(&handler_read, 0, sizeof(handler_read));
1813 memset(&handler_write, 0, sizeof(handler_write));
1814
1815 pthread_mutex_init(&global.lock, NULL);
1816 global.package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
1817 global.uid = uid;
1818 global.gid = gid;
1819 global.multi_user = multi_user;
1820 global.next_generation = 0;
1821 global.inode_ctr = 1;
1822
1823 memset(&global.root, 0, sizeof(global.root));
1824 global.root.nid = FUSE_ROOT_ID; /* 1 */
1825 global.root.refcount = 2;
1826 global.root.namelen = strlen(source_path);
1827 global.root.name = strdup(source_path);
1828 global.root.userid = userid;
1829 global.root.uid = AID_ROOT;
1830 global.root.under_android = false;
1831
1832 strcpy(global.source_path, source_path);
1833
1834 if (multi_user) {
1835 global.root.perm = PERM_PRE_ROOT;
1836 snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path);
1837 } else {
1838 global.root.perm = PERM_ROOT;
1839 snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path);
1840 }
1841
1842 fuse_default.global = &global;
1843 fuse_read.global = &global;
1844 fuse_write.global = &global;
1845
1846 global.fuse_default = &fuse_default;
1847 global.fuse_read = &fuse_read;
1848 global.fuse_write = &fuse_write;
1849
1850 snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label);
1851 snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label);
1852 snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label);
1853
1854 handler_default.fuse = &fuse_default;
1855 handler_read.fuse = &fuse_read;
1856 handler_write.fuse = &fuse_write;
1857
1858 handler_default.token = 0;
1859 handler_read.token = 1;
1860 handler_write.token = 2;
1861
1862 umask(0);
1863
1864 if (multi_user) {
1865 /* Multi-user storage is fully isolated per user, so "other"
1866 * permissions are completely masked off. */
1867 if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
1868 || fuse_setup(&fuse_read, AID_EVERYBODY, 0027)
1869 || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) {
1870 ERROR("failed to fuse_setup\n");
1871 exit(1);
1872 }
1873 } else {
1874 /* Physical storage is readable by all users on device, but
1875 * the Android directories are masked off to a single user
1876 * deep inside attr_from_stat(). */
1877 if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
1878 || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022)
1879 || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) {
1880 ERROR("failed to fuse_setup\n");
1881 exit(1);
1882 }
1883 }
1884
1885 /* Drop privs */
1886 if (setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups) < 0) {
1887 ERROR("cannot setgroups: %s\n", strerror(errno));
1888 exit(1);
1889 }
1890 if (setgid(gid) < 0) {
1891 ERROR("cannot setgid: %s\n", strerror(errno));
1892 exit(1);
1893 }
1894 if (setuid(uid) < 0) {
1895 ERROR("cannot setuid: %s\n", strerror(errno));
1896 exit(1);
1897 }
1898
1899 if (multi_user) {
1900 fs_prepare_dir(global.obb_path, 0775, uid, gid);
1901 }
1902
1903 if (pthread_create(&thread_default, NULL, start_handler, &handler_default)
1904 || pthread_create(&thread_read, NULL, start_handler, &handler_read)
1905 || pthread_create(&thread_write, NULL, start_handler, &handler_write)) {
1906 ERROR("failed to pthread_create\n");
1907 exit(1);
1908 }
1909
1910 watch_package_list(&global);
1911 ERROR("terminated prematurely\n");
1912 exit(1);
1913}
1914
1915int main(int argc, char **argv) {
1916 const char *source_path = NULL;
1917 const char *label = NULL;
1918 uid_t uid = 0;
1919 gid_t gid = 0;
1920 userid_t userid = 0;
1921 bool multi_user = false;
1922 bool full_write = false;
1923 int i;
1924 struct rlimit rlim;
1925 int fs_version;
1926
1927 int opt;
1928 while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) {
1929 switch (opt) {
1930 case 'u':
1931 uid = strtoul(optarg, NULL, 10);
1932 break;
1933 case 'g':
1934 gid = strtoul(optarg, NULL, 10);
1935 break;
1936 case 'U':
1937 userid = strtoul(optarg, NULL, 10);
1938 break;
1939 case 'm':
1940 multi_user = true;
1941 break;
1942 case 'w':
1943 full_write = true;
1944 break;
1945 case '?':
1946 default:
1947 return usage();
1948 }
1949 }
1950
1951 for (i = optind; i < argc; i++) {
1952 char* arg = argv[i];
1953 if (!source_path) {
1954 source_path = arg;
1955 } else if (!label) {
1956 label = arg;
1957 } else {
1958 ERROR("too many arguments\n");
1959 return usage();
1960 }
1961 }
1962
1963 if (!source_path) {
1964 ERROR("no source path specified\n");
1965 return usage();
1966 }
1967 if (!label) {
1968 ERROR("no label specified\n");
1969 return usage();
1970 }
1971 if (!uid || !gid) {
1972 ERROR("uid and gid must be nonzero\n");
1973 return usage();
1974 }
1975
1976 rlim.rlim_cur = 8192;
1977 rlim.rlim_max = 8192;
1978 if (setrlimit(RLIMIT_NOFILE, &rlim)) {
1979 ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno);
1980 }
1981
1982 while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
1983 ERROR("installd fs upgrade not yet complete. Waiting...\n");
1984 sleep(1);
1985 }
1986
1987 run(source_path, label, uid, gid, userid, multi_user, full_write);
1988 return 1;
1989}
diff --git a/sdcard/fuse.h b/sdcard/fuse.h
new file mode 100644
index 000000000..e1347f9ca
--- /dev/null
+++ b/sdcard/fuse.h
@@ -0,0 +1,203 @@
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef FUSE_H_
18#define FUSE_H_
19
20#include <dirent.h>
21#include <fcntl.h>
22#include <linux/fuse.h>
23#include <pthread.h>
24#include <stdbool.h>
25#include <stdlib.h>
26#include <sys/param.h>
27#include <sys/stat.h>
28#include <sys/statfs.h>
29#include <sys/types.h>
30#include <sys/uio.h>
31#include <unistd.h>
32
33#include <cutils/fs.h>
34#include <cutils/hashmap.h>
35#include <cutils/log.h>
36#include <cutils/multiuser.h>
37#include <packagelistparser/packagelistparser.h>
38
39#include <private/android_filesystem_config.h>
40
41#ifdef __cplusplus
42extern "C" {
43#endif
44
45#define FUSE_TRACE 0
46
47#if FUSE_TRACE
48#define TRACE(x...) ALOGD(x)
49#else
50#define TRACE(x...) do {} while (0)
51#endif
52
53#define ERROR(x...) ALOGE(x)
54
55/* Maximum number of bytes to write in one request. */
56#define MAX_WRITE (256 * 1024)
57
58/* Maximum number of bytes to read in one request. */
59#define MAX_READ (128 * 1024)
60
61/* Largest possible request.
62 * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
63 * the largest possible data payload. */
64#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
65
66/* Permission mode for a specific node. Controls how file permissions
67 * are derived for children nodes. */
68typedef enum {
69 /* Nothing special; this node should just inherit from its parent. */
70 PERM_INHERIT,
71 /* This node is one level above a normal root; used for legacy layouts
72 * which use the first level to represent user_id. */
73 PERM_PRE_ROOT,
74 /* This node is "/" */
75 PERM_ROOT,
76 /* This node is "/Android" */
77 PERM_ANDROID,
78 /* This node is "/Android/data" */
79 PERM_ANDROID_DATA,
80 /* This node is "/Android/obb" */
81 PERM_ANDROID_OBB,
82 /* This node is "/Android/media" */
83 PERM_ANDROID_MEDIA,
84} perm_t;
85
86struct handle {
87 int fd;
88};
89
90struct dirhandle {
91 DIR *d;
92};
93
94struct node {
95 __u32 refcount;
96 __u64 nid;
97 __u64 gen;
98 /*
99 * The inode number for this FUSE node. Note that this isn't stable across
100 * multiple invocations of the FUSE daemon.
101 */
102 __u32 ino;
103
104 /* State derived based on current position in hierarchy. */
105 perm_t perm;
106 userid_t userid;
107 uid_t uid;
108 bool under_android;
109
110 struct node *next; /* per-dir sibling list */
111 struct node *child; /* first contained file by this dir */
112 struct node *parent; /* containing directory */
113
114 size_t namelen;
115 char *name;
116 /* If non-null, this is the real name of the file in the underlying storage.
117 * This may differ from the field "name" only by case.
118 * strlen(actual_name) will always equal strlen(name), so it is safe to use
119 * namelen for both fields.
120 */
121 char *actual_name;
122
123 /* If non-null, an exact underlying path that should be grafted into this
124 * position. Used to support things like OBB. */
125 char* graft_path;
126 size_t graft_pathlen;
127
128 bool deleted;
129};
130
131/* Global data for all FUSE mounts */
132struct fuse_global {
133 pthread_mutex_t lock;
134
135 uid_t uid;
136 gid_t gid;
137 bool multi_user;
138
139 char source_path[PATH_MAX];
140 char obb_path[PATH_MAX];
141
142 Hashmap* package_to_appid;
143
144 __u64 next_generation;
145 struct node root;
146
147 /* Used to allocate unique inode numbers for fuse nodes. We use
148 * a simple counter based scheme where inode numbers from deleted
149 * nodes aren't reused. Note that inode allocations are not stable
150 * across multiple invocation of the sdcard daemon, but that shouldn't
151 * be a huge problem in practice.
152 *
153 * Note that we restrict inodes to 32 bit unsigned integers to prevent
154 * truncation on 32 bit processes when unsigned long long stat.st_ino is
155 * assigned to an unsigned long ino_t type in an LP32 process.
156 *
157 * Also note that fuse_attr and fuse_dirent inode values are 64 bits wide
158 * on both LP32 and LP64, but the fuse kernel code doesn't squash 64 bit
159 * inode numbers into 32 bit values on 64 bit kernels (see fuse_squash_ino
160 * in fs/fuse/inode.c).
161 *
162 * Accesses must be guarded by |lock|.
163 */
164 __u32 inode_ctr;
165
166 struct fuse* fuse_default;
167 struct fuse* fuse_read;
168 struct fuse* fuse_write;
169};
170
171/* Single FUSE mount */
172struct fuse {
173 struct fuse_global* global;
174
175 char dest_path[PATH_MAX];
176
177 int fd;
178
179 gid_t gid;
180 mode_t mask;
181};
182
183/* Private data used by a single FUSE handler */
184struct fuse_handler {
185 struct fuse* fuse;
186 int token;
187
188 /* To save memory, we never use the contents of the request buffer and the read
189 * buffer at the same time. This allows us to share the underlying storage. */
190 union {
191 __u8 request_buffer[MAX_REQUEST_SIZE];
192 __u8 read_buffer[MAX_READ + PAGE_SIZE];
193 };
194};
195
196void handle_fuse_requests(struct fuse_handler* handler);
197void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent);
198
199#ifdef __cplusplus
200}; /* extern "C" */
201#endif
202
203#endif /* FUSE_H_ */
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
new file mode 100644
index 000000000..00dcaf9e9
--- /dev/null
+++ b/sdcard/sdcard.cpp
@@ -0,0 +1,418 @@
1// Copyright (C) 2016 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#define LOG_TAG "sdcard"
16
17#include <dirent.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <linux/fuse.h>
21#include <pthread.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/inotify.h>
25#include <sys/mount.h>
26#include <sys/resource.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <unistd.h>
30
31#include <cutils/fs.h>
32#include <cutils/hashmap.h>
33#include <cutils/log.h>
34#include <cutils/multiuser.h>
35#include <packagelistparser/packagelistparser.h>
36
37#include <private/android_filesystem_config.h>
38
39/* README
40 *
41 * What is this?
42 *
43 * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
44 * directory permissions (all files are given fixed owner, group, and
45 * permissions at creation, owner, group, and permissions are not
46 * changeable, symlinks and hardlinks are not createable, etc.
47 *
48 * See usage() for command line options.
49 *
50 * It must be run as root, but will drop to requested UID/GID as soon as it
51 * mounts a filesystem. It will refuse to run if requested UID/GID are zero.
52 *
53 * Things I believe to be true:
54 *
55 * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
56 * CREAT) must bump that node's refcount
57 * - don't forget that FORGET can forget multiple references (req->nlookup)
58 * - if an op that returns a fuse_entry fails writing the reply to the
59 * kernel, you must rollback the refcount to reflect the reference the
60 * kernel did not actually acquire
61 *
62 * This daemon can also derive custom filesystem permissions based on directory
63 * structure when requested. These custom permissions support several features:
64 *
65 * - Apps can access their own files in /Android/data/com.example/ without
66 * requiring any additional GIDs.
67 * - Separate permissions for protecting directories like Pictures and Music.
68 * - Multi-user separation on the same physical device.
69 */
70
71#include "fuse.h"
72
73/* Supplementary groups to execute with. */
74static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
75
76static int str_hash(void *key) {
77 return hashmapHash(key, strlen(static_cast<const char*>(key)));
78}
79
80/* Tests if two string keys are equal ignoring case. */
81static bool str_icase_equals(void *keyA, void *keyB) {
82 return strcasecmp(static_cast<const char*>(keyA), static_cast<const char*>(keyB)) == 0;
83}
84
85static bool remove_str_to_int(void *key, void *value, void *context) {
86 Hashmap* map = static_cast<Hashmap*>(context);
87 hashmapRemove(map, key);
88 free(key);
89 return true;
90}
91
92static bool package_parse_callback(pkg_info *info, void *userdata) {
93 struct fuse_global *global = (struct fuse_global *)userdata;
94
95 char* name = strdup(info->name);
96 hashmapPut(global->package_to_appid, name, (void*) (uintptr_t) info->uid);
97 packagelist_free(info);
98 return true;
99}
100
101static bool read_package_list(struct fuse_global* global) {
102 pthread_mutex_lock(&global->lock);
103
104 hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid);
105
106 bool rc = packagelist_parse(package_parse_callback, global);
107 TRACE("read_package_list: found %zu packages\n",
108 hashmapSize(global->package_to_appid));
109
110 /* Regenerate ownership details using newly loaded mapping */
111 derive_permissions_recursive_locked(global->fuse_default, &global->root);
112
113 pthread_mutex_unlock(&global->lock);
114
115 return rc;
116}
117
118static void watch_package_list(struct fuse_global* global) {
119 struct inotify_event *event;
120 char event_buf[512];
121
122 int nfd = inotify_init();
123 if (nfd < 0) {
124 ERROR("inotify_init failed: %s\n", strerror(errno));
125 return;
126 }
127
128 bool active = false;
129 while (1) {
130 if (!active) {
131 int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
132 if (res == -1) {
133 if (errno == ENOENT || errno == EACCES) {
134 /* Framework may not have created yet, sleep and retry */
135 ERROR("missing \"%s\"; retrying\n", PACKAGES_LIST_FILE);
136 sleep(3);
137 continue;
138 } else {
139 ERROR("inotify_add_watch failed: %s\n", strerror(errno));
140 return;
141 }
142 }
143
144 /* Watch above will tell us about any future changes, so
145 * read the current state. */
146 if (read_package_list(global) == false) {
147 ERROR("read_package_list failed\n");
148 return;
149 }
150 active = true;
151 }
152
153 int event_pos = 0;
154 int res = read(nfd, event_buf, sizeof(event_buf));
155 if (res < (int) sizeof(*event)) {
156 if (errno == EINTR)
157 continue;
158 ERROR("failed to read inotify event: %s\n", strerror(errno));
159 return;
160 }
161
162 while (res >= (int) sizeof(*event)) {
163 int event_size;
164 event = (struct inotify_event *) (event_buf + event_pos);
165
166 TRACE("inotify event: %08x\n", event->mask);
167 if ((event->mask & IN_IGNORED) == IN_IGNORED) {
168 /* Previously watched file was deleted, probably due to move
169 * that swapped in new data; re-arm the watch and read. */
170 active = false;
171 }
172
173 event_size = sizeof(*event) + event->len;
174 res -= event_size;
175 event_pos += event_size;
176 }
177 }
178}
179
180static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) {
181 char opts[256];
182
183 fuse->fd = open("/dev/fuse", O_RDWR);
184 if (fuse->fd == -1) {
185 ERROR("failed to open fuse device: %s\n", strerror(errno));
186 return -1;
187 }
188
189 umount2(fuse->dest_path, MNT_DETACH);
190
191 snprintf(opts, sizeof(opts),
192 "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
193 fuse->fd, fuse->global->uid, fuse->global->gid);
194 if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC |
195 MS_NOATIME, opts) != 0) {
196 ERROR("failed to mount fuse filesystem: %s\n", strerror(errno));
197 return -1;
198 }
199
200 fuse->gid = gid;
201 fuse->mask = mask;
202
203 return 0;
204}
205
206static void* start_handler(void* data) {
207 struct fuse_handler* handler = static_cast<fuse_handler*>(data);
208 handle_fuse_requests(handler);
209 return NULL;
210}
211
212static void run(const char* source_path, const char* label, uid_t uid,
213 gid_t gid, userid_t userid, bool multi_user, bool full_write) {
214 struct fuse_global global;
215 struct fuse fuse_default;
216 struct fuse fuse_read;
217 struct fuse fuse_write;
218 struct fuse_handler handler_default;
219 struct fuse_handler handler_read;
220 struct fuse_handler handler_write;
221 pthread_t thread_default;
222 pthread_t thread_read;
223 pthread_t thread_write;
224
225 memset(&global, 0, sizeof(global));
226 memset(&fuse_default, 0, sizeof(fuse_default));
227 memset(&fuse_read, 0, sizeof(fuse_read));
228 memset(&fuse_write, 0, sizeof(fuse_write));
229 memset(&handler_default, 0, sizeof(handler_default));
230 memset(&handler_read, 0, sizeof(handler_read));
231 memset(&handler_write, 0, sizeof(handler_write));
232
233 pthread_mutex_init(&global.lock, NULL);
234 global.package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
235 global.uid = uid;
236 global.gid = gid;
237 global.multi_user = multi_user;
238 global.next_generation = 0;
239 global.inode_ctr = 1;
240
241 memset(&global.root, 0, sizeof(global.root));
242 global.root.nid = FUSE_ROOT_ID; /* 1 */
243 global.root.refcount = 2;
244 global.root.namelen = strlen(source_path);
245 global.root.name = strdup(source_path);
246 global.root.userid = userid;
247 global.root.uid = AID_ROOT;
248 global.root.under_android = false;
249
250 strcpy(global.source_path, source_path);
251
252 if (multi_user) {
253 global.root.perm = PERM_PRE_ROOT;
254 snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path);
255 } else {
256 global.root.perm = PERM_ROOT;
257 snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path);
258 }
259
260 fuse_default.global = &global;
261 fuse_read.global = &global;
262 fuse_write.global = &global;
263
264 global.fuse_default = &fuse_default;
265 global.fuse_read = &fuse_read;
266 global.fuse_write = &fuse_write;
267
268 snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label);
269 snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label);
270 snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label);
271
272 handler_default.fuse = &fuse_default;
273 handler_read.fuse = &fuse_read;
274 handler_write.fuse = &fuse_write;
275
276 handler_default.token = 0;
277 handler_read.token = 1;
278 handler_write.token = 2;
279
280 umask(0);
281
282 if (multi_user) {
283 /* Multi-user storage is fully isolated per user, so "other"
284 * permissions are completely masked off. */
285 if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
286 || fuse_setup(&fuse_read, AID_EVERYBODY, 0027)
287 || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) {
288 ERROR("failed to fuse_setup\n");
289 exit(1);
290 }
291 } else {
292 /* Physical storage is readable by all users on device, but
293 * the Android directories are masked off to a single user
294 * deep inside attr_from_stat(). */
295 if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
296 || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022)
297 || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) {
298 ERROR("failed to fuse_setup\n");
299 exit(1);
300 }
301 }
302
303 /* Drop privs */
304 if (setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups) < 0) {
305 ERROR("cannot setgroups: %s\n", strerror(errno));
306 exit(1);
307 }
308 if (setgid(gid) < 0) {
309 ERROR("cannot setgid: %s\n", strerror(errno));
310 exit(1);
311 }
312 if (setuid(uid) < 0) {
313 ERROR("cannot setuid: %s\n", strerror(errno));
314 exit(1);
315 }
316
317 if (multi_user) {
318 fs_prepare_dir(global.obb_path, 0775, uid, gid);
319 }
320
321 if (pthread_create(&thread_default, NULL, start_handler, &handler_default)
322 || pthread_create(&thread_read, NULL, start_handler, &handler_read)
323 || pthread_create(&thread_write, NULL, start_handler, &handler_write)) {
324 ERROR("failed to pthread_create\n");
325 exit(1);
326 }
327
328 watch_package_list(&global);
329 ERROR("terminated prematurely\n");
330 exit(1);
331}
332
333static int usage() {
334 ERROR("usage: sdcard [OPTIONS] <source_path> <label>\n"
335 " -u: specify UID to run as\n"
336 " -g: specify GID to run as\n"
337 " -U: specify user ID that owns device\n"
338 " -m: source_path is multi-user\n"
339 " -w: runtime write mount has full write access\n"
340 "\n");
341 return 1;
342}
343
344int main(int argc, char **argv) {
345 const char *source_path = NULL;
346 const char *label = NULL;
347 uid_t uid = 0;
348 gid_t gid = 0;
349 userid_t userid = 0;
350 bool multi_user = false;
351 bool full_write = false;
352 int i;
353 struct rlimit rlim;
354 int fs_version;
355
356 int opt;
357 while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) {
358 switch (opt) {
359 case 'u':
360 uid = strtoul(optarg, NULL, 10);
361 break;
362 case 'g':
363 gid = strtoul(optarg, NULL, 10);
364 break;
365 case 'U':
366 userid = strtoul(optarg, NULL, 10);
367 break;
368 case 'm':
369 multi_user = true;
370 break;
371 case 'w':
372 full_write = true;
373 break;
374 case '?':
375 default:
376 return usage();
377 }
378 }
379
380 for (i = optind; i < argc; i++) {
381 char* arg = argv[i];
382 if (!source_path) {
383 source_path = arg;
384 } else if (!label) {
385 label = arg;
386 } else {
387 ERROR("too many arguments\n");
388 return usage();
389 }
390 }
391
392 if (!source_path) {
393 ERROR("no source path specified\n");
394 return usage();
395 }
396 if (!label) {
397 ERROR("no label specified\n");
398 return usage();
399 }
400 if (!uid || !gid) {
401 ERROR("uid and gid must be nonzero\n");
402 return usage();
403 }
404
405 rlim.rlim_cur = 8192;
406 rlim.rlim_max = 8192;
407 if (setrlimit(RLIMIT_NOFILE, &rlim)) {
408 ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno);
409 }
410
411 while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
412 ERROR("installd fs upgrade not yet complete. Waiting...\n");
413 sleep(1);
414 }
415
416 run(source_path, label, uid, gid, userid, multi_user, full_write);
417 return 1;
418}