Exemple #1
0
// see Win32 API GetDiskFreeSpaceEx
int DOKAN_CALLBACK OnGetDiskFreeSpace(
	PULONGLONG FreeBytesAvailable,
	PULONGLONG TotalNumberOfBytes,
	PULONGLONG TotalNumberOfFreeBytes,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnGetDiskFreeSpace]\n" );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );
		NewDokanDiskFreeSpace( env );

		jobject jdiskFreeSpace = env->CallObjectMethod( gOperations, onGetDiskFreeSpaceID, jdokanFileInfo );
		result = GetOperationResult( env );

		if ( FreeBytesAvailable )
			*FreeBytesAvailable = env->GetLongField( jdiskFreeSpace, freeBytesAvailableID );
		if ( TotalNumberOfBytes )
			*TotalNumberOfBytes = env->GetLongField( jdiskFreeSpace, totalNumberOfBytesID );
		if ( TotalNumberOfFreeBytes )
			*TotalNumberOfFreeBytes = env->GetLongField( jdiskFreeSpace, totalNumberOfFreeBytesID );
	} catch ( const char* msg ) {
		LOG( L"[OnGetDiskFreeSpace] %s\n", msg );
	}

	release_env( env );
	return result;

}
Exemple #2
0
int DOKAN_CALLBACK OnGetFileSecurity(
	LPCWSTR FileName,
	PSECURITY_INFORMATION RequestedSecurityInformation,
	PSECURITY_DESCRIPTOR SecurityDescriptorBuffer,
	ULONG BufferLength,
	PULONG BufferLengthNeeded,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[GetFileSecurity]\n" );
	JNIEnv* env = get_env();

	int result = -ERROR_GEN_FAILURE;
	try {
		//jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );
		LOG( L"GetFileSecurity FileName: %s \n", FileName );
		LOG( L"GetFileSecurity RequestedSecurityInformation: %d\n", RequestedSecurityInformation );
		LOG( L"GetFileSecurity SecurityDescriptorBuffer: %d\n", SecurityDescriptorBuffer );
		LOG( L"GetFileSecurity BufferLength: %d\n", BufferLength );
		LOG( L"GetFileSecurity BufferLengthNeeded: %d\n", BufferLengthNeeded );
	} catch ( const char* msg ) {
		LOGA( "[GetFileSecurity] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #3
0
int DOKAN_CALLBACK OnReadFile(
	LPCWSTR  FileName,
	LPVOID   Buffer,
	DWORD    NumberOfBytesToRead,
	LPDWORD  NumberOfBytesRead,
	LONGLONG Offset,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnReadFile] FileName = %s, Offset = %lld, NumberOfBytesToRead = %d\n",
		  FileName, Offset, NumberOfBytesToRead );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jfileName = ToJavaString( env, FileName );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		DWORD readed = env->CallIntMethod( gOperations, onReadFileID,
													  jfileName,
													  env->NewDirectByteBuffer( Buffer, NumberOfBytesToRead ),
													  Offset,
													  jdokanFileInfo );
		if ( NumberOfBytesRead )
			*NumberOfBytesRead = readed;
		result = GetOperationResult( env );
		if ( result != 0 ) {
			LOGA( "[OnReadFile] result = %d\n", result );
		}
	} catch ( const char* msg ) {
		LOGA( "[OnReadFile] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #4
0
int DOKAN_CALLBACK OnMoveFile(
	LPCWSTR ExistingFileName,
	LPCWSTR NewFileName,
	BOOL	ReplaceExisiting,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnMoveFile] ExistingFileName = %s\n", ExistingFileName );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jExistingFileName = ToJavaString( env, ExistingFileName );
		jstring jNewFileName = ToJavaString( env, NewFileName );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		env->CallVoidMethod( gOperations, onMoveFileID,
									jExistingFileName, jNewFileName, ReplaceExisiting, jdokanFileInfo );
		result = GetOperationResult( env );
	} catch ( const char* msg ) {
		LOGA( "[OnMoveFile] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #5
0
//You should implement either FindFires or FindFilesWithPattern
int DOKAN_CALLBACK OnFindFilesWithPattern(
	LPCWSTR			PathName,
	LPCWSTR			SearchPattern,
	PFillFindData	pFillFindData,		// call this function with PWIN32_FIND_DATAW
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnFindFilesWithPattern] PathName = %s\n", PathName );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jpathName = ToJavaString( env, PathName );
		jstring jsearchPattern = ToJavaString( env, SearchPattern );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		jobjectArray ary = ( jobjectArray )env->CallObjectMethod( gOperations,
								 onFindFilesWithPatternID,
								 jpathName, jsearchPattern, jdokanFileInfo );
		result = GetOperationResult( env );

		if ( result == 0 && ary != NULL && pFillFindData != NULL ) {
			for ( int i = 0; i < env->GetArrayLength( ary ); i++ ) {
				WIN32_FIND_DATAW win32FindData;
				ToWin32FindData( env, env->GetObjectArrayElement( ary, i ), &win32FindData );
				pFillFindData( &win32FindData, DokanFileInfo );
			}
		}
	} catch ( const char* msg ) {
		LOGA( "[OnFindFilesWithPattern] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #6
0
int DOKAN_CALLBACK OnOpenDirectory(
	LPCWSTR				FileName,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnOpenDirectory] FileName = %s\n", FileName );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jfileName = ToJavaString( env, FileName );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		jlong handle = env->CallLongMethod( gOperations, onOpenDirectoryID,
														jfileName, jdokanFileInfo );
		result = GetOperationResult( env );

		if ( result == 0 ) {
			DokanFileInfo->Context = handle;
		}
	} catch ( const char* msg ) {
		LOGA( "[OnOpenDirectory] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #7
0
int DOKAN_CALLBACK OnCreateFile(
	LPCWSTR		FileName,
	DWORD		DesiredAccess,
	DWORD		ShareMode,
	DWORD		CreationDisposition,
	DWORD		FlagsAndAttributes,
	//HANDLE,       // TemplateFile
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnCreateFile] FileName = %s\n", FileName );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jfileName = ToJavaString( env, FileName );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		jlong handle = env->CallLongMethod( gOperations, onCreateFileID,
														jfileName, DesiredAccess, ShareMode, CreationDisposition,
														FlagsAndAttributes, jdokanFileInfo );
		result = GetOperationResult( env );

		if ( result == 0 ) {
			DokanFileInfo->Context = handle;
		}
		LOG( L"[OnCreateFile] result = %d, handle = %d\n", result, handle );
	} catch ( const char* msg ) {
		LOGA( "[OnCreateFile] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #8
0
int DOKAN_CALLBACK OnSetFileTime(
	LPCWSTR		FileName,
	CONST FILETIME* CreationTime,
	CONST FILETIME* LastAccessTime,
	CONST FILETIME* LastWriteTime,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnSetFileTime] FileName = %s\n", FileName );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jfileName = ToJavaString( env, FileName );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		env->CallVoidMethod( gOperations, onSetFileTimeID,
									jfileName, FileTime2LongLong( CreationTime ),
									FileTime2LongLong( LastAccessTime ), FileTime2LongLong( LastWriteTime ),
									jdokanFileInfo );
		result = GetOperationResult( env );
	} catch ( const char* msg ) {
		LOGA( "[OnSetFileTime] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #9
0
/*
 * Copy to statvfs struct from statvfs object.
 */
void copyStatvfsIn(struct statvfs *buf, jobject statvfsObj)
{
    JNIEnv *env = get_env();

    buf->f_bsize = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_bsize);
    buf->f_frsize = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_frsize);
    buf->f_blocks = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_blocks);
    buf->f_bfree = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_bfree);
    buf->f_bavail = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_bavail);
    buf->f_files = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_files);
    buf->f_ffree = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_ffree);
    buf->f_favail = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_favail);
    buf->f_fsid = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_fsid);
    buf->f_flag = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_flag);
    buf->f_namemax = (*env)->GetLongField(env, statvfsObj, fuseSt.statvfsID.f_namemax);

    release_env(env);
}
Exemple #10
0
/*
 * Copy to statvfs object from statvfs struct.
 */
