├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── src ├── CMakeLists.txt ├── lib │ ├── debug.c │ ├── debug.h │ ├── fuse_api │ │ ├── chmod.c │ │ ├── chown.c │ │ ├── create.c │ │ ├── fuse_api.h │ │ ├── getattr.c │ │ ├── link.c │ │ ├── mkdir.c │ │ ├── open.c │ │ ├── read.c │ │ ├── readdir.c │ │ ├── readlink.c │ │ ├── rename.c │ │ ├── rmdir.c │ │ ├── symlink.c │ │ ├── truncate.c │ │ ├── unlink.c │ │ └── write.c │ ├── fuse_functions.def │ ├── generators.c │ ├── generators.def │ ├── generators.h │ ├── httpfs.c │ ├── httpfs.h │ ├── net.c │ ├── net.h │ ├── statuses.def │ ├── templates │ │ └── template.php │ └── version.h └── main.c └── test └── php.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /build-*/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Andrea Cardaci, Emilio Pinna 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | release debug: build 2 | 3 | release: BUILD_DIR=./build-release/ 4 | release: BUILD_TYPE=Release 5 | 6 | debug: BUILD_DIR=./build-debug/ 7 | debug: BUILD_TYPE=Debug 8 | 9 | build: 10 | rm -fr $(BUILD_DIR) 11 | mkdir -p $(BUILD_DIR) 12 | cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) ../src/ && make 13 | 14 | install: 15 | cd ./build-release/ && make install 16 | 17 | uninstall: 18 | cd ./build-release/ && xargs rm < install_manifest.txt 19 | 20 | clean: 21 | rm -fr ./build-release/ ./build-debug/ 22 | 23 | .PHONY: build build-release build-debug install uninstall clean 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | httpfs - Remote FUSE filesystem via server-side script 2 | ====================================================== 3 | 4 | httpfs is a FUSE-based filesystem that uses HTTP messages to mount a remote 5 | directory on a local machine, relying on a generated server-side script 6 | (e.g. PHP) installed on the server. 7 | 8 | Setup 9 | ----- 10 | 11 | 1. Add the current user to the `fuse` group as described in the **Prepare the 12 | environment** paragraph. 13 | 14 | 2. Install the dependencies. For Debian based distros just run: 15 | 16 | sudo apt-get install fuse libfuse-dev libcurl4-openssl-dev cmake 17 | 18 | 3. Install: 19 | 20 | make 21 | sudo make install 22 | 23 | 4. If needed, uninstall: 24 | 25 | sudo make uninstall 26 | 27 | Sample usage 28 | ------------ 29 | 30 | 1. Generate a PHP script: 31 | 32 | httpfs generate php > httpfs.php 33 | 34 | 2. Place the generated script in an accessible location inside the document 35 | root of your web server. 36 | 37 | 3. Mount the remote filesystem: 38 | 39 | mkdir /tmp/httpfs/ 40 | httpfs mount http://target.com/httpfs.php /tmp/httpfs/ /home/john/ 41 | 42 | 4. Now the remote `/home/john/` is mounted in `/tmp/httpfs/`, head there to 43 | browse the remote files. 44 | 45 | 5. Unmount the filesystem: 46 | 47 | fusermount -u /tmp/httpfs/ 48 | 49 | Prepare the environment 50 | ----------------------- 51 | 52 | Make sure the current user is in the `fuse` group, this preliminary step is 53 | mandatory to use any FUSE filesystem. You can list the groups you belong to with 54 | `groups`, if that includes `fuse` you're done, otherwise: 55 | 56 | sudo adduser john fuse 57 | 58 | Then log out and back in or start a new shell with: 59 | 60 | newgrp fuse 61 | 62 | to inform the system about the changes. 63 | 64 | Resources 65 | --------- 66 | 67 | FUSE - http://fuse.sourceforge.net 68 | 69 | Authors 70 | ------- 71 | 72 | Andrea Cardaci - http://cyrus-and.github.com 73 | 74 | Emilio Pinna - http://disse.cting.org 75 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project( httpfs ) 2 | 3 | # CMake setup 4 | cmake_minimum_required( VERSION 2.8 ) 5 | find_package( PkgConfig ) 6 | 7 | # sources 8 | file( GLOB fuse_api lib/fuse_api/*.c ) 9 | file( GLOB lib_src lib/*.c ) 10 | file( GLOB templates RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/lib/templates/ lib/templates/* ) 11 | 12 | # process templates 13 | set( templates_h "" ) 14 | foreach( template ${templates} ) 15 | set( template_h ${template}.h ) 16 | list( APPEND templates_h ${template_h} ) 17 | add_custom_command( 18 | OUTPUT ${template_h} 19 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/lib/templates/${template} ${template} 20 | COMMAND xxd -i ${template} ${template_h} 21 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/lib/templates/${template} 22 | ) 23 | endforeach( template ) 24 | include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) 25 | 26 | # external libraries 27 | find_package( CURL ) 28 | pkg_check_modules( FUSE REQUIRED fuse ) 29 | add_definitions( ${FUSE_CFLAGS} -DFUSE_USE_VERSION=29 ) 30 | 31 | # httpfs library 32 | add_library( httpfs STATIC ${lib_src} ${fuse_api} ${templates_h} ) 33 | 34 | # compilation 35 | include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/lib/ ) 36 | add_executable( httpfs-bin main.c ) 37 | set_target_properties( httpfs-bin PROPERTIES OUTPUT_NAME httpfs) 38 | add_definitions( -Wall ) 39 | target_link_libraries( httpfs-bin httpfs ${CURL_LIBRARY} ${FUSE_LIBRARIES} ) 40 | 41 | # aux targets 42 | add_custom_target( 43 | test-local-php 44 | COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../test/test-local-php.sh 45 | DEPENDS httpfs 46 | ) 47 | 48 | install( PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/httpfs DESTINATION bin ) 49 | -------------------------------------------------------------------------------- /src/lib/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "net.h" 3 | 4 | int HTTPFS_VERBOSE = 0; 5 | 6 | void httpfs_dump_raw_data( struct raw_data *raw_data ) 7 | { 8 | int i; 9 | 10 | fprintf( stderr , "'" ); 11 | 12 | for ( i = 0 ; i < raw_data->size ; i++ ) 13 | { 14 | unsigned char c; 15 | 16 | c = raw_data->payload[ i ]; 17 | fprintf( stderr , isprint( c ) ? "%c" : "\\x%02x" , c ); 18 | } 19 | 20 | fprintf( stderr , "'\n" ); 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef _HTTPFS_DEBUG_H 2 | #define _HTTPFS_DEBUG_H 3 | 4 | #include 5 | #include "net.h" 6 | 7 | #define DEBUG_PREFIX ">>> " 8 | 9 | #define LOG( string ) \ 10 | do if ( HTTPFS_VERBOSE ) fprintf( stderr , DEBUG_PREFIX "%s\n" , string ); \ 11 | while ( 0 ) 12 | 13 | #define LOGF( format , ... ) \ 14 | do if ( HTTPFS_VERBOSE ) fprintf( stderr , DEBUG_PREFIX format "\n" , ##__VA_ARGS__ ); \ 15 | while ( 0 ) 16 | 17 | #define DUMP_RAW_DATA( label , raw_data ) \ 18 | do if ( HTTPFS_VERBOSE ) { \ 19 | fprintf( stderr , DEBUG_PREFIX "%s%zu byte: " , \ 20 | label , raw_data.size ); \ 21 | httpfs_dump_raw_data( &raw_data ); } \ 22 | while ( 0 ) 23 | 24 | extern int HTTPFS_VERBOSE; 25 | 26 | void httpfs_dump_raw_data( struct raw_data *raw_data ); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/lib/fuse_api/chmod.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_chmod( const char *path , 4 | mode_t mode ) 5 | { 6 | struct 7 | { 8 | uint32_t mode; 9 | } 10 | header = { htonl( mode ) }; 11 | 12 | HTTPFS_DO_REQUEST_WITH_HEADER( HTTPFS_OPCODE_chmod ) 13 | { 14 | HTTPFS_CHECK_RESPONSE_STATUS; 15 | HTTPFS_CLEANUP; 16 | HTTPFS_RETURN( 0 ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/fuse_api/chown.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_chown( const char *path , 4 | uid_t uid , 5 | gid_t gid ) 6 | { 7 | struct 8 | { 9 | uint32_t uid; 10 | uint32_t gid; 11 | } 12 | header = { htonl( uid ) , 13 | htonl( gid ) }; 14 | 15 | HTTPFS_DO_REQUEST_WITH_HEADER( HTTPFS_OPCODE_chown ) 16 | { 17 | HTTPFS_CHECK_RESPONSE_STATUS; 18 | HTTPFS_CLEANUP; 19 | HTTPFS_RETURN( 0 ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/fuse_api/create.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_create( const char *path , 4 | mode_t mode , 5 | struct fuse_file_info *fi ) 6 | { 7 | struct 8 | { 9 | uint32_t mode; 10 | } 11 | header = { htonl( mode ) }; 12 | 13 | HTTPFS_DO_REQUEST_WITH_HEADER( HTTPFS_OPCODE_create ) 14 | { 15 | HTTPFS_CHECK_RESPONSE_STATUS; 16 | HTTPFS_CLEANUP; 17 | HTTPFS_RETURN( 0 ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/fuse_api/fuse_api.h: -------------------------------------------------------------------------------- 1 | #ifndef _HTTPFS_FUSE_API_H 2 | #define _HTTPFS_FUSE_API_H 3 | 4 | int httpfs_getattr( const char * , struct stat * ); 5 | int httpfs_readlink( const char * , char * , size_t ); 6 | int httpfs_mknod( const char * , mode_t , dev_t ); 7 | int httpfs_mkdir( const char * , mode_t ); 8 | int httpfs_unlink( const char * ); 9 | int httpfs_rmdir( const char * ); 10 | int httpfs_symlink( const char * , const char * ); 11 | int httpfs_rename( const char * , const char * ); 12 | int httpfs_link( const char * , const char * ); 13 | int httpfs_chmod( const char * , mode_t ); 14 | int httpfs_chown( const char * , uid_t , gid_t ); 15 | int httpfs_truncate( const char * , off_t ); 16 | int httpfs_utime( const char * , struct utimbuf * ); 17 | int httpfs_open( const char * , struct fuse_file_info * ); 18 | int httpfs_read( const char * , char * , size_t , off_t , struct fuse_file_info * ); 19 | int httpfs_write( const char * , const char * , size_t , off_t , struct fuse_file_info * ); 20 | int httpfs_statfs( const char * , struct statvfs * ); 21 | int httpfs_flush( const char * , struct fuse_file_info * ); 22 | int httpfs_release( const char * , struct fuse_file_info * ); 23 | int httpfs_fsync( const char * , int , struct fuse_file_info * ); 24 | int httpfs_setxattr( const char * , const char * , const char * , size_t , int ); 25 | int httpfs_getxattr( const char * , const char * , char * , size_t ); 26 | int httpfs_listxattr( const char * , char * , size_t ); 27 | int httpfs_removexattr( const char * , const char * ); 28 | int httpfs_opendir( const char * , struct fuse_file_info * ); 29 | int httpfs_readdir( const char * , void * , fuse_fill_dir_t , off_t , struct fuse_file_info * ); 30 | int httpfs_releasedir( const char * , struct fuse_file_info * ); 31 | int httpfs_fsyncdir( const char * , int , struct fuse_file_info * ); 32 | int httpfs_access( const char * , int ); 33 | int httpfs_create( const char * , mode_t , struct fuse_file_info * ); 34 | int httpfs_ftruncate( const char * , off_t , struct fuse_file_info * ); 35 | int httpfs_fgetattr( const char * , struct stat * , struct fuse_file_info * ); 36 | int httpfs_lock( const char * , struct fuse_file_info * , int , struct flock * ); 37 | int httpfs_utimens( const char * , const struct timespec [2] ); 38 | int httpfs_bmap( const char * , size_t , uint64_t * ); 39 | int httpfs_ioctl( const char * , int , void * , struct fuse_file_info * , unsigned int , void * ); 40 | int httpfs_poll( const char * , struct fuse_file_info * , struct fuse_pollhandle * , unsigned * ); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/lib/fuse_api/getattr.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_getattr( const char *path , 4 | struct stat *stbuf ) 5 | { 6 | HTTPFS_DO_SIMPLE_REQUEST( HTTPFS_OPCODE_getattr ) 7 | { 8 | struct attrs 9 | { 10 | uint32_t dev; 11 | uint32_t ino; 12 | uint32_t mode; 13 | uint32_t nlink; 14 | uint32_t uid; 15 | uint32_t gid; 16 | uint32_t rdev; 17 | uint32_t size; 18 | uint32_t atime; 19 | uint32_t mtime; 20 | uint32_t ctime; 21 | uint32_t blksize; 22 | uint32_t blocks; 23 | } 24 | attrs; 25 | 26 | HTTPFS_CHECK_RESPONSE_STATUS; 27 | if ( response.size != sizeof( struct attrs ) ) 28 | { 29 | HTTPFS_CLEANUP; 30 | HTTPFS_RETURN( EBADMSG ); 31 | } 32 | 33 | memset( stbuf , 0 , sizeof( struct stat ) ); 34 | attrs = *( struct attrs * )response.payload; 35 | stbuf->st_dev = ntohl( attrs.dev ); 36 | stbuf->st_ino = ntohl( attrs.ino ); 37 | stbuf->st_mode = ntohl( attrs.mode ); 38 | stbuf->st_nlink = ntohl( attrs.nlink ); 39 | stbuf->st_uid = ntohl( attrs.uid ); 40 | stbuf->st_gid = ntohl( attrs.gid ); 41 | stbuf->st_rdev = ntohl( attrs.rdev ); 42 | stbuf->st_size = ntohl( attrs.size ); 43 | stbuf->st_atime = ntohl( attrs.atime ); 44 | stbuf->st_mtime = ntohl( attrs.mtime ); 45 | stbuf->st_ctime = ntohl( attrs.ctime ); 46 | stbuf->st_blksize = ntohl( attrs.blksize ); 47 | stbuf->st_blocks = ntohl( attrs.blocks ); 48 | 49 | HTTPFS_CLEANUP; 50 | HTTPFS_RETURN( 0 ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/lib/fuse_api/link.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_link( const char *path , 4 | const char *newpath ) 5 | { 6 | struct httpfs *httpfs = fuse_get_context()->private_data; 7 | char aux[ 4096 ] = { 0 }; 8 | struct raw_data raw_data = { aux , 0 }; 9 | 10 | /* prepend remote chroot */ 11 | if ( httpfs->remote_chroot ) 12 | { 13 | raw_data.size = strlen( httpfs->remote_chroot ); 14 | strcat( raw_data.payload , httpfs->remote_chroot ); 15 | } 16 | 17 | /* append the other path */ 18 | strcat( raw_data.payload , newpath ); 19 | raw_data.size += strlen( newpath ); 20 | 21 | HTTPFS_DO_REQUEST_WITH_DATA( HTTPFS_OPCODE_link ) 22 | { 23 | HTTPFS_CHECK_RESPONSE_STATUS; 24 | HTTPFS_CLEANUP; 25 | HTTPFS_RETURN( 0 ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/fuse_api/mkdir.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_mkdir( const char *path , 4 | mode_t mode ) 5 | { 6 | struct 7 | { 8 | uint32_t mode; 9 | } 10 | header = { htonl( mode | S_IFDIR ) }; 11 | 12 | HTTPFS_DO_REQUEST_WITH_HEADER( HTTPFS_OPCODE_mkdir ) 13 | { 14 | HTTPFS_CHECK_RESPONSE_STATUS; 15 | HTTPFS_CLEANUP; 16 | HTTPFS_RETURN( 0 ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/fuse_api/open.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_open( const char *path , 4 | struct fuse_file_info *fi ) 5 | { 6 | HTTPFS_RETURN( 0 ); 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/fuse_api/read.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_read( const char *path , 4 | char *buf , 5 | size_t size , 6 | off_t offset , 7 | struct fuse_file_info *fi ) 8 | { 9 | struct 10 | { 11 | uint32_t size; 12 | uint32_t offset; 13 | } 14 | header = { htonl( size ) , 15 | htonl( offset ) }; 16 | 17 | HTTPFS_DO_REQUEST_WITH_HEADER( HTTPFS_OPCODE_read ) 18 | { 19 | HTTPFS_CHECK_RESPONSE_STATUS; 20 | if ( response.size > size ) 21 | { 22 | HTTPFS_CLEANUP; 23 | HTTPFS_RETURN( EBADMSG ); 24 | } 25 | 26 | memcpy( buf , response.payload , response.size ); 27 | 28 | HTTPFS_CLEANUP; 29 | return response.size; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/fuse_api/readdir.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_readdir( const char *path , 4 | void *buf , 5 | fuse_fill_dir_t filler , 6 | off_t offset , 7 | struct fuse_file_info *fi ) 8 | { 9 | HTTPFS_DO_SIMPLE_REQUEST( HTTPFS_OPCODE_readdir ) 10 | { 11 | char *p; 12 | 13 | HTTPFS_CHECK_RESPONSE_STATUS; 14 | 15 | for ( p = response.payload ; 16 | p - response.payload < response.size ; 17 | p += strnlen( p , response.size - ( p - response.payload ) ) + 1 ) 18 | { 19 | filler( buf , p , NULL , 0 ); 20 | } 21 | 22 | HTTPFS_CLEANUP; 23 | HTTPFS_RETURN( 0 ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/fuse_api/readlink.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_readlink( const char *path , 4 | char *buf , 5 | size_t size ) 6 | { 7 | HTTPFS_DO_SIMPLE_REQUEST( HTTPFS_OPCODE_readlink ) 8 | { 9 | HTTPFS_CHECK_RESPONSE_STATUS; 10 | 11 | /* see man 2 readlink */ 12 | if ( response.size <= size - 1 ) 13 | { 14 | memcpy( buf , response.payload , response.size ); 15 | *( buf + response.size ) = '\0'; 16 | } 17 | else 18 | { 19 | memcpy( buf , response.payload , size ); 20 | } 21 | 22 | HTTPFS_CLEANUP; 23 | HTTPFS_RETURN( 0 ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/fuse_api/rename.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_rename( const char *path , 4 | const char *newpath ) 5 | { 6 | struct httpfs *httpfs = fuse_get_context()->private_data; 7 | char aux[ 4096 ] = { 0 }; 8 | struct raw_data raw_data = { aux , 0 }; 9 | 10 | /* prepend remote chroot */ 11 | if ( httpfs->remote_chroot ) 12 | { 13 | raw_data.size = strlen( httpfs->remote_chroot ); 14 | strcat( raw_data.payload , httpfs->remote_chroot ); 15 | } 16 | 17 | /* append the other path */ 18 | strcat( raw_data.payload , newpath ); 19 | raw_data.size += strlen( newpath ); 20 | 21 | HTTPFS_DO_REQUEST_WITH_DATA( HTTPFS_OPCODE_rename ) 22 | { 23 | HTTPFS_CHECK_RESPONSE_STATUS; 24 | HTTPFS_CLEANUP; 25 | HTTPFS_RETURN( 0 ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/fuse_api/rmdir.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_rmdir( const char *path ) 4 | { 5 | HTTPFS_DO_SIMPLE_REQUEST( HTTPFS_OPCODE_rmdir ) 6 | { 7 | HTTPFS_CHECK_RESPONSE_STATUS; 8 | HTTPFS_CLEANUP; 9 | HTTPFS_RETURN( 0 ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/fuse_api/symlink.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_symlink( const char *path , 4 | const char *newpath ) 5 | { 6 | struct httpfs *httpfs = fuse_get_context()->private_data; 7 | char aux[ 4096 ] = { 0 }; 8 | struct raw_data raw_data = { aux , 0 }; 9 | 10 | /* prepend remote chroot */ 11 | if ( httpfs->remote_chroot ) 12 | { 13 | raw_data.size = strlen( httpfs->remote_chroot ); 14 | strcat( raw_data.payload , httpfs->remote_chroot ); 15 | } 16 | 17 | /* append the other path */ 18 | strcat( raw_data.payload , newpath ); 19 | raw_data.size += strlen( newpath ); 20 | 21 | HTTPFS_DO_REQUEST_WITH_DATA( HTTPFS_OPCODE_symlink ) 22 | { 23 | HTTPFS_CHECK_RESPONSE_STATUS; 24 | HTTPFS_CLEANUP; 25 | HTTPFS_RETURN( 0 ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/fuse_api/truncate.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_truncate( const char *path , 4 | off_t offset ) 5 | { 6 | struct 7 | { 8 | uint32_t offset; 9 | } 10 | header = { htonl( offset ) }; 11 | 12 | HTTPFS_DO_REQUEST_WITH_HEADER( HTTPFS_OPCODE_truncate ) 13 | { 14 | HTTPFS_CHECK_RESPONSE_STATUS; 15 | HTTPFS_CLEANUP; 16 | HTTPFS_RETURN( 0 ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/fuse_api/unlink.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_unlink( const char *path ) 4 | { 5 | HTTPFS_DO_SIMPLE_REQUEST( HTTPFS_OPCODE_unlink ) 6 | { 7 | HTTPFS_CHECK_RESPONSE_STATUS; 8 | HTTPFS_CLEANUP; 9 | HTTPFS_RETURN( 0 ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/fuse_api/write.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | 3 | int httpfs_write( const char *path , 4 | const char *buf , 5 | size_t size , 6 | off_t offset , 7 | struct fuse_file_info *fi ) 8 | { 9 | struct raw_data raw_data = { ( char * )buf , size }; 10 | struct 11 | { 12 | uint32_t size; 13 | uint32_t offset; 14 | } 15 | header = { htonl( size ) , 16 | htonl( offset ) }; 17 | 18 | HTTPFS_DO_REQUEST_WITH_HEADER_AND_DATA( HTTPFS_OPCODE_write ) 19 | { 20 | uint32_t write_size; 21 | 22 | HTTPFS_CHECK_RESPONSE_STATUS; 23 | if ( response.size != sizeof( uint32_t ) ) 24 | { 25 | HTTPFS_CLEANUP; 26 | HTTPFS_RETURN( EBADMSG ); 27 | } 28 | 29 | write_size = ntohl( *( uint32_t * )response.payload ); 30 | HTTPFS_CLEANUP; 31 | return write_size; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/fuse_functions.def: -------------------------------------------------------------------------------- 1 | _( getattr ) 2 | _( readdir ) 3 | _( open ) 4 | _( read ) 5 | _( write ) 6 | _( truncate ) 7 | _( create ) 8 | _( unlink ) 9 | _( mkdir ) 10 | _( rmdir ) 11 | _( rename ) 12 | _( link ) 13 | _( readlink ) 14 | _( symlink ) 15 | _( chmod ) 16 | _( chown ) 17 | /*...*/ 18 | #undef _ 19 | -------------------------------------------------------------------------------- /src/lib/generators.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "httpfs.h" 3 | #include "generators.h" 4 | 5 | const struct httpfs_generator HTTPFS_GENERATORS[] = { 6 | #define _( x ) { #x , httpfs_generate_##x } , 7 | #include "generators.def" 8 | { NULL , NULL } 9 | }; 10 | 11 | int httpfs_generate( const char *name ) 12 | { 13 | const struct httpfs_generator *generator; 14 | 15 | for ( generator = HTTPFS_GENERATORS ; generator->name ; generator++ ) 16 | { 17 | if ( strcmp( generator->name , name ) == 0 ) 18 | { 19 | generator->function(); 20 | return 1; 21 | } 22 | } 23 | 24 | return 0; 25 | } 26 | 27 | void httpfs_generate_php() 28 | { 29 | #include "template.php.h" 30 | int i; 31 | const char **p; 32 | 33 | /* opcode names array */ 34 | printf( "" ); 55 | 56 | /* dump template */ 57 | for ( i = 0 ; i < template_php_len ; i++ ) 58 | { 59 | fputc( template_php[ i ] , stdout ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/lib/generators.def: -------------------------------------------------------------------------------- 1 | _( php ) 2 | #undef _ 3 | -------------------------------------------------------------------------------- /src/lib/generators.h: -------------------------------------------------------------------------------- 1 | #ifndef _HTTPFS_GENERATORS_H 2 | #define _HTTPFS_GENERATORS_H 3 | 4 | struct httpfs_generator 5 | { 6 | const char *name; 7 | void ( *function )(); 8 | }; 9 | 10 | extern const struct httpfs_generator HTTPFS_GENERATORS[]; 11 | 12 | int httpfs_generate( const char *name ); 13 | 14 | #define _( x ) void httpfs_generate_##x(); 15 | #include "generators.def" 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/lib/httpfs.c: -------------------------------------------------------------------------------- 1 | #include "httpfs.h" 2 | #include "fuse_api/fuse_api.h" 3 | 4 | const char *HTTPFS_OPCODE_NAMES[] = { 5 | "NONE" , 6 | #define _( x ) #x , 7 | #include "fuse_functions.def" 8 | NULL 9 | }; 10 | 11 | const char *HTTPFS_STATUS_NAMES[] = { 12 | #define _( x ) #x , 13 | #include "statuses.def" 14 | NULL 15 | }; 16 | 17 | static int check_remote_availability( struct httpfs *httpfs ) 18 | { 19 | _HTTPFS_DO_REQUEST( HTTPFS_OPCODE_getattr , httpfs , 20 | httpfs_prepare_request( httpfs , &_in , HTTPFS_OPCODE_getattr , 21 | NULL , "/" , NULL ); ) 22 | { 23 | HTTPFS_CHECK_RESPONSE_STATUS; 24 | HTTPFS_CLEANUP; 25 | HTTPFS_RETURN( 0 ); 26 | } 27 | } 28 | 29 | int httpfs_fuse_start( struct httpfs *httpfs , 30 | const char *url , 31 | const char *remote_chroot , 32 | char *mount_point ) 33 | { 34 | int argc; 35 | char *argv[ 4 ]; 36 | int rv; 37 | 38 | const struct fuse_operations operations = { 39 | #define _( x ) .x = httpfs_##x , 40 | #include "fuse_functions.def" 41 | }; 42 | 43 | httpfs->url = url; 44 | httpfs->remote_chroot = remote_chroot; 45 | 46 | /* initialize curl */ 47 | httpfs->curl = curl_easy_init(); 48 | if ( !httpfs->curl ) 49 | { 50 | return HTTPFS_CURL_ERROR; 51 | } 52 | 53 | /* check remote availability before mounting */ 54 | rv = -check_remote_availability( httpfs ); 55 | switch ( rv ) 56 | { 57 | case 0: break; 58 | case ECOMM: return HTTPFS_UNREACHABLE_SERVER_ERROR; 59 | case ENOENT: return HTTPFS_WRONG_CHROOT_ERROR; 60 | default: 61 | { 62 | errno = rv; 63 | return HTTPFS_ERRNO_ERROR; 64 | } 65 | } 66 | 67 | /* fuse arguments */ 68 | argc = 0; 69 | argv[ argc++ ] = "httpfs"; 70 | argv[ argc++ ] = "-s"; /* single thread */ 71 | if ( HTTPFS_VERBOSE ) argv[ argc++ ] = "-d"; /* debug and core dump */ 72 | argv[ argc++ ] = mount_point; 73 | 74 | /* start loop */ 75 | if ( fuse_main( argc , argv , &operations , httpfs ) ) 76 | { 77 | return HTTPFS_FUSE_ERROR; 78 | } 79 | 80 | return HTTPFS_NO_ERROR; 81 | } 82 | 83 | void httpfs_prepare_request( struct httpfs *httpfs , 84 | struct raw_data *in , 85 | uint8_t opcode , 86 | struct raw_data *header , 87 | const char *path , 88 | struct raw_data *data ) 89 | { 90 | size_t offset , header_size , remote_chroot_length , path_length , data_size; 91 | 92 | header_size = ( header ? header->size : 0 ); 93 | remote_chroot_length = ( httpfs->remote_chroot ? strlen( httpfs->remote_chroot ) : 0 ); 94 | path_length = strlen( path ) + 1; 95 | data_size = ( data ? data->size : 0 ); 96 | 97 | in->size = 1 + header_size + remote_chroot_length + path_length + data_size; 98 | in->payload = malloc( in->size ); 99 | 100 | /* opcode */ 101 | *in->payload = opcode; 102 | 103 | /* header */ 104 | offset = 1; 105 | if ( header ) 106 | { 107 | memcpy( in->payload + offset , header->payload , header->size ); 108 | } 109 | 110 | /* path */ 111 | offset += header_size; 112 | memcpy( in->payload + offset , httpfs->remote_chroot , remote_chroot_length ); 113 | offset += remote_chroot_length; 114 | memcpy( in->payload + offset , path , path_length ); 115 | 116 | /* data */ 117 | if ( data ) 118 | { 119 | offset += path_length; 120 | memcpy( in->payload + offset , data->payload , data->size ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/lib/httpfs.h: -------------------------------------------------------------------------------- 1 | #ifndef _HTTPFS_HTTPFS_H 2 | #define _HTTPFS_HTTPFS_H 3 | 4 | /* 5 | 6 | Request format: 7 | 8 | +--------+--------+------+------+ 9 | | opcode | fields | path | data | 10 | +--------+--------+------+------+ 11 | 12 | - opcode: 1 byte that identifies the requested operation (see "operation code" 13 | enum) 14 | 15 | - fields: arbitrarily long (even 0) packed data in big endian byte order 16 | 17 | - path: absolute Unix-like path ('\0' terminated string) 18 | 19 | - data: arbitrarily long (even 0) raw data 20 | 21 | Response format: 22 | 23 | +--------+------+ 24 | | status | data | 25 | +--------+------+ 26 | 27 | - status: 1 byte that describes the result of the operation (see "response 28 | status" enum) 29 | 30 | - data: arbitrarily long (even 0) raw data 31 | 32 | NOTE: these messages are carried over HTTP so there's no need to include an 33 | additional length field 34 | 35 | */ 36 | 37 | /* common includes for API implementation */ 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include "debug.h" 44 | #include "net.h" 45 | 46 | /* convenience macros used to implement the FUSE API functions; 'response' is 47 | the data received and it's available to the implementation; a structure named 48 | 'header' may be filled with values to be passed to the PHP script before 49 | calling this macros; a 'struct raw_data raw_data' may contains the additional 50 | data to pass to the PHP script; this macros expect a following block where 51 | the logic should be implemented */ 52 | #define HTTPFS_DO_SIMPLE_REQUEST( op ) \ 53 | _HTTPFS_DO_REQUEST( op , fuse_get_context()->private_data , \ 54 | httpfs_prepare_request( fuse_get_context()->private_data , \ 55 | &_in , op , NULL , path , NULL ); ) 56 | 57 | #define HTTPFS_DO_REQUEST_WITH_HEADER( op ) \ 58 | _HTTPFS_DO_REQUEST( op , fuse_get_context()->private_data , \ 59 | _header_data.payload = ( char * )&header; \ 60 | _header_data.size = sizeof( header ); \ 61 | httpfs_prepare_request( fuse_get_context()->private_data , \ 62 | &_in , op , &_header_data , path , NULL ); ) 63 | 64 | #define HTTPFS_DO_REQUEST_WITH_DATA( op ) \ 65 | _HTTPFS_DO_REQUEST( op , fuse_get_context()->private_data , \ 66 | httpfs_prepare_request( fuse_get_context()->private_data , \ 67 | &_in , op , NULL , path , &raw_data ); ) 68 | 69 | #define HTTPFS_DO_REQUEST_WITH_HEADER_AND_DATA( op ) \ 70 | _HTTPFS_DO_REQUEST( op , fuse_get_context()->private_data , \ 71 | _header_data.payload = ( char * )&header; \ 72 | _header_data.size = sizeof( header ); \ 73 | httpfs_prepare_request( fuse_get_context()->private_data , \ 74 | &_in , op , &_header_data , path , &raw_data ); ) 75 | 76 | /* common */ 77 | #define _HTTPFS_DO_REQUEST( op , httpfs , prepare_header ) \ 78 | LOGF( "REQUEST: %s (%i)" , \ 79 | HTTPFS_OPCODE_NAMES[ op ] , op ); \ 80 | struct raw_data _in = { 0 } , _out = { 0 } , _header_data = { 0 }; \ 81 | struct raw_data response = { 0 }; \ 82 | ( void )response; \ 83 | ( void )_header_data; \ 84 | prepare_header \ 85 | DUMP_RAW_DATA( "SENDING " , _in ); \ 86 | if ( CURLE_OK != httpfs_do_post( httpfs , &_in , &_out ) ) { \ 87 | LOG( "REQUEST: failed" ); \ 88 | HTTPFS_CLEANUP; \ 89 | return -ECOMM; \ 90 | } else 91 | 92 | /* return errno from FUSE callback functions */ 93 | #define HTTPFS_RETURN( errno ) \ 94 | LOGF( "RETURN: %s (%i)" , strerror( errno ) , errno ); \ 95 | return -errno; 96 | 97 | /* check the response status and return if an error is occurred */ 98 | #define HTTPFS_CHECK_RESPONSE_STATUS \ 99 | response.payload = _out.payload + 1; \ 100 | response.size = _out.size - 1; \ 101 | DUMP_RAW_DATA( "RECEIVED " , _out ); \ 102 | switch ( *_out.payload ) { \ 103 | _HTTPFS_CHECK_HANDLE_ERROR( ENTRY_NOT_FOUND , ENOENT ) \ 104 | _HTTPFS_CHECK_HANDLE_ERROR( CANNOT_ACCESS , EACCES ) \ 105 | _HTTPFS_CHECK_HANDLE_ERROR( NOT_PERMITTED , EPERM ) \ 106 | case HTTPFS_STATUS_OK: _HTTPFS_DUMP_STATUS; break; \ 107 | default: HTTPFS_CLEANUP; HTTPFS_RETURN( EBADMSG ); \ 108 | } 109 | 110 | #define _HTTPFS_CHECK_HANDLE_ERROR( status , errno ) \ 111 | case HTTPFS_STATUS_##status: \ 112 | _HTTPFS_DUMP_STATUS; HTTPFS_CLEANUP; \ 113 | HTTPFS_RETURN( errno ) 114 | 115 | #define _HTTPFS_DUMP_STATUS \ 116 | LOGF( "RESPONSE: %s (%i)" , \ 117 | HTTPFS_STATUS_NAMES[ ( int )*_out.payload ] , *_out.payload ); \ 118 | 119 | /* to be called before return in FUSE API functions */ 120 | #define HTTPFS_CLEANUP \ 121 | free( _in.payload ); \ 122 | free( _out.payload ) 123 | 124 | /* initialization errors */ 125 | enum 126 | { 127 | HTTPFS_NO_ERROR , 128 | HTTPFS_FUSE_ERROR , 129 | HTTPFS_CURL_ERROR , 130 | HTTPFS_UNREACHABLE_SERVER_ERROR , 131 | HTTPFS_WRONG_CHROOT_ERROR , 132 | HTTPFS_ERRNO_ERROR 133 | }; 134 | 135 | /* context */ 136 | struct httpfs 137 | { 138 | const char *url; 139 | const char *remote_chroot; 140 | CURL *curl; 141 | }; 142 | 143 | /* operation codes */ 144 | #define _( x ) HTTPFS_OPCODE_##x , 145 | enum { HTTPFS_OPCODE_NONE , 146 | #include "fuse_functions.def" 147 | }; 148 | extern const char *HTTPFS_OPCODE_NAMES[]; 149 | 150 | /* response status */ 151 | #define _( x ) HTTPFS_STATUS_##x , 152 | enum { 153 | #include "statuses.def" 154 | }; 155 | extern const char *HTTPFS_STATUS_NAMES[]; 156 | 157 | int httpfs_fuse_start( struct httpfs *httpfs , 158 | const char *url , 159 | const char *remote_chroot , 160 | char *mount_point ); 161 | 162 | void httpfs_prepare_request( struct httpfs *httpfs , 163 | struct raw_data *in , 164 | uint8_t opcode , 165 | struct raw_data *header , 166 | const char *path , 167 | struct raw_data *data ); 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /src/lib/net.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "httpfs.h" 4 | 5 | static size_t retrieve_chunk( char *ptr , 6 | size_t size , 7 | size_t nmemb , 8 | void *userdata ) 9 | { 10 | struct raw_data *data; 11 | size_t chunk_size; 12 | 13 | chunk_size = size * nmemb; 14 | data = ( struct raw_data * )userdata; 15 | data->payload = realloc( data->payload , data->size + chunk_size ); 16 | memcpy( data->payload + data->size , ptr , chunk_size ); 17 | data->size += chunk_size; 18 | 19 | return chunk_size; 20 | } 21 | 22 | CURLcode httpfs_do_post( struct httpfs *httpfs , 23 | const struct raw_data *in , 24 | struct raw_data *out ) 25 | { 26 | CURL *curl; 27 | 28 | out->payload = malloc( 1 ); 29 | out->size = 0; 30 | 31 | curl = httpfs->curl; 32 | curl_easy_setopt( curl , CURLOPT_URL , httpfs->url ); 33 | curl_easy_setopt( curl , CURLOPT_POSTFIELDS , in->payload ); 34 | curl_easy_setopt( curl , CURLOPT_POSTFIELDSIZE , in->size ); 35 | curl_easy_setopt( curl , CURLOPT_WRITEFUNCTION , retrieve_chunk ); 36 | curl_easy_setopt( curl , CURLOPT_WRITEDATA , out ); 37 | 38 | return curl_easy_perform( curl ); 39 | } 40 | -------------------------------------------------------------------------------- /src/lib/net.h: -------------------------------------------------------------------------------- 1 | #ifndef _HTTPFS_NET_H 2 | #define _HTTPFS_NET_H 3 | 4 | #include 5 | 6 | struct httpfs; 7 | 8 | struct raw_data 9 | { 10 | char *payload; 11 | size_t size; 12 | }; 13 | 14 | CURLcode httpfs_do_post( struct httpfs *httpfs , 15 | const struct raw_data *in , 16 | struct raw_data *out ); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/lib/statuses.def: -------------------------------------------------------------------------------- 1 | _( OK ) 2 | _( ENTRY_NOT_FOUND ) 3 | _( CANNOT_ACCESS ) 4 | _( NOT_PERMITTED ) 5 | /*...*/ 6 | #undef _ 7 | -------------------------------------------------------------------------------- /src/lib/templates/template.php: -------------------------------------------------------------------------------- 1 | 310 | -------------------------------------------------------------------------------- /src/lib/version.h: -------------------------------------------------------------------------------- 1 | #ifndef _HTTPFS_VERSION_H 2 | #define _HTTPFS_VERSION_H 3 | 4 | #define HTTPFS_VERSION "0.3" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "generators.h" 3 | #include "httpfs.h" 4 | #include "fuse_api/fuse_api.h" 5 | #include "version.h" 6 | 7 | static void usage() 8 | { 9 | fprintf( stderr , 10 | "Usage:\n\n" 11 | " httpfs --help\n" 12 | " httpfs --version\n" 13 | " httpfs generators\n" 14 | " httpfs generate \n" 15 | " httpfs mount []\n" ); 16 | } 17 | 18 | static void info() 19 | { 20 | fprintf( stderr , "httpfs " HTTPFS_VERSION "\n" ); 21 | } 22 | 23 | static void set_verbose_mode() 24 | { 25 | char *env; 26 | 27 | env = getenv( "HTTPFS_VERBOSE" ); 28 | if ( env && strcmp( env , "1" ) == 0 ) HTTPFS_VERBOSE = 1; 29 | } 30 | 31 | int main( int argc , char *argv[] ) 32 | { 33 | set_verbose_mode(); 34 | 35 | if ( argc == 2 && strcmp( argv[ 1 ] , "--version" ) == 0 ) 36 | { 37 | info(); 38 | } 39 | else if ( argc == 2 && strcmp( argv[ 1 ] , "--help" ) == 0 ) 40 | { 41 | usage(); 42 | } 43 | else if ( argc == 2 && strcmp( argv[ 1 ] , "generators" ) == 0 ) 44 | { 45 | const struct httpfs_generator *generator; 46 | 47 | for ( generator = HTTPFS_GENERATORS ; generator->name ; generator++ ) 48 | { 49 | printf( "%s\n" , generator->name ); 50 | } 51 | } 52 | else if ( argc == 3 && strcmp( argv[ 1 ] , "generate" ) == 0 ) 53 | { 54 | if ( !httpfs_generate( argv[ 2 ] ) ) 55 | { 56 | usage(); 57 | return EXIT_FAILURE; 58 | } 59 | } 60 | else if ( ( argc == 4 || argc == 5 ) && 61 | strcmp( argv[ 1 ] , "mount" ) == 0 ) 62 | { 63 | struct httpfs httpfs; 64 | const char *url; 65 | const char *remote_chroot; 66 | char *mount_point; 67 | int rv; 68 | 69 | url = argv[ 2 ]; 70 | remote_chroot = ( argc == 5 ? argv[ 4 ] : NULL ); 71 | mount_point = argv[ 3 ]; 72 | 73 | rv = httpfs_fuse_start( &httpfs , url , remote_chroot , mount_point ); 74 | 75 | if ( rv ) 76 | { 77 | fprintf( stderr , "Unable to mount: " ); 78 | 79 | switch ( rv ) 80 | { 81 | case HTTPFS_FUSE_ERROR: 82 | fprintf( stderr , "cannot initialize FUSE\n" ); 83 | break; 84 | 85 | case HTTPFS_CURL_ERROR: 86 | fprintf( stderr , "cannot initialize cURL\n" ); 87 | break; 88 | 89 | case HTTPFS_UNREACHABLE_SERVER_ERROR: 90 | fprintf( stderr , "cannot reach the remote server\n" ); 91 | break; 92 | 93 | case HTTPFS_WRONG_CHROOT_ERROR: 94 | fprintf( stderr , "cannot find the remote path\n" ); 95 | break; 96 | 97 | case HTTPFS_ERRNO_ERROR: 98 | fprintf( stderr , "errno (%i) %s\n" , errno , strerror( errno ) ); 99 | break; 100 | } 101 | } 102 | } 103 | else 104 | { 105 | usage(); 106 | return EXIT_FAILURE; 107 | } 108 | 109 | return EXIT_SUCCESS; 110 | } 111 | -------------------------------------------------------------------------------- /test/php.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # The current working directory must contain the httpfs executable e.g.: 4 | # 5 | # make debug 6 | # cd build-debug 7 | # ../test/php.sh 8 | # 9 | # Also, PHP with server support must be installed and configured to have the 10 | # proper permissions in /tmp/. 11 | 12 | function on_error 13 | { 14 | set +e +v 15 | echo -e '\n>>> Error!\n' 16 | exit 1 17 | } 18 | 19 | function on_exit 20 | { 21 | set +e +v 22 | kill $HTTPFS_PID $PHP_PID 23 | echo '>>> Unmounting filesystem' 24 | fusermount -z -u $BASE_DIR/mnt/ &>> $BASE_DIR/log/httpfs.log 25 | echo ">>> less $BASE_DIR/log/httpfs.log" 26 | echo ">>> less $BASE_DIR/log/php.log" 27 | echo ">>> ls -l $BASE_DIR/tmp/" 28 | } 29 | 30 | trap 'on_error' ERR 31 | trap 'on_exit' EXIT 32 | 33 | # setup 34 | export HTTPFS_VERBOSE=1 35 | BASE_DIR=$(mktemp -d) 36 | echo ">>> Setting up the environment in $BASE_DIR/" 37 | mkdir -p $BASE_DIR/www/ 38 | mkdir -p $BASE_DIR/mnt/ 39 | mkdir -p $BASE_DIR/tmp/ 40 | mkdir -p $BASE_DIR/log/ 41 | echo '>>> Generating PHP file' 42 | ./httpfs generate php > $BASE_DIR/www/httpfs.php 43 | echo '>>> Starting web server on http://localhost:8000' 44 | php -S localhost:8000 -t $BASE_DIR/www/ &> $BASE_DIR/log/php.log & disown 45 | sleep 1 46 | PHP_PID=$! 47 | echo '>>> Mounting filesystem' 48 | ./httpfs mount http://localhost:8000/httpfs.php \ 49 | $BASE_DIR/mnt/ $BASE_DIR/tmp/ &> $BASE_DIR/log/httpfs.log & disown 50 | sleep 1 51 | HTTPFS_PID=$! 52 | 53 | # tests 54 | echo '>>> Starting tests' 55 | cd $BASE_DIR/mnt/ 56 | set -e -v 57 | ############################################################ 58 | help > foo 59 | chmod 765 foo 60 | [ $(stat -c "%a" foo) -eq 765 ] 61 | ln -s foo bar 62 | mkdir baz 63 | cd baz 64 | cp ../foo . 65 | wc foo > qwe 66 | ls -l > list 67 | cd .. 68 | ln baz/list list 69 | [ $(stat -c "%h" list) -eq 2 ] 70 | mv list qwerty 71 | rm -fr baz 72 | [ $(stat -c "%h" qwerty) -eq 1 ] 73 | ############################################################ 74 | set +e +v 75 | --------------------------------------------------------------------------------