Arquivos
hhvm/hphp/runtime/ext/ext_apc.cpp
T
ptarjan 0038b76a58 kill TAINTED code
While I was working on the TestCodeRun refactor I found two tests about Tainted code. I looked into it and coulnd't get HHVM to compile with TAINTED=1. Then I checked and none of the extension functions we exposed about tainting were used in WWW. Scratching my head I asked, @srenfro and @jdelong, who  thought it was dead. So I killed this zombie.
2013-04-12 12:04:04 -07:00

1304 linhas
41 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
| Copyright (c) 1997-2010 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <runtime/ext/ext_apc.h>
#include <runtime/ext/ext_variable.h>
#include <runtime/ext/ext_fb.h>
#include <runtime/base/runtime_option.h>
#include <util/async_job.h>
#include <util/timer.h>
#include <dlfcn.h>
#include <runtime/base/program_functions.h>
#include <runtime/base/builtin_functions.h>
#include <runtime/base/variable_serializer.h>
#include <util/alloc.h>
#include <runtime/base/ini_setting.h>
using HPHP::Util::ScopedMem;
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
extern void const_load();
static class apcExtension : public Extension {
public:
apcExtension() : Extension("apc") {}
virtual void moduleInit() {
IniSetting::SetGlobalDefault("apc.enabled","1");
IniSetting::SetGlobalDefault("apc.stat",
RuntimeOption::RepoAuthoritative
? "0" : "1");
IniSetting::SetGlobalDefault("apc.enable_cli", "1");
if (RuntimeOption::ApcUseFileStorage) {
s_apc_file_storage.enable(RuntimeOption::ApcFileStoragePrefix,
RuntimeOption::ApcFileStorageChunkSize,
RuntimeOption::ApcFileStorageMaxSize);
}
}
virtual void moduleShutdown() {
if (RuntimeOption::ApcUseFileStorage) {
s_apc_file_storage.cleanup();
}
}
} s_apc_extension;
bool f_apc_store(CStrRef key, CVarRef var, int64_t ttl /* = 0 */,
int64_t cache_id /* = 0 */) {
if (!RuntimeOption::EnableApc) return false;
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
throw_invalid_argument("cache_id: %d", cache_id);
return false;
}
return s_apc_store[cache_id].store(key, var, ttl);
}
bool f_apc_add(CStrRef key, CVarRef var, int64_t ttl /* = 0 */,
int64_t cache_id /* = 0 */) {
if (!RuntimeOption::EnableApc) return false;
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
throw_invalid_argument("cache_id: %d", cache_id);
return false;
}
return s_apc_store[cache_id].store(key, var, ttl, false);
}
Variant f_apc_fetch(CVarRef key, VRefParam success /* = null */,
int64_t cache_id /* = 0 */) {
if (!RuntimeOption::EnableApc) return false;
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
throw_invalid_argument("cache_id: %d", cache_id);
return false;
}
Variant v;
if (key.is(KindOfArray)) {
bool tmp = false;
Array keys = key.toArray();
ArrayInit init(keys.size());
for (ArrayIter iter(keys); iter; ++iter) {
Variant k = iter.second();
if (!k.isString()) {
throw_invalid_argument("apc key: (not a string)");
return false;
}
String strKey = k.toString();
if (s_apc_store[cache_id].get(strKey, v)) {
tmp = true;
init.set(strKey, v, true);
}
}
success = tmp;
return init.create();
}
if (s_apc_store[cache_id].get(key.toString(), v)) {
success = true;
} else {
success = false;
v = false;
}
return v;
}
Variant f_apc_delete(CVarRef key, int64_t cache_id /* = 0 */) {
if (!RuntimeOption::EnableApc) return false;
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
throw_invalid_argument("cache_id: %d", cache_id);
return false;
}
if (key.is(KindOfArray)) {
Array keys = key.toArray();
ArrayInit init(keys.size());
for (ArrayIter iter(keys); iter; ++iter) {
Variant k = iter.second();
if (!k.isString()) {
raise_warning("apc key is not a string");
init.set(k);
} else if (!s_apc_store[cache_id].erase(k.toString())) {
init.set(k);
}
}
return init.create();
}
return s_apc_store[cache_id].erase(key.toString());
}
bool f_apc_clear_cache(int64_t cache_id /* = 0 */) {
if (!RuntimeOption::EnableApc) return false;
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
throw_invalid_argument("cache_id: %d", cache_id);
return false;
}
return s_apc_store[cache_id].clear();
}
Variant f_apc_inc(CStrRef key, int64_t step /* = 1 */,
VRefParam success /* = null */, int64_t cache_id /* = 0 */) {
if (!RuntimeOption::EnableApc) return false;
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
throw_invalid_argument("cache_id: %d", cache_id);
return false;
}
bool found = false;
int64_t newValue = s_apc_store[cache_id].inc(key, step, found);
success = found;
return newValue;
}
Variant f_apc_dec(CStrRef key, int64_t step /* = 1 */,
VRefParam success /* = null */, int64_t cache_id /* = 0 */) {
if (!RuntimeOption::EnableApc) return false;
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
throw_invalid_argument("cache_id: %d", cache_id);
return false;
}
bool found = false;
int64_t newValue = s_apc_store[cache_id].inc(key, -step, found);
success = found;
return newValue;
}
bool f_apc_cas(CStrRef key, int64_t old_cas, int64_t new_cas,
int64_t cache_id /* = 0 */) {
if (!RuntimeOption::EnableApc) return false;
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
throw_invalid_argument("cache_id: %d", cache_id);
return false;
}
return s_apc_store[cache_id].cas(key, old_cas, new_cas);
}
Variant f_apc_exists(CVarRef key, int64_t cache_id /* = 0 */) {
if (!RuntimeOption::EnableApc) return false;
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
throw_invalid_argument("cache_id: %d", cache_id);
return false;
}
if (key.is(KindOfArray)) {
Array keys = key.toArray();
ArrayInit init(keys.size());
for (ArrayIter iter(keys); iter; ++iter) {
Variant k = iter.second();
if (!k.isString()) {
throw_invalid_argument("apc key: (not a string)");
return false;
}
String strKey = k.toString();
if (s_apc_store[cache_id].exists(strKey)) {
init.set(strKey);
}
}
return init.create();
}
return s_apc_store[cache_id].exists(key.toString());
}
Variant f_apc_cache_info(int64_t cache_id /* = 0 */, bool limited /* = false */) {
return CREATE_MAP1("start_time", start_time());
}
Array f_apc_sma_info(bool limited /* = false */) {
return Array::Create();
}
bool f_apc_define_constants(CStrRef key, CStrRef constants,
bool case_sensitive /* = true */,
int64_t cache_id /* = 0 */) {
throw NotSupportedException(__func__, "dynamic coding");
}
bool f_apc_load_constants(CStrRef key, bool case_sensitive /* = true */,
int64_t cache_id /* = 0 */) {
throw NotSupportedException(__func__, "dynamic coding");
}
bool f_apc_compile_file(CStrRef filename, bool atomic /* = true */,
int64_t cache_id /* = 0 */) {
throw NotSupportedException(__func__, "dynamic coding");
}
Array f_apc_filehits() {
throw NotSupportedException(__func__, "feature not supported");
}
Variant f_apc_delete_file(CVarRef keys, int64_t cache_id /* = 0 */) {
throw NotSupportedException(__func__, "feature not supported");
}
Variant f_apc_bin_dump(int64_t cache_id /* = 0 */, CVarRef filter /* = null_variant */) {
throw NotSupportedException(__func__, "feature not supported");
}
bool f_apc_bin_load(CStrRef data, int64_t flags /* = 0 */, int64_t cache_id /* = 0 */) {
throw NotSupportedException(__func__, "feature not supported");
}
Variant f_apc_bin_dumpfile(int64_t cache_id, CVarRef filter,
CStrRef filename, int64_t flags /* = 0 */,
CObjRef context /* = uninit_null() */) {
throw NotSupportedException(__func__, "feature not supported");
}
bool f_apc_bin_loadfile(CStrRef filename, CObjRef context /* = uninit_null() */,
int64_t flags /* = 0 */, int64_t cache_id /* = 0 */) {
throw NotSupportedException(__func__, "feature not supported");
}
///////////////////////////////////////////////////////////////////////////////
// loading APC from archive files
typedef void(*PFUNC_APC_LOAD)();
// Structure to hold cache meta data
// Same definition in ext_apc.cpp
struct cache_info {
char *a_name;
bool use_const;
};
static Mutex dl_mutex;
static PFUNC_APC_LOAD apc_load_func(void *handle, const char *name) {
Lock lock(dl_mutex);
dlerror(); // clear errors
PFUNC_APC_LOAD p = (PFUNC_APC_LOAD)dlsym(handle, name);
const char *error = dlerror();
if (error || p == NULL) {
throw Exception("Unable to find %s in %s: %s", name,
RuntimeOption::ApcPrimeLibrary.c_str(),
error ? error : "(unknown error)");
}
return p;
}
DECLARE_BOOST_TYPES(ApcLoadJob);
class ApcLoadJob {
public:
ApcLoadJob(void *handle, int index) : m_handle(handle), m_index(index) {}
void *m_handle; int m_index;
};
class ApcLoadWorker {
public:
void onThreadEnter() {}
void doJob(ApcLoadJobPtr job) {
char func_name[128];
snprintf(func_name, sizeof(func_name), "_apc_load_%d", job->m_index);
apc_load_func(job->m_handle, func_name)();
}
void onThreadExit() {}
};
static size_t s_const_map_size = 0;
KEEP_SECTION
void apc_load(int thread) {
static void *handle = NULL;
if (handle ||
RuntimeOption::ApcPrimeLibrary.empty() ||
!RuntimeOption::EnableApc) {
static uint64_t keep_entry_points_around_under_lto;
if (++keep_entry_points_around_under_lto == UINT64_MAX) {
// this had better never happen...
// Fill out a cache_info to prevent g++ from optimizing out
// the calls to const_load_impl*
cache_info info;
info.a_name = "dummy";
info.use_const = true;
const_load();
const_load_impl(&info, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
const_load_impl_compressed(&info,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
apc_load_impl(&info, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
apc_load_impl_compressed(&info,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
}
return;
}
Timer timer(Timer::WallTime, "loading APC data");
handle = dlopen(RuntimeOption::ApcPrimeLibrary.c_str(), RTLD_LAZY);
if (!handle) {
throw Exception("Unable to open apc prime library %s: %s",
RuntimeOption::ApcPrimeLibrary.c_str(), dlerror());
}
if (thread <= 1) {
apc_load_func(handle, "_apc_load_all")();
} else {
int count = ((int(*)())apc_load_func(handle, "_apc_load_count"))();
ApcLoadJobPtrVec jobs;
jobs.reserve(count);
for (int i = 0; i < count; i++) {
jobs.push_back(ApcLoadJobPtr(new ApcLoadJob(handle, i)));
}
JobDispatcher<ApcLoadJob, ApcLoadWorker>(jobs, thread).run();
}
s_apc_store[0].primeDone();
if (RuntimeOption::EnableConstLoad) {
#ifdef USE_JEMALLOC
size_t allocated_before = 0;
size_t allocated_after = 0;
size_t sz = sizeof(size_t);
if (mallctl) {
uint64_t epoch = 1;
mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
mallctl("stats.allocated", &allocated_before, &sz, NULL, 0);
// Ignore the first result because it may be inaccurate due to internal
// allocation.
epoch = 1;
mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
mallctl("stats.allocated", &allocated_before, &sz, NULL, 0);
}
#endif
apc_load_func(handle, "_hphp_const_load_all")();
#ifdef USE_JEMALLOC
if (mallctl) {
uint64_t epoch = 1;
mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
sz = sizeof(size_t);
mallctl("stats.allocated", &allocated_after, &sz, NULL, 0);
s_const_map_size = allocated_after - allocated_before;
}
#endif
}
// We've copied all the data out, so close it out.
dlclose(handle);
}
size_t get_const_map_size() {
return s_const_map_size;
}
//define in ext_fb.cpp
extern void const_load_set(CStrRef key, CVarRef value);
///////////////////////////////////////////////////////////////////////////////
// Constant and APC priming with uncompressed data
// Note (qixin): this is going to be deprecated by the compressed version.
static int count_items(const char **p, int step) {
int count = 0;
for (const char **k = p; *k; k += step) {
count++;
}
return count;
}
KEEP_SECTION
void const_load_impl(struct cache_info *info,
const char **int_keys, long long *int_values,
const char **char_keys, char *char_values,
const char **strings, const char **objects,
const char **thrifts, const char **others) {
if (!RuntimeOption::EnableConstLoad || !info || !info->use_const) return;
{
int count = count_items(int_keys, 2);
if (count) {
const char **k = int_keys;
long long* v = int_values;
for (int i = 0; i < count; i++, k += 2) {
String key(*k, (int)(int64_t)*(k+1), CopyString);
int64_t value = *v++;
const_load_set(key, value);
}
}
}
{
int count = count_items(char_keys, 2);
if (count) {
const char **k = char_keys;
char *v = char_values;
for (int i = 0; i < count; i++, k += 2) {
String key(*k, (int)(int64_t)*(k+1), CopyString);
Variant value;
switch (*v++) {
case 0: value = false; break;
case 1: value = true; break;
case 2: value = uninit_null(); break;
default:
throw Exception("bad apc archive, unknown char type");
}
const_load_set(key, value);
}
}
}
{
int count = count_items(strings, 4);
if (count) {
const char **p = strings;
for (int i = 0; i < count; i++, p += 4) {
String key(*p, (int)(int64_t)*(p+1), CopyString);
String value(*(p+2), (int)(int64_t)*(p+3), CopyString);
const_load_set(key, value);
}
}
}
// unserialize_from_string object is extremely slow here;
// currently turned off: no objects in haste_maps.
if (false) {
int count = count_items(objects, 4);
if (count) {
const char **p = objects;
for (int i = 0; i < count; i++, p += 4) {
String key(*p, (int)(int64_t)*(p+1), CopyString);
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
const_load_set(key, unserialize_from_string(value));
}
}
}
{
int count = count_items(thrifts, 4);
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
const char **p = thrifts;
for (int i = 0; i < count; i++, p += 4) {
String key(*p, (int)(int64_t)*(p+1), CopyString);
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
Variant success;
Variant v = f_fb_thrift_unserialize(value, ref(success));
if (same(success, false)) {
throw Exception("bad apc archive, f_fb_thrift_unserialize failed");
}
const_load_set(key, v);
}
}
}
{//Would we use others[]?
int count = count_items(others, 4);
if (count) {
const char **p = others;
for (int i = 0; i < count; i++, p += 4) {
String key(*p, (int)(int64_t)*(p+1), CopyString);
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
Variant v = unserialize_from_string(value);
if (same(v, false)) {
throw Exception("bad apc archive, unserialize_from_string failed");
}
const_load_set(key, v);
}
}
}
}
KEEP_SECTION
void apc_load_impl(struct cache_info *info,
const char **int_keys, long long *int_values,
const char **char_keys, char *char_values,
const char **strings, const char **objects,
const char **thrifts, const char **others) {
if (!RuntimeOption::ForceConstLoadToAPC) {
if (RuntimeOption::EnableConstLoad && info && info->use_const) return;
}
SharedStore &s = s_apc_store[0];
{
int count = count_items(int_keys, 2);
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
const char **k = int_keys;
long long*v = int_values;
for (int i = 0; i < count; i++, k += 2) {
SharedStore::KeyValuePair &item = vars[i];
item.key = *k;
item.len = (int)(int64_t)*(k+1);
s.constructPrime(*v++, item);
}
s.prime(vars);
}
}
{
int count = count_items(char_keys, 2);
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
const char **k = char_keys;
char *v = char_values;
for (int i = 0; i < count; i++, k += 2) {
SharedStore::KeyValuePair &item = vars[i];
item.key = *k;
item.len = (int)(int64_t)*(k+1);
switch (*v++) {
case 0: s.constructPrime(false, item); break;
case 1: s.constructPrime(true , item); break;
case 2: s.constructPrime(uninit_null() , item); break;
default:
throw Exception("bad apc archive, unknown char type");
}
}
s.prime(vars);
}
}
{
int count = count_items(strings, 4);
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
const char **p = strings;
for (int i = 0; i < count; i++, p += 4) {
SharedStore::KeyValuePair &item = vars[i];
item.key = *p;
item.len = (int)(int64_t)*(p+1);
// Strings would be copied into APC anyway.
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
value.checkStatic();
s.constructPrime(value, item, false);
}
s.prime(vars);
}
}
{
int count = count_items(objects, 4);
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
const char **p = objects;
for (int i = 0; i < count; i++, p += 4) {
SharedStore::KeyValuePair &item = vars[i];
item.key = *p;
item.len = (int)(int64_t)*(p+1);
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
s.constructPrime(value, item, true);
}
s.prime(vars);
}
}
{
int count = count_items(thrifts, 4);
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
const char **p = thrifts;
for (int i = 0; i < count; i++, p += 4) {
SharedStore::KeyValuePair &item = vars[i];
item.key = *p;
item.len = (int)(int64_t)*(p+1);
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
Variant success;
Variant v = f_fb_thrift_unserialize(value, ref(success));
if (same(success, false)) {
throw Exception("bad apc archive, f_fb_thrift_unserialize failed");
}
s.constructPrime(v, item);
}
s.prime(vars);
}
}
{
int count = count_items(others, 4);
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
const char **p = others;
for (int i = 0; i < count; i++, p += 4) {
SharedStore::KeyValuePair &item = vars[i];
item.key = *p;
item.len = (int)(int64_t)*(p+1);
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
Variant v = unserialize_from_string(value);
if (same(v, false)) {
// we can't possibly get here if it was a boolean "false" that's
// supposed to be serialized as a char
throw Exception("bad apc archive, unserialize_from_string failed");
}
s.constructPrime(v, item);
}
s.prime(vars);
}
}
}
///////////////////////////////////////////////////////////////////////////////
// Constant and APC priming with compressed data
KEEP_SECTION
void const_load_impl_compressed
(struct cache_info *info,
int *int_lens, const char *int_keys, long long *int_values,
int *char_lens, const char *char_keys, char *char_values,
int *string_lens, const char *strings,
int *object_lens, const char *objects,
int *thrift_lens, const char *thrifts,
int *other_lens, const char *others) {
if (!RuntimeOption::EnableConstLoad || !info || !info->use_const) return;
{
int count = int_lens[0];
int len = int_lens[1];
if (count) {
char *keys = gzdecode(int_keys, len);
if (keys == NULL) throw Exception("bad compressed const archive.");
ScopedMem holder(keys);
const char *k = keys;
long long* v = int_values;
for (int i = 0; i < count; i++) {
String key(k, int_lens[i + 2], CopyString);
int64_t value = *v++;
const_load_set(key, value);
k += int_lens[i + 2] + 1;
}
assert((k - keys) == len);
}
}
{
int count = char_lens[0];
int len = char_lens[1];
if (count) {
char *keys = gzdecode(char_keys, len);
if (keys == NULL) throw Exception("bad compressed const archive.");
ScopedMem holder(keys);
const char *k = keys;
char *v = char_values;
for (int i = 0; i < count; i++) {
String key(k, char_lens[i + 2], CopyString);
Variant value;
switch (*v++) {
case 0: value = false; break;
case 1: value = true; break;
case 2: value = uninit_null(); break;
default:
throw Exception("bad const archive, unknown char type");
}
const_load_set(key, value);
k += char_lens[i + 2] + 1;
}
assert((k - keys) == len);
}
}
{
int count = string_lens[0] / 2;
int len = string_lens[1];
if (count) {
char *decoded = gzdecode(strings, len);
if (decoded == NULL) throw Exception("bad compressed const archive.");
ScopedMem holder(decoded);
const char *p = decoded;
for (int i = 0; i < count; i++) {
String key(p, string_lens[i + i + 2], CopyString);
p += string_lens[i + i + 2] + 1;
String value(p, string_lens[i + i + 3], CopyString);
const_load_set(key, value);
p += string_lens[i + i + 3] + 1;
}
assert((p - decoded) == len);
}
}
// unserialize_from_string object is extremely slow here;
// currently turned off: no objects in haste_maps.
if (false) {
int count = object_lens[0] / 2;
int len = object_lens[1];
if (count) {
char *decoded = gzdecode(objects, len);
if (decoded == NULL) throw Exception("bad compressed const archive.");
ScopedMem holder(decoded);
const char *p = decoded;
for (int i = 0; i < count; i++) {
String key(p, object_lens[i + i + 2], CopyString);
p += object_lens[i + i + 2] + 1;
String value(p, object_lens[i + i + 3], AttachLiteral);
const_load_set(key, unserialize_from_string(value));
p += object_lens[i + i + 3] + 1;
}
assert((p - decoded) == len);
}
}
{
int count = thrift_lens[0] / 2;
int len = thrift_lens[1];
if (count) {
char *decoded = gzdecode(thrifts, len);
if (decoded == NULL) throw Exception("bad compressed const archive.");
ScopedMem holder(decoded);
const char *p = decoded;
for (int i = 0; i < count; i++) {
String key(p, thrift_lens[i + i + 2], CopyString);
p += thrift_lens[i + i + 2] + 1;
String value(p, thrift_lens[i + i + 3], AttachLiteral);
Variant success;
Variant v = f_fb_thrift_unserialize(value, ref(success));
if (same(success, false)) {
throw Exception("bad apc archive, f_fb_thrift_unserialize failed");
}
const_load_set(key, v);
p += thrift_lens[i + i + 3] + 1;
}
assert((p - decoded) == len);
}
}
{//Would we use others[]?
int count = other_lens[0] / 2;
int len = other_lens[1];
if (count) {
char *decoded = gzdecode(others, len);
if (decoded == NULL) throw Exception("bad compressed const archive.");
ScopedMem holder(decoded);
const char *p = decoded;
for (int i = 0; i < count; i++) {
String key(p, other_lens[i + i + 2], CopyString);
p += other_lens[i + i + 2] + 1;
String value(p, other_lens[i + i + 3], AttachLiteral);
Variant v = unserialize_from_string(value);
if (same(v, false)) {
throw Exception("bad apc archive, unserialize_from_string failed");
}
const_load_set(key, v);
p += other_lens[i + i + 3] + 1;
}
assert((p - decoded) == len);
}
}
}
KEEP_SECTION
void apc_load_impl_compressed
(struct cache_info *info,
int *int_lens, const char *int_keys, long long *int_values,
int *char_lens, const char *char_keys, char *char_values,
int *string_lens, const char *strings,
int *object_lens, const char *objects,
int *thrift_lens, const char *thrifts,
int *other_lens, const char *others) {
if (!RuntimeOption::ForceConstLoadToAPC) {
if (RuntimeOption::EnableConstLoad && info && info->use_const) return;
}
SharedStore &s = s_apc_store[0];
{
int count = int_lens[0];
int len = int_lens[1];
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
char *keys = gzdecode(int_keys, len);
if (keys == NULL) throw Exception("bad compressed apc archive.");
ScopedMem holder(keys);
const char *k = keys;
long long* v = int_values;
for (int i = 0; i < count; i++) {
SharedStore::KeyValuePair &item = vars[i];
item.key = k;
item.len = int_lens[i + 2];
s.constructPrime(*v++, item);
k += int_lens[i + 2] + 1; // skip \0
}
s.prime(vars);
assert((k - keys) == len);
}
}
{
int count = char_lens[0];
int len = char_lens[1];
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
char *keys = gzdecode(char_keys, len);
if (keys == NULL) throw Exception("bad compressed apc archive.");
ScopedMem holder(keys);
const char *k = keys;
char *v = char_values;
for (int i = 0; i < count; i++) {
SharedStore::KeyValuePair &item = vars[i];
item.key = k;
item.len = char_lens[i + 2];
switch (*v++) {
case 0: s.constructPrime(false, item); break;
case 1: s.constructPrime(true , item); break;
case 2: s.constructPrime(uninit_null() , item); break;
default:
throw Exception("bad apc archive, unknown char type");
}
k += char_lens[i + 2] + 1; // skip \0
}
s.prime(vars);
assert((k - keys) == len);
}
}
{
int count = string_lens[0] / 2;
int len = string_lens[1];
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
char *decoded = gzdecode(strings, len);
if (decoded == NULL) throw Exception("bad compressed apc archive.");
ScopedMem holder(decoded);
const char *p = decoded;
for (int i = 0; i < count; i++) {
SharedStore::KeyValuePair &item = vars[i];
item.key = p;
item.len = string_lens[i + i + 2];
p += string_lens[i + i + 2] + 1; // skip \0
// Strings would be copied into APC anyway.
String value(p, string_lens[i + i + 3], AttachLiteral);
value.checkStatic();
s.constructPrime(value, item, false);
p += string_lens[i + i + 3] + 1; // skip \0
}
s.prime(vars);
assert((p - decoded) == len);
}
}
{
int count = object_lens[0] / 2;
int len = object_lens[1];
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
char *decoded = gzdecode(objects, len);
if (decoded == NULL) throw Exception("bad compressed APC archive.");
ScopedMem holder(decoded);
const char *p = decoded;
for (int i = 0; i < count; i++) {
SharedStore::KeyValuePair &item = vars[i];
item.key = p;
item.len = object_lens[i + i + 2];
p += object_lens[i + i + 2] + 1; // skip \0
String value(p, object_lens[i + i + 3], AttachLiteral);
s.constructPrime(value, item, true);
p += object_lens[i + i + 3] + 1; // skip \0
}
s.prime(vars);
assert((p - decoded) == len);
}
}
{
int count = thrift_lens[0] / 2;
int len = thrift_lens[1];
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
char *decoded = gzdecode(thrifts, len);
if (decoded == NULL) throw Exception("bad compressed apc archive.");
ScopedMem holder(decoded);
const char *p = decoded;
for (int i = 0; i < count; i++) {
SharedStore::KeyValuePair &item = vars[i];
item.key = p;
item.len = thrift_lens[i + i + 2];
p += thrift_lens[i + i + 2] + 1; // skip \0
String value(p, thrift_lens[i + i + 3], AttachLiteral);
Variant success;
Variant v = f_fb_thrift_unserialize(value, ref(success));
if (same(success, false)) {
throw Exception("bad apc archive, f_fb_thrift_unserialize failed");
}
s.constructPrime(v, item);
p += thrift_lens[i + i + 3] + 1; // skip \0
}
s.prime(vars);
assert((p - decoded) == len);
}
}
{
int count = other_lens[0] / 2;
int len = other_lens[1];
if (count) {
vector<SharedStore::KeyValuePair> vars(count);
char *decoded = gzdecode(others, len);
if (decoded == NULL) throw Exception("bad compressed apc archive.");
ScopedMem holder(decoded);
const char *p = decoded;
for (int i = 0; i < count; i++) {
SharedStore::KeyValuePair &item = vars[i];
item.key = p;
item.len = other_lens[i + i + 2];
p += other_lens[i + i + 2] + 1; // skip \0
String value(p, other_lens[i + i + 3], AttachLiteral);
Variant v = unserialize_from_string(value);
if (same(v, false)) {
// we can't possibly get here if it was a boolean "false" that's
// supposed to be serialized as a char
throw Exception("bad apc archive, unserialize_from_string failed");
}
s.constructPrime(v, item);
p += other_lens[i + i + 3] + 1; // skip \0
}
s.prime(vars);
assert((p - decoded) == len);
}
}
}
///////////////////////////////////////////////////////////////////////////////
static double my_time() {
struct timeval a;
double t;
gettimeofday(&a, NULL);
t = a.tv_sec + (a.tv_usec/1000000.00);
return t;
}
#define RFC1867_TRACKING_KEY_MAXLEN 63
#define RFC1867_NAME_MAXLEN 63
#define RFC1867_FILENAME_MAXLEN 127
int apc_rfc1867_progress(apc_rfc1867_data *rfc1867ApcData,
unsigned int event, void *event_data,
void **extra) {
switch (event) {
case MULTIPART_EVENT_START: {
multipart_event_start *data = (multipart_event_start *) event_data;
rfc1867ApcData->content_length = data->content_length;
rfc1867ApcData->tracking_key.clear();
rfc1867ApcData->name.clear();
rfc1867ApcData->cancel_upload = 0;
rfc1867ApcData->temp_filename = NULL;
rfc1867ApcData->start_time = my_time();
rfc1867ApcData->bytes_processed = 0;
rfc1867ApcData->prev_bytes_processed = 0;
rfc1867ApcData->rate = 0;
rfc1867ApcData->update_freq = RuntimeOption::Rfc1867Freq;
if (rfc1867ApcData->update_freq < 0) {
assert(false); // TODO: support percentage
// frequency is a percentage, not bytes
rfc1867ApcData->update_freq =
rfc1867ApcData->content_length * RuntimeOption::Rfc1867Freq / 100;
}
break;
}
case MULTIPART_EVENT_FORMDATA: {
multipart_event_formdata *data = (multipart_event_formdata *)event_data;
if (data->name &&
!strncasecmp(data->name, RuntimeOption::Rfc1867Name.c_str(),
RuntimeOption::Rfc1867Name.size()) &&
data->value && data->length &&
data->length < RFC1867_TRACKING_KEY_MAXLEN -
RuntimeOption::Rfc1867Prefix.size()) {
int len = RuntimeOption::Rfc1867Prefix.size();
if (len > RFC1867_TRACKING_KEY_MAXLEN) {
len = RFC1867_TRACKING_KEY_MAXLEN;
}
rfc1867ApcData->tracking_key =
string(RuntimeOption::Rfc1867Prefix.c_str(), len);
len = strlen(*data->value);
int rem = RFC1867_TRACKING_KEY_MAXLEN -
rfc1867ApcData->tracking_key.size();
if (len > rem) len = rem;
rfc1867ApcData->tracking_key +=
string(*data->value, len);
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
}
/* Facebook: Temporary fix for a bug in PHP's rfc1867 code,
fixed here for convenience:
http://cvs.php.net/viewvc.cgi/php-src/main/
rfc1867.c?r1=1.173.2.1.2.11&r2=1.173.2.1.2.12 */
(*data->newlength) = data->length;
break;
}
case MULTIPART_EVENT_FILE_START:
if (!rfc1867ApcData->tracking_key.empty()) {
multipart_event_file_start *data =
(multipart_event_file_start *)event_data;
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
int len = strlen(*data->filename);
if (len > RFC1867_FILENAME_MAXLEN) len = RFC1867_FILENAME_MAXLEN;
rfc1867ApcData->filename = string(*data->filename, len);
rfc1867ApcData->temp_filename = NULL;
len = strlen(data->name);
if (len > RFC1867_NAME_MAXLEN) len = RFC1867_NAME_MAXLEN;
rfc1867ApcData->name = string(data->name, len);
Array track;
track.set("total", rfc1867ApcData->content_length);
track.set("current", rfc1867ApcData->bytes_processed);
track.set("filename", rfc1867ApcData->filename);
track.set("name", rfc1867ApcData->name);
track.set("done", 0);
track.set("start_time", rfc1867ApcData->start_time);
f_apc_store(rfc1867ApcData->tracking_key, track, 3600);
}
break;
case MULTIPART_EVENT_FILE_DATA:
if (!rfc1867ApcData->tracking_key.empty()) {
multipart_event_file_data *data =
(multipart_event_file_data *) event_data;
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
if (rfc1867ApcData->bytes_processed -
rfc1867ApcData->prev_bytes_processed >
rfc1867ApcData->update_freq) {
Variant v;
if (s_apc_store[0].get(rfc1867ApcData->tracking_key, v)) {
if (v.is(KindOfArray)) {
Array track;
track.set("total", rfc1867ApcData->content_length);
track.set("current", rfc1867ApcData->bytes_processed);
track.set("filename", rfc1867ApcData->filename);
track.set("name", rfc1867ApcData->name);
track.set("done", 0);
track.set("start_time", rfc1867ApcData->start_time);
f_apc_store(rfc1867ApcData->tracking_key, track, 3600);
}
rfc1867ApcData->prev_bytes_processed =
rfc1867ApcData->bytes_processed;
}
}
}
break;
case MULTIPART_EVENT_FILE_END:
if (!rfc1867ApcData->tracking_key.empty()) {
multipart_event_file_end *data =
(multipart_event_file_end *)event_data;
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
rfc1867ApcData->cancel_upload = data->cancel_upload;
rfc1867ApcData->temp_filename = data->temp_filename;
Array track;
track.set("total", rfc1867ApcData->content_length);
track.set("current", rfc1867ApcData->bytes_processed);
track.set("filename", rfc1867ApcData->filename);
track.set("name", rfc1867ApcData->name);
track.set("temp_filename", rfc1867ApcData->temp_filename, CopyString);
track.set("cancel_upload", rfc1867ApcData->cancel_upload);
track.set("done", 0);
track.set("start_time", rfc1867ApcData->start_time);
f_apc_store(rfc1867ApcData->tracking_key, track, 3600);
}
break;
case MULTIPART_EVENT_END:
if (!rfc1867ApcData->tracking_key.empty()) {
double now = my_time();
multipart_event_end *data = (multipart_event_end *)event_data;
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
if(now>rfc1867ApcData->start_time) {
rfc1867ApcData->rate =
8.0*rfc1867ApcData->bytes_processed/(now-rfc1867ApcData->start_time);
} else {
rfc1867ApcData->rate =
8.0*rfc1867ApcData->bytes_processed; /* Too quick */
Array track;
track.set("total", rfc1867ApcData->content_length);
track.set("current", rfc1867ApcData->bytes_processed);
track.set("rate", rfc1867ApcData->rate);
track.set("filename", rfc1867ApcData->filename);
track.set("name", rfc1867ApcData->name);
track.set("cancel_upload", rfc1867ApcData->cancel_upload);
track.set("done", 1);
track.set("start_time", rfc1867ApcData->start_time);
f_apc_store(rfc1867ApcData->tracking_key, track, 3600);
}
}
break;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// apc serialization
String apc_serialize(CVarRef value) {
VariableSerializer::Type sType =
RuntimeOption::EnableApcSerialize ?
VariableSerializer::APCSerialize :
VariableSerializer::Serialize;
VariableSerializer vs(sType);
return vs.serialize(value, true);
}
Variant apc_unserialize(CStrRef str) {
VariableUnserializer::Type sType =
RuntimeOption::EnableApcSerialize ?
VariableUnserializer::APCSerialize :
VariableUnserializer::Serialize;
return unserialize_ex(str, sType);
}
void reserialize(VariableUnserializer *uns, StringBuffer &buf) {
char type = uns->readChar();
char sep = uns->readChar();
if (type == 'N') {
buf.append(type);
buf.append(sep);
return;
}
switch (type) {
case 'r':
case 'R':
case 'b':
case 'i':
case 'd':
{
buf.append(type);
buf.append(sep);
while (uns->peek() != ';') {
char ch;
ch = uns->readChar();
buf.append(ch);
}
}
break;
case 'S':
case 'A':
{
// shouldn't happen, but keep the code here anyway.
buf.append(type);
buf.append(sep);
char pointer[8];
uns->read(pointer, 8);
buf.append(pointer, 8);
}
break;
case 's':
{
String v;
v.unserialize(uns);
assert(!v.isNull());
if (v->isStatic()) {
union {
char pointer[8];
StringData *sd;
} u;
u.sd = v.get();
buf.append("S:");
buf.append(u.pointer, 8);
buf.append(';');
} else {
buf.append("s:");
buf.append(v.size());
buf.append(":\"");
buf.append(v.data(), v.size());
buf.append("\";");
}
sep = uns->readChar();
return;
}
break;
case 'a':
{
buf.append("a:");
int64_t size = uns->readInt();
char sep2 = uns->readChar();
buf.append(size);
buf.append(sep2);
sep2 = uns->readChar();
buf.append(sep2);
for (int64_t i = 0; i < size; i++) {
reserialize(uns, buf); // key
reserialize(uns, buf); // value
}
sep2 = uns->readChar(); // '}'
buf.append(sep2);
return;
}
break;
case 'o':
case 'O':
{
buf.append(type);
buf.append(sep);
String clsName;
clsName.unserialize(uns);
buf.append(clsName.size());
buf.append(":\"");
buf.append(clsName.data(), clsName.size());
buf.append("\":");
uns->readChar();
int64_t size = uns->readInt();
char sep2 = uns->readChar();
buf.append(size);
buf.append(sep2);
sep2 = uns->readChar(); // '{'
buf.append(sep2);
for (int64_t i = 0; i < size; i++) {
reserialize(uns, buf); // property name
reserialize(uns, buf); // property value
}
sep2 = uns->readChar(); // '}'
buf.append(sep2);
return;
}
break;
case 'C':
{
buf.append(type);
buf.append(sep);
String clsName;
clsName.unserialize(uns);
buf.append(clsName.size());
buf.append(":\"");
buf.append(clsName.data(), clsName.size());
buf.append("\":");
sep = uns->readChar(); // ':'
String serialized;
serialized.unserialize(uns, '{', '}');
buf.append(serialized.size());
buf.append(":{");
buf.append(serialized.data(), serialized.size());
buf.append('}');
return;
}
break;
default:
throw Exception("Unknown type '%c'", type);
}
sep = uns->readChar(); // the last ';'
buf.append(sep);
}
String apc_reserialize(CStrRef str) {
if (str.empty() ||
!RuntimeOption::EnableApcSerialize) return str;
VariableUnserializer uns(str.data(), str.size(),
VariableUnserializer::APCSerialize);
StringBuffer buf;
reserialize(&uns, buf);
return buf.detach();
}
///////////////////////////////////////////////////////////////////////////////
// debugging support
bool apc_dump(const char *filename, bool keyOnly, int waitSeconds) {
const int CACHE_ID = 0; /* 0 is used as default for apc */
std::ofstream out(filename);
if (out.fail()) {
return false;
}
s_apc_store[CACHE_ID].dump(out, keyOnly, waitSeconds);
out.close();
return true;
}
///////////////////////////////////////////////////////////////////////////////
}