aboutsummaryrefslogtreecommitdiffstats
path: root/libcutils
diff options
context:
space:
mode:
authorSteve Kondik <shade@chemlab.org>2012-11-18 19:19:50 -0800
committerSteve Kondik <shade@chemlab.org>2012-11-18 19:19:50 -0800
commit39d33d8d54ba55e49f9b430f842647a84751cb85 (patch)
treef5a3756ea25d3b87902ae6a6a8df2428509e4246 /libcutils
parentd8aa8ab7424be375e4408ab360c000ac8b05d15d (diff)
parent31da9db0d1bf3227e3c383aa6ac28bde3c6409e5 (diff)
downloadsystem_core-39d33d8d54ba55e49f9b430f842647a84751cb85.tar.gz
system_core-39d33d8d54ba55e49f9b430f842647a84751cb85.tar.bz2
system_core-39d33d8d54ba55e49f9b430f842647a84751cb85.zip
Merge branch 'jb-mr1-release' of https://android.googlesource.com/platform/system/core into mr1
Conflicts: adb/Android.mk adb/usb_vendors.c include/private/android_filesystem_config.h include/system/audio.h include/system/camera.h init/property_service.c libnetutils/ifc_utils.c mkbootimg/mkbootimg.c rootdir/init.rc Change-Id: Ie42f0c14808e9f8cabd24854bfe15b6667955229
Diffstat (limited to 'libcutils')
-rw-r--r--libcutils/Android.mk9
-rw-r--r--libcutils/atomic-android-sh.c137
-rw-r--r--libcutils/dlmalloc_stubs.c20
-rw-r--r--libcutils/fs.c142
-rw-r--r--libcutils/mspace.c286
-rw-r--r--libcutils/multiuser.c29
6 files changed, 187 insertions, 436 deletions
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 6a2ba663..3051baf7 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -50,7 +50,7 @@ commonSources := \
threads.c \
sched_policy.c \
iosched_policy.c \
- str_parms.c
+ str_parms.c \
commonHostSources := \
ashmem-host.c
@@ -75,9 +75,10 @@ ifeq ($(WINDOWS_HOST_ONLY),1)
else
commonSources += \
abort_socket.c \
- mspace.c \
+ fs.c \
selector.c \
tztime.c \
+ multiuser.c \
zygote.c
commonHostSources += \
@@ -129,16 +130,12 @@ LOCAL_SRC_FILES := $(commonSources) \
ifeq ($(TARGET_ARCH),arm)
LOCAL_SRC_FILES += arch-arm/memset32.S
else # !arm
-ifeq ($(TARGET_ARCH),sh)
-LOCAL_SRC_FILES += memory.c atomic-android-sh.c
-else # !sh
ifeq ($(TARGET_ARCH_VARIANT),x86-atom)
LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32
LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c
else # !x86-atom
LOCAL_SRC_FILES += memory.c
endif # !x86-atom
-endif # !sh
endif # !arm
ifneq ($(TARGET_RECOVERY_PRE_COMMAND),)
diff --git a/libcutils/atomic-android-sh.c b/libcutils/atomic-android-sh.c
deleted file mode 100644
index 8bac68ad..00000000
--- a/libcutils/atomic-android-sh.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2007 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 <cutils/atomic.h>
-#ifdef HAVE_WIN32_THREADS
-#include <windows.h>
-#else
-#include <sched.h>
-#endif
-
-/*
- * Note :
- *
- * (1) SuperH does not have CMPXCHG. It has only TAS for atomic
- * operations. It does not seem a good idea to implement CMPXCHG,
- * with TAS. So, we choose to implemnt these operations with
- * posix mutexes. Please be sure that this might cause performance
- * problem for Android-SH. Using LL/SC instructions supported in SH-X3,
- * best performnace would be realized.
- *
- * (2) Mutex initialization problem happens, which is commented for
- * ARM implementation, in this file above.
- * We follow the fact that the initializer for mutex is a simple zero
- * value.
- *
- * (3) These operations are NOT safe for SMP, as there is no currently
- * no definition for a memory barrier operation.
- */
-
-#include <pthread.h>
-
-#define SWAP_LOCK_COUNT 32U
-static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT];
-
-#define SWAP_LOCK(addr) \
- &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT]
-
-
-int32_t android_atomic_acquire_load(volatile const int32_t* addr)
-{
- return *addr;
-}
-
-int32_t android_atomic_release_load(volatile const int32_t* addr)
-{
- return *addr;
-}
-
-void android_atomic_acquire_store(int32_t value, volatile int32_t* addr) {
- int32_t oldValue;
- do {
- oldValue = *addr;
- } while (android_atomic_release_cas(oldValue, value, addr));
-}
-
-void android_atomic_release_store(int32_t value, volatile int32_t* addr) {
- int32_t oldValue;
- do {
- oldValue = *addr;
- } while (android_atomic_release_cas(oldValue, value, addr));
-}
-
-int32_t android_atomic_inc(volatile int32_t* addr) {
- int32_t oldValue;
- do {
- oldValue = *addr;
- } while (android_atomic_release_cas(oldValue, oldValue+1, addr));
- return oldValue;
-}
-
-int32_t android_atomic_dec(volatile int32_t* addr) {
- int32_t oldValue;
- do {
- oldValue = *addr;
- } while (android_atomic_release_cas(oldValue, oldValue-1, addr));
- return oldValue;
-}
-
-int32_t android_atomic_add(int32_t value, volatile int32_t* addr) {
- int32_t oldValue;
- do {
- oldValue = *addr;
- } while (android_atomic_release_cas(oldValue, oldValue+value, addr));
- return oldValue;
-}
-
-int32_t android_atomic_and(int32_t value, volatile int32_t* addr) {
- int32_t oldValue;
- do {
- oldValue = *addr;
- } while (android_atomic_release_cas(oldValue, oldValue&value, addr));
- return oldValue;
-}
-
-int32_t android_atomic_or(int32_t value, volatile int32_t* addr) {
- int32_t oldValue;
- do {
- oldValue = *addr;
- } while (android_atomic_release_cas(oldValue, oldValue|value, addr));
- return oldValue;
-}
-
-int android_atomic_acquire_cmpxchg(int32_t oldvalue, int32_t newvalue,
- volatile int32_t* addr) {
- return android_atomic_release_cmpxchg(oldValue, newValue, addr);
-}
-
-int android_atomic_release_cmpxchg(int32_t oldvalue, int32_t newvalue,
- volatile int32_t* addr) {
- int result;
- pthread_mutex_t* lock = SWAP_LOCK(addr);
-
- pthread_mutex_lock(lock);
-
- if (*addr == oldvalue) {
- *addr = newvalue;
- result = 0;
- } else {
- result = 1;
- }
- pthread_mutex_unlock(lock);
- return result;
-}
-
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
index 1ced147b..c327a55a 100644
--- a/libcutils/dlmalloc_stubs.c
+++ b/libcutils/dlmalloc_stubs.c
@@ -14,16 +14,22 @@
* limitations under the License.
*/
-/* No-op stubs for functions defined in system/bionic/bionic/dlmalloc.c.
- */
-void dlmalloc_walk_free_pages()
-{
-}
+#include "../../../bionic/libc/bionic/dlmalloc.h"
+#include "cutils/log.h"
-void dlmalloc_walk_heap()
+/*
+ * Stubs for functions defined in bionic/libc/bionic/dlmalloc.c. These
+ * are used in host builds, as the host libc will not contain these
+ * functions.
+ */
+void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*),
+ void* arg)
{
+ ALOGW("Called host unimplemented stub: dlmalloc_inspect_all");
}
-void dlmalloc_trim()
+int dlmalloc_trim(size_t unused)
{
+ ALOGW("Called host unimplemented stub: dlmalloc_trim");
+ return 0;
}
diff --git a/libcutils/fs.c b/libcutils/fs.c
new file mode 100644
index 00000000..1226d447
--- /dev/null
+++ b/libcutils/fs.c
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "cutils"
+
+#include <cutils/fs.h>
+#include <cutils/log.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+
+#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+#define BUF_SIZE 64
+
+int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+ // Check if path needs to be created
+ struct stat sb;
+ if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) {
+ if (errno == ENOENT) {
+ goto create;
+ } else {
+ ALOGE("Failed to lstat(%s): %s", path, strerror(errno));
+ return -1;
+ }
+ }
+
+ // Exists, verify status
+ if (!S_ISDIR(sb.st_mode)) {
+ ALOGE("Not a directory: %s", path);
+ return -1;
+ }
+ if (((sb.st_mode & ALL_PERMS) == mode) && (sb.st_uid == uid) && (sb.st_gid == gid)) {
+ return 0;
+ } else {
+ goto fixup;
+ }
+
+create:
+ if (TEMP_FAILURE_RETRY(mkdir(path, mode)) == -1) {
+ if (errno != EEXIST) {
+ ALOGE("Failed to mkdir(%s): %s", path, strerror(errno));
+ return -1;
+ }
+ }
+
+fixup:
+ if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) {
+ ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno));
+ return -1;
+ }
+ if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) {
+ ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int fs_read_atomic_int(const char* path, int* out_value) {
+ int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));
+ if (fd == -1) {
+ ALOGE("Failed to read %s: %s", path, strerror(errno));
+ return -1;
+ }
+
+ char buf[BUF_SIZE];
+ if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) {
+ ALOGE("Failed to read %s: %s", path, strerror(errno));
+ goto fail;
+ }
+ if (sscanf(buf, "%d", out_value) != 1) {
+ ALOGE("Failed to parse %s: %s", path, strerror(errno));
+ goto fail;
+ }
+ close(fd);
+ return 0;
+
+fail:
+ close(fd);
+ *out_value = -1;
+ return -1;
+}
+
+int fs_write_atomic_int(const char* path, int value) {
+ char temp[PATH_MAX];
+ if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) {
+ ALOGE("Path too long");
+ return -1;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(mkstemp(temp));
+ if (fd == -1) {
+ ALOGE("Failed to open %s: %s", temp, strerror(errno));
+ return -1;
+ }
+
+ char buf[BUF_SIZE];
+ int len = snprintf(buf, BUF_SIZE, "%d", value) + 1;
+ if (len > BUF_SIZE) {
+ ALOGE("Value %d too large: %s", value, strerror(errno));
+ goto fail;
+ }
+ if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) {
+ ALOGE("Failed to write %s: %s", temp, strerror(errno));
+ goto fail;
+ }
+ if (close(fd) == -1) {
+ ALOGE("Failed to close %s: %s", temp, strerror(errno));
+ goto fail_closed;
+ }
+
+ if (rename(temp, path) == -1) {
+ ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno));
+ goto fail_closed;
+ }
+
+ return 0;
+
+fail:
+ close(fd);
+fail_closed:
+ unlink(temp);
+ return -1;
+}
diff --git a/libcutils/mspace.c b/libcutils/mspace.c
deleted file mode 100644
index 6d3b35c8..00000000
--- a/libcutils/mspace.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/* Copyright 2006 The Android Open Source Project */
-
-/* A wrapper file for dlmalloc.c that compiles in the
- * mspace_*() functions, which provide an interface for
- * creating multiple heaps.
- */
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <sys/ioctl.h>
-
-#include <cutils/ashmem.h>
-
-/* It's a pain getting the mallinfo stuff to work
- * with Linux, OSX, and klibc, so just turn it off
- * for now.
- * TODO: make mallinfo work
- */
-#define NO_MALLINFO 1
-
-/* Allow setting the maximum heap footprint.
- */
-#define USE_MAX_ALLOWED_FOOTPRINT 1
-
-/* Don't try to trim memory.
- * TODO: support this.
- */
-#define MORECORE_CANNOT_TRIM 1
-
-/* Use mmap()d anonymous memory to guarantee
- * that an mspace is contiguous.
- *
- * create_mspace() won't work right if this is
- * defined, so hide the definition of it and
- * break any users at build time.
- */
-#define USE_CONTIGUOUS_MSPACES 1
-#if USE_CONTIGUOUS_MSPACES
-/* This combination of settings forces sys_alloc()
- * to always use MORECORE(). It won't expect the
- * results to be contiguous, but we'll guarantee
- * that they are.
- */
-#define HAVE_MMAP 0
-#define HAVE_MORECORE 1
-#define MORECORE_CONTIGUOUS 0
-/* m is always the appropriate local when MORECORE() is called. */
-#define MORECORE(S) contiguous_mspace_morecore(m, S)
-#define create_mspace HIDDEN_create_mspace_HIDDEN
-#define destroy_mspace HIDDEN_destroy_mspace_HIDDEN
-typedef struct malloc_state *mstate0;
-static void *contiguous_mspace_morecore(mstate0 m, ssize_t nb);
-#endif
-
-#define MSPACES 1
-#define ONLY_MSPACES 1
-#include "../../../bionic/libc/bionic/dlmalloc.c"
-
-#ifndef PAGESIZE
-#define PAGESIZE mparams.page_size
-#endif
-
-#define ALIGN_UP(p, alignment) \
- (((uintptr_t)(p) + (alignment)-1) & ~((alignment)-1))
-
-/* A direct copy of dlmalloc_usable_size(),
- * which isn't compiled in when ONLY_MSPACES is set.
- * The mspace parameter isn't actually necessary,
- * but we include it to be consistent with the
- * rest of the mspace_*() functions.
- */
-size_t mspace_usable_size(mspace _unused, const void* mem) {
- if (mem != 0) {
- const mchunkptr p = mem2chunk(mem);
- if (cinuse(p))
- return chunksize(p) - overhead_for(p);
- }
- return 0;
-}
-
-#if USE_CONTIGUOUS_MSPACES
-#include <sys/mman.h>
-#include <limits.h>
-
-#define CONTIG_STATE_MAGIC 0xf00dd00d
-struct mspace_contig_state {
- unsigned int magic;
- char *brk;
- char *top;
- mspace m;
-};
-
-static void *contiguous_mspace_morecore(mstate m, ssize_t nb) {
- struct mspace_contig_state *cs;
- char *oldbrk;
- const unsigned int pagesize = PAGESIZE;
-
- cs = (struct mspace_contig_state *)((uintptr_t)m & ~(pagesize-1));
- assert(cs->magic == CONTIG_STATE_MAGIC);
- assert(cs->m == m);
-assert(nb >= 0); //xxx deal with the trim case
-
- oldbrk = cs->brk;
- if (nb > 0) {
- /* Break to the first page boundary that satisfies the request.
- */
- char *newbrk = (char *)ALIGN_UP(oldbrk + nb, pagesize);
- if (newbrk > cs->top)
- return CMFAIL;
-
- /* Update the protection on the underlying memory.
- * Pages we've given to dlmalloc are read/write, and
- * pages we haven't are not accessable (read or write
- * will cause a seg fault).
- */
- if (mprotect(cs, newbrk - (char *)cs, PROT_READ | PROT_WRITE) < 0)
- return CMFAIL;
- if (newbrk != cs->top) {
- if (mprotect(newbrk, cs->top - newbrk, PROT_NONE) < 0)
- return CMFAIL;
- }
-
- cs->brk = newbrk;
-
- /* Make sure that dlmalloc will merge this block with the
- * initial block that was passed to create_mspace_with_base().
- * We don't care about extern vs. non-extern, so just clear it.
- */
- m->seg.sflags &= ~EXTERN_BIT;
- }
-
- return oldbrk;
-}
-
-mspace create_contiguous_mspace_with_base(size_t starting_capacity,
- size_t max_capacity, int locked, void *base) {
- struct mspace_contig_state *cs;
- unsigned int pagesize;
- mstate m;
-
- init_mparams();
- pagesize = PAGESIZE;
- assert(starting_capacity <= max_capacity);
- assert(((uintptr_t)base & (pagesize-1)) == 0);
- assert(((uintptr_t)max_capacity & (pagesize-1)) == 0);
- starting_capacity = (size_t)ALIGN_UP(starting_capacity, pagesize);
-
- /* Make the first page read/write. dlmalloc needs to use that page.
- */
- if (mprotect(base, starting_capacity, PROT_READ | PROT_WRITE) < 0) {
- goto error;
- }
-
- /* Create the mspace, pointing to the memory given.
- */
- m = create_mspace_with_base((char *)base + sizeof(*cs), starting_capacity,
- locked);
- if (m == (mspace)0) {
- goto error;
- }
- /* Make sure that m is in the same page as base.
- */
- assert(((uintptr_t)m & (uintptr_t)~(pagesize-1)) == (uintptr_t)base);
- /* Use some space for the information that our MORECORE needs.
- */
- cs = (struct mspace_contig_state *)base;
-
- /* Find out exactly how much of the memory the mspace
- * is using.
- */
- cs->brk = m->seg.base + m->seg.size;
- cs->top = (char *)base + max_capacity;
-
- assert((char *)base <= cs->brk);
- assert(cs->brk <= cs->top);
- /* Prevent access to the memory we haven't handed out yet.
- */
- if (cs->brk != cs->top) {
- /* mprotect() requires page-aligned arguments, but it's possible
- * for cs->brk not to be page-aligned at this point.
- */
- char *prot_brk = (char *)ALIGN_UP(cs->brk, pagesize);
- if ((mprotect(base, prot_brk - (char *)base, PROT_READ | PROT_WRITE) < 0) ||
- (mprotect(prot_brk, cs->top - prot_brk, PROT_NONE) < 0)) {
- goto error;
- }
- }
-
- cs->m = m;
- cs->magic = CONTIG_STATE_MAGIC;
-
- return (mspace)m;
-
-error:
- return (mspace)0;
-}
-
-
-mspace create_contiguous_mspace_with_name(size_t starting_capacity,
- size_t max_capacity, int locked, char const *name) {
- int fd, ret;
- char buf[ASHMEM_NAME_LEN] = "mspace";
- void *base;
- unsigned int pagesize;
- mstate m;
-
- if (starting_capacity > max_capacity)
- return (mspace)0;
-
- init_mparams();
- pagesize = PAGESIZE;
-
- /* Create the anonymous memory that will back the mspace.
- * This reserves all of the virtual address space we could
- * ever need. Physical pages will be mapped as the memory
- * is touched.
- *
- * Align max_capacity to a whole page.
- */
- max_capacity = (size_t)ALIGN_UP(max_capacity, pagesize);
-
- if (name)
- snprintf(buf, sizeof(buf), "mspace/%s", name);
- fd = ashmem_create_region(buf, max_capacity);
- if (fd < 0)
- return (mspace)0;
-
- base = mmap(NULL, max_capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
- close(fd);
- if (base == MAP_FAILED)
- return (mspace)0;
-
- /* Make sure that base is at the beginning of a page.
- */
- assert(((uintptr_t)base & (pagesize-1)) == 0);
-
- m = create_contiguous_mspace_with_base(starting_capacity, max_capacity,
- locked, base);
- if (m == 0) {
- munmap(base, max_capacity);
- }
- return m;
-}
-
-mspace create_contiguous_mspace(size_t starting_capacity,
- size_t max_capacity, int locked) {
- return create_contiguous_mspace_with_name(starting_capacity,
- max_capacity, locked, NULL);
-}
-
-size_t destroy_contiguous_mspace(mspace msp) {
- mstate ms = (mstate)msp;
-
- if (ok_magic(ms)) {
- struct mspace_contig_state *cs;
- size_t length;
- const unsigned int pagesize = PAGESIZE;
-
- cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1));
- assert(cs->magic == CONTIG_STATE_MAGIC);
- assert(cs->m == ms);
-
- length = cs->top - (char *)cs;
- if (munmap((char *)cs, length) != 0)
- return length;
- }
- else {
- USAGE_ERROR_ACTION(ms, ms);
- }
- return 0;
-}
-
-void *contiguous_mspace_sbrk0(mspace msp) {
- struct mspace_contig_state *cs;
- mstate ms;
- const unsigned int pagesize = PAGESIZE;
-
- ms = (mstate)msp;
- cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1));
- assert(cs->magic == CONTIG_STATE_MAGIC);
- assert(cs->m == ms);
- return cs->brk;
-}
-#endif
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
new file mode 100644
index 00000000..7c74bb86
--- /dev/null
+++ b/libcutils/multiuser.c
@@ -0,0 +1,29 @@
+/*
+ * 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 <cutils/multiuser.h>
+
+userid_t multiuser_get_user_id(uid_t uid) {
+ return uid / MULTIUSER_APP_PER_USER_RANGE;
+}
+
+appid_t multiuser_get_app_id(uid_t uid) {
+ return uid % MULTIUSER_APP_PER_USER_RANGE;
+}
+
+uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
+ return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+}