void copyStatvfsOut(jobject statvfsObj, struct statvfs *buf)
{
    JNIEnv *env = get_env();

    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_bsize, buf->f_bsize);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_frsize, buf->f_frsize);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_blocks, buf->f_blocks);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_bfree, buf->f_bfree);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_bavail, buf->f_bavail);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_files, buf->f_files);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_ffree, buf->f_ffree);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_favail, buf->f_favail);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_fsid, buf->f_fsid);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_flag, buf->f_flag);
    (*env)->SetLongField(env, statvfsObj, fuseSt.statvfsID.f_namemax, buf->f_namemax);

    release_env(env);
}
Exemple #11
0
int DOKAN_CALLBACK OnWriteFile(
	LPCWSTR  FileName,
	LPCVOID  Buffer,
	DWORD    NumberOfBytesToWrite,
	LPDWORD  NumberOfBytesWritten,
	LONGLONG Offset,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnWriteFile] FileName = %s, Offset = %lld, NumberOfBytesToWrite = %d\n",
		  FileName, Offset, NumberOfBytesToWrite );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jfileName = ToJavaString( env, FileName );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		// Some one please modify here for the faster way !!
		LPVOID tmpBuffer = malloc( NumberOfBytesToWrite );
		if ( tmpBuffer == NULL )
			throw "Cannot allocate memory";
		CopyMemory( tmpBuffer, Buffer, NumberOfBytesToWrite );
		DWORD written = env->CallIntMethod( gOperations, onWriteFileID,
														jfileName,
														env->NewDirectByteBuffer( tmpBuffer, NumberOfBytesToWrite ),
														Offset,
														jdokanFileInfo );
		free( tmpBuffer );

		if ( NumberOfBytesWritten )
			*NumberOfBytesWritten = written;
		result = GetOperationResult( env );
		if ( result != 0 ) {
			LOGA( "[OnWriteFile] ERROR result = %d\n", result );
		} else {
			LOGA( "[OnWriteFile] written = %d\n", written );
		}
	} catch ( const char* msg ) {
		LOGA( "[OnWriteFile] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #12
0
int DOKAN_CALLBACK OnUnmount(
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnUnmount]\n" );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		env->CallVoidMethod( gOperations, onUnmountID, jdokanFileInfo );
		result = GetOperationResult( env );
	} catch ( const char* msg ) {
		LOGA( "[OnUnmount] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #13
0
int DOKAN_CALLBACK OnSetFileSecurity(
	LPCWSTR FileName,
	PSECURITY_INFORMATION RequestedSecurityInformation,
	PSECURITY_DESCRIPTOR SecurityDescriptorBuffer,
	ULONG BufferLength,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[SetFileSecurity]\n" );
	JNIEnv* env = get_env();

	int result = -ERROR_GEN_FAILURE;
	try {
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );
		LOG( L"SetFileSecurity has ben called!" );
	} catch ( const char* msg ) {
		LOGA( "[SetFileSecurity] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #14
0
int DOKAN_CALLBACK OnGetFileInformation(
	LPCWSTR          FileName,
	LPBY_HANDLE_FILE_INFORMATION ByHandleFileInfo,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnGetFileInformation] FileName = %s\n", FileName );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jfileName = ToJavaString( env, FileName );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		jobject jobj = env->CallObjectMethod( gOperations, onGetFileInformationID,
														  jfileName, jdokanFileInfo );
		result = GetOperationResult( env );

		if ( result == 0 ) {
			ToByHandleFileInfo( env, jobj, ByHandleFileInfo );
			LOGA( "[OnGetFileInformation] %d %d %d\n",
					ByHandleFileInfo->dwFileAttributes,
					ByHandleFileInfo->nFileSizeHigh,
					ByHandleFileInfo->nFileSizeLow );
			LOGA( "[OnGetFileInformation] CreationTime: %d %d\n",
					ByHandleFileInfo->ftCreationTime.dwHighDateTime,
					ByHandleFileInfo->ftCreationTime.dwLowDateTime );
			LOGA( "[OnGetFileInformation] LastAccess: %d %d\n",
					ByHandleFileInfo->ftLastAccessTime.dwHighDateTime,
					ByHandleFileInfo->ftLastAccessTime.dwLowDateTime );
			LOGA( "[OnGetFileInformation] LastWrite: %d %d\n",
					ByHandleFileInfo->ftLastWriteTime.dwHighDateTime,
					ByHandleFileInfo->ftLastWriteTime.dwLowDateTime );
		}
	} catch ( const char* msg ) {
		LOGA( "[OnGetFileInformation] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #15
0
// see Win32 API GetVolumeInformation
int DOKAN_CALLBACK OnGetVolumeInformation(
	LPWSTR		VolumeNameBuffer,
	DWORD		VolumeNameSize,
	LPDWORD		VolumeSerialNumber,
	LPDWORD		MaximumComponentLength,
	LPDWORD		FileSystemFlags,
	LPWSTR		FileSystemNameBuffer,
	DWORD		FileSystemNameSize,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnGetVolumeInformation]\n" );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jvolumeName = ToJavaString( env, L"dokan" );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		jobject jvolumeInfo= env->CallObjectMethod( gOperations, onGetVolumeInformationID, jvolumeName, jdokanFileInfo );
		result = GetOperationResult( env );

		if ( VolumeSerialNumber )
			*VolumeSerialNumber = env->GetIntField( jvolumeInfo, volumeSerialNumberID );
		if ( MaximumComponentLength )
			*MaximumComponentLength = env->GetIntField( jvolumeInfo, maximumComponentLengthID );
		if ( FileSystemFlags )
			*FileSystemFlags = env->GetIntField( jvolumeInfo, fileSystemFlagsID );

		// VolumeName, FileSystemName
		CopyStringField( env, jvolumeInfo, volumeNameID, VolumeNameBuffer, VolumeNameSize );
		CopyStringField( env, jvolumeInfo, fileSystemNameID, FileSystemNameBuffer, FileSystemNameSize );
	} catch ( const char* msg ) {
		LOGA( "[OnGetVolumeInformation] %s\n", msg );
	}

	release_env( env );
	return result;

}
Exemple #16
0
int DOKAN_CALLBACK OnFlushFileBuffers(
	LPCWSTR FileName,
	PDOKAN_FILE_INFO DokanFileInfo ) {
	LOG( L"[OnFlushFileBuffers] FileName = %s\n", FileName );
	JNIEnv* env = get_env();
	//jvm->AttachCurrentThread((void **)&env, NULL);

	int result = -ERROR_GEN_FAILURE;
	try {
		jstring jfileName = ToJavaString( env, FileName );
		jobject jdokanFileInfo = ToDokanFileInfoJavaObject( env, DokanFileInfo );

		env->CallVoidMethod( gOperations, onFlushFileBuffersID,
									jfileName, jdokanFileInfo );
		result = GetOperationResult( env );
	} catch ( const char* msg ) {
		LOGA( "[OnFlushFileBuffers] %s\n", msg );
	}

	release_env( env );
	return result;
}
Exemple #17
0
/*
 * Initilize the statvfs class.
 */
void init_statvfsClass()
{
    JNIEnv *env = get_env();

    jclass _statvfsClass = (*env)->FindClass(env, "javafuse/Statvfs");
    fuseSt.statvfsClass = (*env)->NewGlobalRef(env, _statvfsClass);
    assert(fuseSt.statvfsClass != NULL);

    fuseSt.statvfsID.Ctor = (*env)->GetMethodID(env, fuseSt.statvfsClass, "<init>", "()V");
    assert(fuseSt.statvfsID.Ctor != NULL);

    fuseSt.statvfsID.finalize = (*env)->GetMethodID(env, fuseSt.statvfsClass, "finalize", "()V");
    assert(fuseSt.statvfsID.finalize != NULL);

    fuseSt.statvfsID.f_bsize = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_bsize", "J");
    assert(fuseSt.statvfsID.f_bsize != NULL);
    fuseSt.statvfsID.f_frsize = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_frsize", "J");
    assert(fuseSt.statvfsID.f_frsize != NULL);
    fuseSt.statvfsID.f_blocks = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_blocks", "J");
    assert(fuseSt.statvfsID.f_blocks != NULL);
    fuseSt.statvfsID.f_bfree = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_bfree", "J");
    assert(fuseSt.statvfsID.f_bfree != NULL);
    fuseSt.statvfsID.f_bavail = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_bavail", "J");
    assert(fuseSt.statvfsID.f_bavail != NULL);
    fuseSt.statvfsID.f_files = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_files", "J");
    assert(fuseSt.statvfsID.f_files != NULL);
    fuseSt.statvfsID.f_ffree = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_ffree", "J");
    assert(fuseSt.statvfsID.f_ffree != NULL);
    fuseSt.statvfsID.f_favail = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_favail", "J");
    assert(fuseSt.statvfsID.f_favail != NULL);
    fuseSt.statvfsID.f_fsid = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_fsid", "J");
    assert(fuseSt.statvfsID.f_fsid != NULL);
    fuseSt.statvfsID.f_flag = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_flag", "J");
    assert(fuseSt.statvfsID.f_flag != NULL);
    fuseSt.statvfsID.f_namemax = (*env)->GetFieldID(env, fuseSt.statvfsClass, "f_namemax", "J");
    assert(fuseSt.statvfsID.f_namemax != NULL);

    release_env(env);
}
Exemple #18
0
static lisp_obj *apply(lisp_expr_application *app, lisp_env *env, lisp_err *err)
{
    lisp_obj *callable = FORCE_VALUE(app->proc, env, err);
    if (! callable){
        return NULL;
    }

    lisp_obj *res = NIL;

    /* Internal procedure */
    if (callable->type == PROC){
        /* Eval args */
        lisp_obj **args = calloc(app->nparams, sizeof(lisp_obj*));
        for (size_t i=0; i<app->nparams; i++){
            lisp_obj *arg = FORCE_VALUE(app->params[i], env, err);
            if (! arg){
                for (size_t j=0; j<i; j++){
                    release(args[j]);
                }
                free(args);
                return NULL;
            }
            args[i] = arg;
        }

        /* Eval internal */
        res = callable->value.p(app->nparams, args);
        
        /* Free args */
        for (size_t i=0; i<app->nparams; i++){
            release(args[i]);
        }
        free(args);
    }

    /* Lisp func */
    else if (callable->type == LAMBDA){
        lisp_lambda *lambda = &(callable->value.l);
        lisp_expr_lambda *lambda_expr = &(lambda->declaration->value.mklambda);

        /* Check arity */
        if (app->nparams != lambda_expr->nparams){
            raise_error(err, WRONG_ARITY, "Arity error ! Expected %d params, got %d",
                lambda_expr->nparams, app->nparams);
            return NULL;
        }

        /* Extend env */
        lisp_env *locals = create_env(lambda->context);
        for (size_t i=0; i<lambda_expr->nparams; i++){
            lisp_obj *param = eval_expression(app->params[i], env, err);
            if (! param){
                release_env(locals);
                return NULL;
            }
            DEBUG("Extend env with %s", lambda_expr->param_names[i]);
            release(set_env(locals, lambda_expr->param_names[i], param));
        }

        if (enable_debug){
            printf("\033[1mCALL\033[0m ");
            dump_expr(lambda_expr->body);
            printf(" with env\n");
            dump_env(locals);
        }

        /* Wrap in thunk for trampoline */
        res = make_thunk(lambda_expr->body, locals);
        release_env(locals);
    }
    else {
        lisp_print(callable);
        raise_error(err, NOT_CALLABLE, "CANNOT CALL obj %p", callable);
        return NULL;
    }

    release(callable);
    return res;
}