summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs_mgr/fs_mgr.c17
-rw-r--r--fs_mgr/fs_mgr_priv.h6
-rw-r--r--fs_mgr/include/fs_mgr.h1
-rw-r--r--include/cutils/fs.h8
-rw-r--r--include/memtrack/memtrack.h (renamed from libmemtrack/include/memtrack.h)9
-rw-r--r--libcutils/fs.c94
-rw-r--r--libmemtrack/Android.mk3
-rw-r--r--libmemtrack/memtrack.c2
-rw-r--r--libmemtrack/memtrack_test.c2
-rw-r--r--libutils/SystemClock.cpp13
-rw-r--r--logcat/event.logtags6
-rw-r--r--logwrapper/include/logwrap/logwrap.h16
-rw-r--r--logwrapper/logwrap.c43
-rw-r--r--logwrapper/logwrapper.c2
-rw-r--r--rootdir/init.rc7
-rw-r--r--sdcard/sdcard.c139
16 files changed, 277 insertions, 91 deletions
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 82c579821..f432f6a25 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -53,6 +53,8 @@
53#define E2FSCK_BIN "/system/bin/e2fsck" 53#define E2FSCK_BIN "/system/bin/e2fsck"
54#define MKSWAP_BIN "/system/bin/mkswap" 54#define MKSWAP_BIN "/system/bin/mkswap"
55 55
56#define FSCK_LOG_FILE "/dev/fscklogs/log"
57
56#define ZRAM_CONF_DEV "/sys/block/zram0/disksize" 58#define ZRAM_CONF_DEV "/sys/block/zram0/disksize"
57 59
58#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) 60#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
@@ -92,6 +94,7 @@ static struct flag_list fs_mgr_flags[] = {
92 { "swapprio=", MF_SWAPPRIO }, 94 { "swapprio=", MF_SWAPPRIO },
93 { "zramsize=", MF_ZRAMSIZE }, 95 { "zramsize=", MF_ZRAMSIZE },
94 { "verify", MF_VERIFY }, 96 { "verify", MF_VERIFY },
97 { "noemulatedsd", MF_NOEMULATEDSD },
95 { "defaults", 0 }, 98 { "defaults", 0 },
96 { 0, 0 }, 99 { 0, 0 },
97}; 100};
@@ -427,6 +430,10 @@ void fs_mgr_free_fstab(struct fstab *fstab)
427{ 430{
428 int i; 431 int i;
429 432
433 if (!fstab) {
434 return;
435 }
436
430 for (i = 0; i < fstab->num_entries; i++) { 437 for (i = 0; i < fstab->num_entries; i++) {
431 /* Free the pointers return by strdup(3) */ 438 /* Free the pointers return by strdup(3) */
432 free(fstab->recs[i].blk_device); 439 free(fstab->recs[i].blk_device);
@@ -483,7 +490,8 @@ static void check_fs(char *blk_device, char *fs_type, char *target)
483 INFO("Running %s on %s\n", E2FSCK_BIN, blk_device); 490 INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
484 491
485 ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, 492 ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
486 &status, true, LOG_KLOG, true); 493 &status, true, LOG_KLOG | LOG_FILE,
494 true, FSCK_LOG_FILE);
487 495
488 if (ret < 0) { 496 if (ret < 0) {
489 /* No need to check for error in fork, we can't really handle it now */ 497 /* No need to check for error in fork, we can't really handle it now */
@@ -800,7 +808,7 @@ int fs_mgr_swapon_all(struct fstab *fstab)
800 /* Initialize the swap area */ 808 /* Initialize the swap area */
801 mkswap_argv[1] = fstab->recs[i].blk_device; 809 mkswap_argv[1] = fstab->recs[i].blk_device;
802 err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv, 810 err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv,
803 &status, true, LOG_KLOG, false); 811 &status, true, LOG_KLOG, false, NULL);
804 if (err) { 812 if (err) {
805 ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device); 813 ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device);
806 ret = -1; 814 ret = -1;
@@ -931,3 +939,8 @@ int fs_mgr_is_encryptable(struct fstab_rec *fstab)
931{ 939{
932 return fstab->fs_mgr_flags & MF_CRYPT; 940 return fstab->fs_mgr_flags & MF_CRYPT;
933} 941}
942
943int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab)
944{
945 return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
946}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index f284ca69e..59ffd785c 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -72,6 +72,12 @@
72#define MF_SWAPPRIO 0x80 72#define MF_SWAPPRIO 0x80
73#define MF_ZRAMSIZE 0x100 73#define MF_ZRAMSIZE 0x100
74#define MF_VERIFY 0x200 74#define MF_VERIFY 0x200
75/*
76 * There is no emulated sdcard daemon running on /data/media on this device,
77 * so treat the physical SD card as the only external storage device,
78 * a la the Nexus One.
79 */
80#define MF_NOEMULATEDSD 0x400
75 81
76#define DM_BUF_SIZE 4096 82#define DM_BUF_SIZE 4096
77 83
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 384d19594..0f90c32f1 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -62,6 +62,7 @@ struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const ch
62int fs_mgr_is_voldmanaged(struct fstab_rec *fstab); 62int fs_mgr_is_voldmanaged(struct fstab_rec *fstab);
63int fs_mgr_is_nonremovable(struct fstab_rec *fstab); 63int fs_mgr_is_nonremovable(struct fstab_rec *fstab);
64int fs_mgr_is_encryptable(struct fstab_rec *fstab); 64int fs_mgr_is_encryptable(struct fstab_rec *fstab);
65int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab);
65int fs_mgr_swapon_all(struct fstab *fstab); 66int fs_mgr_swapon_all(struct fstab *fstab);
66#ifdef __cplusplus 67#ifdef __cplusplus
67} 68}
diff --git a/include/cutils/fs.h b/include/cutils/fs.h
index fd5296bb9..d1d4cf28d 100644
--- a/include/cutils/fs.h
+++ b/include/cutils/fs.h
@@ -55,6 +55,14 @@ extern int fs_read_atomic_int(const char* path, int* value);
55 */ 55 */
56extern int fs_write_atomic_int(const char* path, int value); 56extern int fs_write_atomic_int(const char* path, int value);
57 57
58/*
59 * Ensure that all directories along given path exist, creating parent
60 * directories as needed. Validates that given path is absolute and that
61 * it contains no relative "." or ".." paths or symlinks. Last path segment
62 * is treated as filename and ignored, unless the path ends with "/".
63 */
64extern int fs_mkdirs(const char* path, mode_t mode);
65
58#ifdef __cplusplus 66#ifdef __cplusplus
59} 67}
60#endif 68#endif
diff --git a/libmemtrack/include/memtrack.h b/include/memtrack/memtrack.h
index d6b370b22..0f1f85e22 100644
--- a/libmemtrack/include/memtrack.h
+++ b/include/memtrack/memtrack.h
@@ -19,6 +19,11 @@
19 19
20#include <sys/types.h> 20#include <sys/types.h>
21#include <stddef.h> 21#include <stddef.h>
22#include <cutils/compiler.h>
23
24#ifdef __cplusplus
25extern "C" {
26#endif
22 27
23/** 28/**
24 * struct memtrack_proc 29 * struct memtrack_proc
@@ -135,4 +140,8 @@ ssize_t memtrack_proc_other_total(struct memtrack_proc *p);
135 */ 140 */
136ssize_t memtrack_proc_other_pss(struct memtrack_proc *p); 141ssize_t memtrack_proc_other_pss(struct memtrack_proc *p);
137 142
143#ifdef __cplusplus
144}
145#endif
146
138#endif 147#endif
diff --git a/libcutils/fs.c b/libcutils/fs.c
index 116526dca..286a8eb09 100644
--- a/libcutils/fs.c
+++ b/libcutils/fs.c
@@ -16,6 +16,11 @@
16 16
17#define LOG_TAG "cutils" 17#define LOG_TAG "cutils"
18 18
19/* These defines are only needed because prebuilt headers are out of date */
20#define __USE_XOPEN2K8 1
21#define _ATFILE_SOURCE 1
22#define _GNU_SOURCE 1
23
19#include <cutils/fs.h> 24#include <cutils/fs.h>
20#include <cutils/log.h> 25#include <cutils/log.h>
21 26
@@ -27,6 +32,7 @@
27#include <string.h> 32#include <string.h>
28#include <limits.h> 33#include <limits.h>
29#include <stdlib.h> 34#include <stdlib.h>
35#include <dirent.h>
30 36
31#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) 37#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
32#define BUF_SIZE 64 38#define BUF_SIZE 64
@@ -141,3 +147,91 @@ fail_closed:
141 unlink(temp); 147 unlink(temp);
142 return -1; 148 return -1;
143} 149}
150
151#ifndef __APPLE__
152
153int fs_mkdirs(const char* path, mode_t mode) {
154 int res = 0;
155 int fd = 0;
156 struct stat sb;
157 char* buf = strdup(path);
158
159 if (*buf != '/') {
160 ALOGE("Relative paths are not allowed: %s", buf);
161 res = -EINVAL;
162 goto done;
163 }
164
165 if ((fd = open("/", 0)) == -1) {
166 ALOGE("Failed to open(/): %s", strerror(errno));
167 res = -errno;
168 goto done;
169 }
170
171 char* segment = buf + 1;
172 char* p = segment;
173 while (*p != '\0') {
174 if (*p == '/') {
175 *p = '\0';
176
177 if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
178 ALOGE("Invalid path: %s", buf);
179 res = -EINVAL;
180 goto done_close;
181 }
182
183 if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
184 if (errno == ENOENT) {
185 /* Nothing there yet; let's create it! */
186 if (mkdirat(fd, segment, mode) != 0) {
187 if (errno == EEXIST) {
188 /* We raced with someone; ignore */
189 } else {
190 ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
191 res = -errno;
192 goto done_close;
193 }
194 }
195 } else {
196 ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
197 res = -errno;
198 goto done_close;
199 }
200 } else {
201 if (S_ISLNK(sb.st_mode)) {
202 ALOGE("Symbolic links are not allowed: %s", buf);
203 res = -ELOOP;
204 goto done_close;
205 }
206 if (!S_ISDIR(sb.st_mode)) {
207 ALOGE("Existing segment not a directory: %s", buf);
208 res = -ENOTDIR;
209 goto done_close;
210 }
211 }
212
213 /* Yay, segment is ready for us to step into */
214 int next_fd;
215 if ((next_fd = openat(fd, segment, 0)) == -1) {
216 ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
217 res = -errno;
218 goto done_close;
219 }
220
221 close(fd);
222 fd = next_fd;
223
224 *p = '/';
225 segment = p + 1;
226 }
227 p++;
228 }
229
230done_close:
231 close(fd);
232done:
233 free(buf);
234 return res;
235}
236
237#endif
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
index c23b6f410..a8fb3eb00 100644
--- a/libmemtrack/Android.mk
+++ b/libmemtrack/Android.mk
@@ -3,10 +3,9 @@
3LOCAL_PATH:= $(call my-dir) 3LOCAL_PATH:= $(call my-dir)
4 4
5include $(CLEAR_VARS) 5include $(CLEAR_VARS)
6LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
7LOCAL_SRC_FILES := memtrack.c 6LOCAL_SRC_FILES := memtrack.c
8LOCAL_MODULE := libmemtrack 7LOCAL_MODULE := libmemtrack
9LOCAL_C_INCLUDES += $(LOCAL_PATH)/include hardware/libhardware/include 8LOCAL_C_INCLUDES += hardware/libhardware/include
10LOCAL_SHARED_LIBRARIES := libhardware liblog 9LOCAL_SHARED_LIBRARIES := libhardware liblog
11LOCAL_CFLAGS := -Wall -Werror 10LOCAL_CFLAGS := -Wall -Werror
12include $(BUILD_SHARED_LIBRARY) 11include $(BUILD_SHARED_LIBRARY)
diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c
index 2b2651a28..9a656dfd3 100644
--- a/libmemtrack/memtrack.c
+++ b/libmemtrack/memtrack.c
@@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17#include <memtrack.h> 17#include <memtrack/memtrack.h>
18 18
19#define LOG_TAG "memtrack" 19#define LOG_TAG "memtrack"
20 20
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c
index f306f67f1..cd94bc5e1 100644
--- a/libmemtrack/memtrack_test.c
+++ b/libmemtrack/memtrack_test.c
@@ -19,7 +19,7 @@
19#include <string.h> 19#include <string.h>
20#include <sys/types.h> 20#include <sys/types.h>
21 21
22#include <memtrack.h> 22#include <memtrack/memtrack.h>
23 23
24#include <pagemap/pagemap.h> 24#include <pagemap/pagemap.h>
25 25
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 4b7488941..ac8da8871 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -61,12 +61,20 @@ int64_t elapsedRealtime()
61#define METHOD_IOCTL 1 61#define METHOD_IOCTL 1
62#define METHOD_SYSTEMTIME 2 62#define METHOD_SYSTEMTIME 2
63 63
64/*
65 * To debug/verify the timestamps returned by the kernel, change
66 * DEBUG_TIMESTAMP to 1 and call the timestamp routine from a single thread
67 * in the test program. b/10899829
68 */
69#define DEBUG_TIMESTAMP 0
70
64static const char *gettime_method_names[] = { 71static const char *gettime_method_names[] = {
65 "clock_gettime", 72 "clock_gettime",
66 "ioctl", 73 "ioctl",
67 "systemTime", 74 "systemTime",
68}; 75};
69 76
77#if DEBUG_TIMESTAMP
70static inline void checkTimeStamps(int64_t timestamp, 78static inline void checkTimeStamps(int64_t timestamp,
71 int64_t volatile *prevTimestampPtr, 79 int64_t volatile *prevTimestampPtr,
72 int volatile *prevMethodPtr, 80 int volatile *prevMethodPtr,
@@ -93,6 +101,9 @@ static inline void checkTimeStamps(int64_t timestamp,
93 *prevMethodPtr = curMethod; 101 *prevMethodPtr = curMethod;
94#endif 102#endif
95} 103}
104#else
105#define checkTimeStamps(timestamp, prevTimestampPtr, prevMethodPtr, curMethod)
106#endif
96 107
97/* 108/*
98 * native public static long elapsedRealtimeNano(); 109 * native public static long elapsedRealtimeNano();
@@ -103,8 +114,10 @@ int64_t elapsedRealtimeNano()
103 struct timespec ts; 114 struct timespec ts;
104 int result; 115 int result;
105 int64_t timestamp; 116 int64_t timestamp;
117#if DEBUG_TIMESTAMP
106 static volatile int64_t prevTimestamp; 118 static volatile int64_t prevTimestamp;
107 static volatile int prevMethod; 119 static volatile int prevMethod;
120#endif
108 121
109#if 0 122#if 0
110 /* 123 /*
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 3a1b28140..a325692e8 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -90,10 +90,6 @@
90# Logged when the supplicant switches to a new state 90# Logged when the supplicant switches to a new state
9150023 wifi_supplicant_state_changed (supplicant_state|1|5) 9150023 wifi_supplicant_state_changed (supplicant_state|1|5)
92 92
93# Do not change these names without updating tag in:
94#//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c
9551000 socket_stats (send|1|2),(recv|1|2),(ip|1|5),(port|1|5),(close|1|5)
96
97# Database operation samples. 93# Database operation samples.
98# db: the filename of the database 94# db: the filename of the database
99# sql: the executed query (without query args) 95# sql: the executed query (without query args)
@@ -136,7 +132,7 @@
13680310 bionic_event_resolver_wrong_query (uid|1) 13280310 bionic_event_resolver_wrong_query (uid|1)
137 133
138# libcore failure logging 134# libcore failure logging
13990100 cert_pin_failure (certs|4) 13590100 exp_det_cert_pin_failure (certs|4)
140 136
141# NOTE - the range 1000000-2000000 is reserved for partners and others who 137# NOTE - the range 1000000-2000000 is reserved for partners and others who
142# want to define their own log tags without conflicting with the core platform. 138# want to define their own log tags without conflicting with the core platform.
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
index 8087f0a4b..4307a3055 100644
--- a/logwrapper/include/logwrap/logwrap.h
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -44,11 +44,15 @@ __BEGIN_DECLS
44 * send a signal twice to signal the caller (once for the child, and 44 * send a signal twice to signal the caller (once for the child, and
45 * once for the caller) 45 * once for the caller)
46 * log_target: Specify where to log the output of the child, either LOG_NONE, 46 * log_target: Specify where to log the output of the child, either LOG_NONE,
47 * LOG_ALOG (for the Android system log) or LOG_KLOG (for the kernel 47 * LOG_ALOG (for the Android system log), LOG_KLOG (for the kernel
48 * log). 48 * log), or LOG_FILE (and you need to specify a pathname in the
49 * file_path argument, otherwise pass NULL). These are bit fields,
50 * and can be OR'ed together to log to multiple places.
49 * abbreviated: If true, capture up to the first 100 lines and last 4K of 51 * abbreviated: If true, capture up to the first 100 lines and last 4K of
50 * output from the child. The abbreviated output is not dumped to 52 * output from the child. The abbreviated output is not dumped to
51 * the specified log until the child has exited. 53 * the specified log until the child has exited.
54 * file_path: if log_target has the LOG_FILE bit set, then this parameter
55 * must be set to the pathname of the file to log to.
52 * 56 *
53 * Return value: 57 * Return value:
54 * 0 when logwrap successfully run the child process and captured its status 58 * 0 when logwrap successfully run the child process and captured its status
@@ -58,13 +62,14 @@ __BEGIN_DECLS
58 * 62 *
59 */ 63 */
60 64
61/* Values for the log_target parameter android_fork_exec_ext() */ 65/* Values for the log_target parameter android_fork_execvp_ext() */
62#define LOG_NONE 0 66#define LOG_NONE 0
63#define LOG_ALOG 1 67#define LOG_ALOG 1
64#define LOG_KLOG 2 68#define LOG_KLOG 2
69#define LOG_FILE 4
65 70
66int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit, 71int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
67 int log_target, bool abbreviated); 72 int log_target, bool abbreviated, char *file_path);
68 73
69/* Similar to above, except abbreviated logging is not available, and if logwrap 74/* Similar to above, except abbreviated logging is not available, and if logwrap
70 * is true, logging is to the Android system log, and if false, there is no 75 * is true, logging is to the Android system log, and if false, there is no
@@ -74,10 +79,9 @@ static inline int android_fork_execvp(int argc, char* argv[], int *status,
74 bool ignore_int_quit, bool logwrap) 79 bool ignore_int_quit, bool logwrap)
75{ 80{
76 return android_fork_execvp_ext(argc, argv, status, ignore_int_quit, 81 return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
77 (logwrap ? LOG_ALOG : LOG_NONE), false); 82 (logwrap ? LOG_ALOG : LOG_NONE), false, NULL);
78} 83}
79 84
80
81__END_DECLS 85__END_DECLS
82 86
83#endif /* __LIBS_LOGWRAP_H */ 87#endif /* __LIBS_LOGWRAP_H */
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 01cc9a189..4ca1db4c8 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -93,6 +93,7 @@ struct log_info {
93 char klog_fmt[MAX_KLOG_TAG * 2]; 93 char klog_fmt[MAX_KLOG_TAG * 2];
94 char *btag; 94 char *btag;
95 bool abbreviated; 95 bool abbreviated;
96 FILE *fp;
96 struct abbr_buf a_buf; 97 struct abbr_buf a_buf;
97}; 98};
98 99
@@ -158,11 +159,15 @@ static void add_line_to_circular_buf(struct ending_buf *e_buf,
158 159
159/* Log directly to the specified log */ 160/* Log directly to the specified log */
160static void do_log_line(struct log_info *log_info, char *line) { 161static void do_log_line(struct log_info *log_info, char *line) {
161 if (log_info->log_target == LOG_KLOG) { 162 if (log_info->log_target & LOG_KLOG) {
162 klog_write(6, log_info->klog_fmt, line); 163 klog_write(6, log_info->klog_fmt, line);
163 } else if (log_info->log_target == LOG_ALOG) { 164 }
165 if (log_info->log_target & LOG_ALOG) {
164 ALOG(LOG_INFO, log_info->btag, "%s", line); 166 ALOG(LOG_INFO, log_info->btag, "%s", line);
165 } 167 }
168 if (log_info->log_target & LOG_FILE) {
169 fprintf(log_info->fp, "%s\n", line);
170 }
166} 171}
167 172
168/* Log to either the abbreviated buf, or directly to the specified log 173/* Log to either the abbreviated buf, or directly to the specified log
@@ -290,7 +295,7 @@ static void print_abbr_buf(struct log_info *log_info) {
290} 295}
291 296
292static int parent(const char *tag, int parent_read, pid_t pid, 297static int parent(const char *tag, int parent_read, pid_t pid,
293 int *chld_sts, int log_target, bool abbreviated) { 298 int *chld_sts, int log_target, bool abbreviated, char *file_path) {
294 int status = 0; 299 int status = 0;
295 char buffer[4096]; 300 char buffer[4096];
296 struct pollfd poll_fds[] = { 301 struct pollfd poll_fds[] = {
@@ -300,6 +305,7 @@ static int parent(const char *tag, int parent_read, pid_t pid,
300 }, 305 },
301 }; 306 };
302 int rc = 0; 307 int rc = 0;
308 int fd;
303 309
304 struct log_info log_info; 310 struct log_info log_info;
305 311
@@ -309,8 +315,6 @@ static int parent(const char *tag, int parent_read, pid_t pid,
309 bool found_child = false; 315 bool found_child = false;
310 char tmpbuf[256]; 316 char tmpbuf[256];
311 317
312 log_info.log_target = log_target;
313 log_info.abbreviated = abbreviated;
314 log_info.btag = basename(tag); 318 log_info.btag = basename(tag);
315 if (!log_info.btag) { 319 if (!log_info.btag) {
316 log_info.btag = (char*) tag; 320 log_info.btag = (char*) tag;
@@ -323,11 +327,30 @@ static int parent(const char *tag, int parent_read, pid_t pid,
323 init_abbr_buf(&log_info.a_buf); 327 init_abbr_buf(&log_info.a_buf);
324 } 328 }
325 329
326 if (log_target == LOG_KLOG) { 330 if (log_target & LOG_KLOG) {
327 snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt), 331 snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt),
328 "<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag); 332 "<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag);
329 } 333 }
330 334
335 if ((log_target & LOG_FILE) && !file_path) {
336 /* No file_path specified, clear the LOG_FILE bit */
337 log_target &= ~LOG_FILE;
338 }
339
340 if (log_target & LOG_FILE) {
341 fd = open(file_path, O_WRONLY | O_CREAT, 0664);
342 if (fd < 0) {
343 ERROR("Cannot log to file %s\n", file_path);
344 log_target &= ~LOG_FILE;
345 } else {
346 lseek(fd, 0, SEEK_END);
347 log_info.fp = fdopen(fd, "a");
348 }
349 }
350
351 log_info.log_target = log_target;
352 log_info.abbreviated = abbreviated;
353
331 while (!found_child) { 354 while (!found_child) {
332 if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) { 355 if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) {
333 ERROR("poll failed\n"); 356 ERROR("poll failed\n");
@@ -432,6 +455,9 @@ static int parent(const char *tag, int parent_read, pid_t pid,
432 455
433err_waitpid: 456err_waitpid:
434err_poll: 457err_poll:
458 if (log_target & LOG_FILE) {
459 fclose(log_info.fp); /* Also closes underlying fd */
460 }
435 if (abbreviated) { 461 if (abbreviated) {
436 free_abbr_buf(&log_info.a_buf); 462 free_abbr_buf(&log_info.a_buf);
437 } 463 }
@@ -451,7 +477,7 @@ static void child(int argc, char* argv[]) {
451} 477}
452 478
453int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit, 479int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
454 int log_target, bool abbreviated) { 480 int log_target, bool abbreviated, char *file_path) {
455 pid_t pid; 481 pid_t pid;
456 int parent_ptty; 482 int parent_ptty;
457 int child_ptty; 483 int child_ptty;
@@ -523,7 +549,8 @@ int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int
523 sigaction(SIGQUIT, &ignact, &quitact); 549 sigaction(SIGQUIT, &ignact, &quitact);
524 } 550 }
525 551
526 rc = parent(argv[0], parent_ptty, pid, status, log_target, abbreviated); 552 rc = parent(argv[0], parent_ptty, pid, status, log_target,
553 abbreviated, file_path);
527 } 554 }
528 555
529 if (ignore_int_quit) { 556 if (ignore_int_quit) {
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
index d1c62403a..d0d8d1471 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.c
@@ -80,7 +80,7 @@ int main(int argc, char* argv[]) {
80 } 80 }
81 81
82 rc = android_fork_execvp_ext(argc, &argv[0], &status, true, 82 rc = android_fork_execvp_ext(argc, &argv[0], &status, true,
83 log_target, abbreviated); 83 log_target, abbreviated, NULL);
84 if (!rc) { 84 if (!rc) {
85 if (WIFEXITED(status)) 85 if (WIFEXITED(status))
86 rc = WEXITSTATUS(status); 86 rc = WEXITSTATUS(status);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index be74f6fc2..86e124f15 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -61,7 +61,8 @@ loglevel 3
61 61
62 # See storage config details at http://source.android.com/tech/storage/ 62 # See storage config details at http://source.android.com/tech/storage/
63 mkdir /mnt/shell 0700 shell shell 63 mkdir /mnt/shell 0700 shell shell
64 mkdir /storage 0050 root sdcard_r 64 mkdir /mnt/media_rw 0700 media_rw media_rw
65 mkdir /storage 0751 root sdcard_r
65 66
66 # Directory for putting things only root should see. 67 # Directory for putting things only root should see.
67 mkdir /mnt/secure 0700 root root 68 mkdir /mnt/secure 0700 root root
@@ -133,6 +134,10 @@ loglevel 3
133# This is needed by any process that uses socket tagging. 134# This is needed by any process that uses socket tagging.
134 chmod 0644 /dev/xt_qtaguid 135 chmod 0644 /dev/xt_qtaguid
135 136
137# Create location for fs_mgr to store abbreviated output from filesystem
138# checker programs.
139 mkdir /dev/fscklogs 0770 root system
140
136on post-fs 141on post-fs
137 # once everything is setup, no need to modify / 142 # once everything is setup, no need to modify /
138 mount rootfs rootfs / ro remount 143 mount rootfs rootfs / ro remount
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 9a1dd17b7..05fbfbad3 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -32,6 +32,7 @@
32#include <sys/resource.h> 32#include <sys/resource.h>
33#include <sys/inotify.h> 33#include <sys/inotify.h>
34 34
35#include <cutils/fs.h>
35#include <cutils/hashmap.h> 36#include <cutils/hashmap.h>
36#include <cutils/multiuser.h> 37#include <cutils/multiuser.h>
37 38
@@ -193,8 +194,9 @@ static int str_hash(void *key) {
193 return hashmapHash(key, strlen(key)); 194 return hashmapHash(key, strlen(key));
194} 195}
195 196
196static bool str_equals(void *keyA, void *keyB) { 197/** Test if two string keys are equal ignoring case */
197 return strcmp(keyA, keyB) == 0; 198static bool str_icase_equals(void *keyA, void *keyB) {
199 return strcasecmp(keyA, keyB) == 0;
198} 200}
199 201
200static int int_hash(void *key) { 202static int int_hash(void *key) {
@@ -213,6 +215,7 @@ struct fuse {
213 int fd; 215 int fd;
214 derive_t derive; 216 derive_t derive;
215 bool split_perms; 217 bool split_perms;
218 gid_t write_gid;
216 struct node root; 219 struct node root;
217 char obbpath[PATH_MAX]; 220 char obbpath[PATH_MAX];
218 221
@@ -401,6 +404,20 @@ static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const s
401 attr->mode = (attr->mode & S_IFMT) | filtered_mode; 404 attr->mode = (attr->mode & S_IFMT) | filtered_mode;
402} 405}
403 406
407static int touch(char* path, mode_t mode) {
408 int fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, mode);
409 if (fd == -1) {
410 if (errno == EEXIST) {
411 return 0;
412 } else {
413 ERROR("Failed to open(%s): %s\n", path, strerror(errno));
414 return -1;
415 }
416 }
417 close(fd);
418 return 0;
419}
420
404static void derive_permissions_locked(struct fuse* fuse, struct node *parent, 421static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
405 struct node *node) { 422 struct node *node) {
406 appid_t appid; 423 appid_t appid;
@@ -429,37 +446,37 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
429 case PERM_ROOT: 446 case PERM_ROOT:
430 /* Assume masked off by default. */ 447 /* Assume masked off by default. */
431 node->mode = 0770; 448 node->mode = 0770;
432 if (!strcmp(node->name, "Android")) { 449 if (!strcasecmp(node->name, "Android")) {
433 /* App-specific directories inside; let anyone traverse */ 450 /* App-specific directories inside; let anyone traverse */
434 node->perm = PERM_ANDROID; 451 node->perm = PERM_ANDROID;
435 node->mode = 0771; 452 node->mode = 0771;
436 } else if (fuse->split_perms) { 453 } else if (fuse->split_perms) {
437 if (!strcmp(node->name, "DCIM") 454 if (!strcasecmp(node->name, "DCIM")
438 || !strcmp(node->name, "Pictures")) { 455 || !strcasecmp(node->name, "Pictures")) {
439 node->gid = AID_SDCARD_PICS; 456 node->gid = AID_SDCARD_PICS;
440 } else if (!strcmp(node->name, "Alarms") 457 } else if (!strcasecmp(node->name, "Alarms")
441 || !strcmp(node->name, "Movies") 458 || !strcasecmp(node->name, "Movies")
442 || !strcmp(node->name, "Music") 459 || !strcasecmp(node->name, "Music")
443 || !strcmp(node->name, "Notifications") 460 || !strcasecmp(node->name, "Notifications")
444 || !strcmp(node->name, "Podcasts") 461 || !strcasecmp(node->name, "Podcasts")
445 || !strcmp(node->name, "Ringtones")) { 462 || !strcasecmp(node->name, "Ringtones")) {
446 node->gid = AID_SDCARD_AV; 463 node->gid = AID_SDCARD_AV;
447 } 464 }
448 } 465 }
449 break; 466 break;
450 case PERM_ANDROID: 467 case PERM_ANDROID:
451 if (!strcmp(node->name, "data")) { 468 if (!strcasecmp(node->name, "data")) {
452 /* App-specific directories inside; let anyone traverse */ 469 /* App-specific directories inside; let anyone traverse */
453 node->perm = PERM_ANDROID_DATA; 470 node->perm = PERM_ANDROID_DATA;
454 node->mode = 0771; 471 node->mode = 0771;
455 } else if (!strcmp(node->name, "obb")) { 472 } else if (!strcasecmp(node->name, "obb")) {
456 /* App-specific directories inside; let anyone traverse */ 473 /* App-specific directories inside; let anyone traverse */
457 node->perm = PERM_ANDROID_OBB; 474 node->perm = PERM_ANDROID_OBB;
458 node->mode = 0771; 475 node->mode = 0771;
459 /* Single OBB directory is always shared */ 476 /* Single OBB directory is always shared */
460 node->graft_path = fuse->obbpath; 477 node->graft_path = fuse->obbpath;
461 node->graft_pathlen = strlen(fuse->obbpath); 478 node->graft_pathlen = strlen(fuse->obbpath);
462 } else if (!strcmp(node->name, "user")) { 479 } else if (!strcasecmp(node->name, "user")) {
463 /* User directories must only be accessible to system, protected 480 /* User directories must only be accessible to system, protected
464 * by sdcard_all. Zygote will bind mount the appropriate user- 481 * by sdcard_all. Zygote will bind mount the appropriate user-
465 * specific path. */ 482 * specific path. */
@@ -505,9 +522,9 @@ static bool check_caller_access_to_name(struct fuse* fuse,
505 const char* name, int mode, bool has_rw) { 522 const char* name, int mode, bool has_rw) {
506 /* Always block security-sensitive files at root */ 523 /* Always block security-sensitive files at root */
507 if (parent_node && parent_node->perm == PERM_ROOT) { 524 if (parent_node && parent_node->perm == PERM_ROOT) {
508 if (!strcmp(name, "autorun.inf") 525 if (!strcasecmp(name, "autorun.inf")
509 || !strcmp(name, ".android_secure") 526 || !strcasecmp(name, ".android_secure")
510 || !strcmp(name, "android_secure")) { 527 || !strcasecmp(name, "android_secure")) {
511 return false; 528 return false;
512 } 529 }
513 } 530 }
@@ -517,8 +534,9 @@ static bool check_caller_access_to_name(struct fuse* fuse,
517 return true; 534 return true;
518 } 535 }
519 536
520 /* Root or shell always have access */ 537 /* Root always has access; access for any other UIDs should always
521 if (hdr->uid == 0 || hdr->uid == AID_SHELL) { 538 * be controlled through packages.list. */
539 if (hdr->uid == 0) {
522 return true; 540 return true;
523 } 541 }
524 542
@@ -664,13 +682,14 @@ static struct node* acquire_or_create_child_locked(
664} 682}
665 683
666static void fuse_init(struct fuse *fuse, int fd, const char *source_path, 684static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
667 gid_t fs_gid, derive_t derive, bool split_perms) { 685 gid_t write_gid, derive_t derive, bool split_perms) {
668 pthread_mutex_init(&fuse->lock, NULL); 686 pthread_mutex_init(&fuse->lock, NULL);
669 687
670 fuse->fd = fd; 688 fuse->fd = fd;
671 fuse->next_generation = 0; 689 fuse->next_generation = 0;
672 fuse->derive = derive; 690 fuse->derive = derive;
673 fuse->split_perms = split_perms; 691 fuse->split_perms = split_perms;
692 fuse->write_gid = write_gid;
674 693
675 memset(&fuse->root, 0, sizeof(fuse->root)); 694 memset(&fuse->root, 0, sizeof(fuse->root));
676 fuse->root.nid = FUSE_ROOT_ID; /* 1 */ 695 fuse->root.nid = FUSE_ROOT_ID; /* 1 */
@@ -695,18 +714,19 @@ static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
695 * just below that. Shared OBB path is also at top level. */ 714 * just below that. Shared OBB path is also at top level. */
696 fuse->root.perm = PERM_LEGACY_PRE_ROOT; 715 fuse->root.perm = PERM_LEGACY_PRE_ROOT;
697 fuse->root.mode = 0771; 716 fuse->root.mode = 0771;
698 fuse->root.gid = fs_gid; 717 fuse->root.gid = AID_SDCARD_R;
699 fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals); 718 fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
700 fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); 719 fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
701 snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path); 720 snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
721 fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid());
702 break; 722 break;
703 case DERIVE_UNIFIED: 723 case DERIVE_UNIFIED:
704 /* Unified multiuser layout which places secondary user_id under 724 /* Unified multiuser layout which places secondary user_id under
705 * /Android/user and shared OBB path under /Android/obb. */ 725 * /Android/user and shared OBB path under /Android/obb. */
706 fuse->root.perm = PERM_ROOT; 726 fuse->root.perm = PERM_ROOT;
707 fuse->root.mode = 0771; 727 fuse->root.mode = 0771;
708 fuse->root.gid = fs_gid; 728 fuse->root.gid = AID_SDCARD_R;
709 fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals); 729 fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
710 fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); 730 fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
711 snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path); 731 snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path);
712 break; 732 break;
@@ -752,36 +772,7 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
752 struct stat s; 772 struct stat s;
753 773
754 if (lstat(path, &s) < 0) { 774 if (lstat(path, &s) < 0) {
755 /* But wait! We'll automatically create a directory if its 775 return -errno;
756 * a valid package name under data or obb, since apps may not
757 * have enough permissions to create for themselves. */
758 if (errno == ENOENT && (parent->perm == PERM_ANDROID_DATA
759 || parent->perm == PERM_ANDROID_OBB)) {
760 TRACE("automatically creating %s\n", path);
761
762 pthread_mutex_lock(&fuse->lock);
763 bool validPackage = hashmapContainsKey(fuse->package_to_appid, (char*) name);
764 pthread_mutex_unlock(&fuse->lock);
765
766 if (!validPackage) {
767 return -ENOENT;
768 }
769 if (mkdir(path, 0775) == -1) {
770 /* We might have raced with ourselves and already created */
771 if (errno != EEXIST) {
772 ERROR("failed to mkdir(%s): %s\n", name, strerror(errno));
773 return -ENOENT;
774 }
775 }
776
777 /* It should exist this time around! */
778 if (lstat(path, &s) < 0) {
779 ERROR("failed to lstat(%s): %s\n", name, strerror(errno));
780 return -errno;
781 }
782 } else {
783 return -errno;
784 }
785 } 776 }
786 777
787 pthread_mutex_lock(&fuse->lock); 778 pthread_mutex_lock(&fuse->lock);
@@ -1006,6 +997,25 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
1006 if (mkdir(child_path, mode) < 0) { 997 if (mkdir(child_path, mode) < 0) {
1007 return -errno; 998 return -errno;
1008 } 999 }
1000
1001 /* When creating /Android/data and /Android/obb, mark them as .nomedia */
1002 if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "data")) {
1003 char nomedia[PATH_MAX];
1004 snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path);
1005 if (touch(nomedia, 0664) != 0) {
1006 ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
1007 return -ENOENT;
1008 }
1009 }
1010 if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) {
1011 char nomedia[PATH_MAX];
1012 snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obbpath);
1013 if (touch(nomedia, 0664) != 0) {
1014 ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
1015 return -ENOENT;
1016 }
1017 }
1018
1009 return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); 1019 return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
1010} 1020}
1011 1021
@@ -1615,7 +1625,7 @@ static int read_package_list(struct fuse *fuse) {
1615 1625
1616 char* token = strtok(gids, ","); 1626 char* token = strtok(gids, ",");
1617 while (token != NULL) { 1627 while (token != NULL) {
1618 if (strtoul(token, NULL, 10) == AID_SDCARD_RW) { 1628 if (strtoul(token, NULL, 10) == fuse->write_gid) {
1619 hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 1); 1629 hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 1);
1620 break; 1630 break;
1621 } 1631 }
@@ -1624,7 +1634,7 @@ static int read_package_list(struct fuse *fuse) {
1624 } 1634 }
1625 } 1635 }
1626 1636
1627 TRACE("read_package_list: found %d packages, %d with sdcard_rw\n", 1637 TRACE("read_package_list: found %d packages, %d with write_gid\n",
1628 hashmapSize(fuse->package_to_appid), 1638 hashmapSize(fuse->package_to_appid),
1629 hashmapSize(fuse->appid_with_rw)); 1639 hashmapSize(fuse->appid_with_rw));
1630 fclose(file); 1640 fclose(file);
@@ -1741,7 +1751,7 @@ static int usage()
1741 ERROR("usage: sdcard [OPTIONS] <source_path> <dest_path>\n" 1751 ERROR("usage: sdcard [OPTIONS] <source_path> <dest_path>\n"
1742 " -u: specify UID to run as\n" 1752 " -u: specify UID to run as\n"
1743 " -g: specify GID to run as\n" 1753 " -g: specify GID to run as\n"
1744 " -G: specify default GID for files (default sdcard_r, requires -d or -l)\n" 1754 " -w: specify GID required to write (default sdcard_rw, requires -d or -l)\n"
1745 " -t: specify number of threads to use (default %d)\n" 1755 " -t: specify number of threads to use (default %d)\n"
1746 " -d: derive file permissions based on path\n" 1756 " -d: derive file permissions based on path\n"
1747 " -l: derive file permissions based on legacy internal layout\n" 1757 " -l: derive file permissions based on legacy internal layout\n"
@@ -1751,7 +1761,8 @@ static int usage()
1751} 1761}
1752 1762
1753static int run(const char* source_path, const char* dest_path, uid_t uid, 1763static int run(const char* source_path, const char* dest_path, uid_t uid,
1754 gid_t gid, gid_t fs_gid, int num_threads, derive_t derive, bool split_perms) { 1764 gid_t gid, gid_t write_gid, int num_threads, derive_t derive,
1765 bool split_perms) {
1755 int fd; 1766 int fd;
1756 char opts[256]; 1767 char opts[256];
1757 int res; 1768 int res;
@@ -1794,7 +1805,7 @@ static int run(const char* source_path, const char* dest_path, uid_t uid,
1794 goto error; 1805 goto error;
1795 } 1806 }
1796 1807
1797 fuse_init(&fuse, fd, source_path, fs_gid, derive, split_perms); 1808 fuse_init(&fuse, fd, source_path, write_gid, derive, split_perms);
1798 1809
1799 umask(0); 1810 umask(0);
1800 res = ignite_fuse(&fuse, num_threads); 1811 res = ignite_fuse(&fuse, num_threads);
@@ -1814,7 +1825,7 @@ int main(int argc, char **argv)
1814 const char *dest_path = NULL; 1825 const char *dest_path = NULL;
1815 uid_t uid = 0; 1826 uid_t uid = 0;
1816 gid_t gid = 0; 1827 gid_t gid = 0;
1817 gid_t fs_gid = AID_SDCARD_R; 1828 gid_t write_gid = AID_SDCARD_RW;
1818 int num_threads = DEFAULT_NUM_THREADS; 1829 int num_threads = DEFAULT_NUM_THREADS;
1819 derive_t derive = DERIVE_NONE; 1830 derive_t derive = DERIVE_NONE;
1820 bool split_perms = false; 1831 bool split_perms = false;
@@ -1822,7 +1833,7 @@ int main(int argc, char **argv)
1822 struct rlimit rlim; 1833 struct rlimit rlim;
1823 1834
1824 int opt; 1835 int opt;
1825 while ((opt = getopt(argc, argv, "u:g:G:t:dls")) != -1) { 1836 while ((opt = getopt(argc, argv, "u:g:w:t:dls")) != -1) {
1826 switch (opt) { 1837 switch (opt) {
1827 case 'u': 1838 case 'u':
1828 uid = strtoul(optarg, NULL, 10); 1839 uid = strtoul(optarg, NULL, 10);
@@ -1830,8 +1841,8 @@ int main(int argc, char **argv)
1830 case 'g': 1841 case 'g':
1831 gid = strtoul(optarg, NULL, 10); 1842 gid = strtoul(optarg, NULL, 10);
1832 break; 1843 break;
1833 case 'G': 1844 case 'w':
1834 fs_gid = strtoul(optarg, NULL, 10); 1845 write_gid = strtoul(optarg, NULL, 10);
1835 break; 1846 break;
1836 case 't': 1847 case 't':
1837 num_threads = strtoul(optarg, NULL, 10); 1848 num_threads = strtoul(optarg, NULL, 10);
@@ -1894,6 +1905,6 @@ int main(int argc, char **argv)
1894 ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno); 1905 ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno);
1895 } 1906 }
1896 1907
1897 res = run(source_path, dest_path, uid, gid, fs_gid, num_threads, derive, split_perms); 1908 res = run(source_path, dest_path, uid, gid, write_gid, num_threads, derive, split_perms);
1898 return res < 0 ? 1 : 0; 1909 return res < 0 ? 1 : 0;
1899} 1910}