Skip to content
GitLab
Explore
Sign in
Commits on Source (4)
fuse integration test setup
· a5a88018
sevenuz
authored
Aug 06, 2025
a5a88018
improve test and fix touch in fuse
· 685ff9ad
sevenuz
authored
Aug 08, 2025
685ff9ad
cleanup and refactoring, fix lookup_count
· 23aeae88
sevenuz
authored
Aug 08, 2025
23aeae88
mkdir causes segvault
· 37d6d804
sevenuz
authored
Aug 08, 2025
37d6d804
Show whitespace changes
Inline
Side-by-side
include/client/fuse/fuse_client.hpp
View file @
37d6d804
...
@@ -97,6 +97,31 @@ struct __dirstream {
...
@@ -97,6 +97,31 @@ struct __dirstream {
#include
<client/preload_context.hpp>
#include
<client/preload_context.hpp>
#include
<client/user_functions.hpp>
#include
<client/user_functions.hpp>
// TODO do we really need the stat here? no i dont think so
struct
Inode
{
std
::
string
path
;
struct
stat
st
;
uint64_t
lookup_count
;
};
enum
{
CACHE_NEVER
,
CACHE_NORMAL
,
CACHE_ALWAYS
,
};
struct
u_data
{
pthread_mutex_t
mutex
;
int
debug
;
int
writeback
;
int
flock
;
int
xattr
;
char
*
source
;
double
timeout
;
int
cache
;
int
timeout_set
;
};
struct
GkfsDir
{
// Hypothetical structure that might be used if DIR is cast
struct
GkfsDir
{
// Hypothetical structure that might be used if DIR is cast
int
fd
;
int
fd
;
long
int
tell_pos
;
// for telldir/seekdir
long
int
tell_pos
;
// for telldir/seekdir
...
@@ -104,68 +129,4 @@ struct GkfsDir { // Hypothetical structure that might be used if DIR is cast
...
@@ -104,68 +129,4 @@ struct GkfsDir { // Hypothetical structure that might be used if DIR is cast
// other members libc DIR might have
// other members libc DIR might have
};
};
/*
* Creates files on the underlying file system in response to a FUSE_MKNOD
* operation
*/
static
inline
int
mknod_wrapper
(
int
dirfd
,
const
char
*
path
,
const
char
*
link
,
int
mode
,
dev_t
rdev
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"mknod_wrapper
\n
"
);
int
res
=
-
1
;
if
(
S_ISREG
(
mode
))
{
// res = lol_openat(dirfd, path, mode, O_CREAT | O_EXCL | O_WRONLY);
fuse_log
(
FUSE_LOG_DEBUG
,
"lol_openat internal %s
\n
"
,
res
);
if
(
res
>=
0
)
res
=
gkfs
::
syscall
::
gkfs_close
(
res
);
}
else
if
(
S_ISDIR
(
mode
))
{
// GKFS_PATH_OPERATION(create, dirfd, path, mode | S_IFDIR)
// res = gkfs::syscall::gkfs_create(resolved, mode | S_IFDIR);
// res = mkdirat(dirfd, path, mode);
}
else
if
(
S_ISLNK
(
mode
)
&&
link
!=
NULL
)
{
fuse_log
(
FUSE_LOG_ERR
,
"fifo in mknod_wrapper not supported
\n
"
);
errno
=
ENOTSUP
;
return
-
1
;
// res = symlinkat(link, dirfd, path);
}
else
if
(
S_ISFIFO
(
mode
))
{
fuse_log
(
FUSE_LOG_ERR
,
"fifo in mknod_wrapper not supported
\n
"
);
errno
=
ENOTSUP
;
return
-
1
;
// res = mkfifoat(dirfd, path, mode);
#ifdef __FreeBSD__
}
else
if
(
S_ISSOCK
(
mode
))
{
struct
sockaddr_un
su
;
int
fd
;
if
(
strlen
(
path
)
>=
sizeof
(
su
.
sun_path
))
{
errno
=
ENAMETOOLONG
;
return
-
1
;
}
fd
=
socket
(
AF_UNIX
,
SOCK_STREAM
,
0
);
if
(
fd
>=
0
)
{
/*
* We must bind the socket to the underlying file
* system to create the socket file, even though
* we'll never listen on this socket.
*/
su
.
sun_family
=
AF_UNIX
;
strncpy
(
su
.
sun_path
,
path
,
sizeof
(
su
.
sun_path
));
res
=
bindat
(
dirfd
,
fd
,
(
struct
sockaddr
*
)
&
su
,
sizeof
(
su
));
if
(
res
==
0
)
close
(
fd
);
}
else
{
res
=
-
1
;
}
#endif
}
else
{
fuse_log
(
FUSE_LOG_ERR
,
"mknodat in mknod_wrapper not supported
\n
"
);
errno
=
ENOTSUP
;
return
-
1
;
// res = mknodat(dirfd, path, mode, rdev);
}
return
res
;
}
#endif // GKFS_CLIENT_FUSE_CONTEXT_HPP
#endif // GKFS_CLIENT_FUSE_CONTEXT_HPP
src/client/fuse/fuse_client.cpp
View file @
37d6d804
...
@@ -39,47 +39,45 @@
...
@@ -39,47 +39,45 @@
#include
<client/fuse/fuse_client.hpp>
#include
<client/fuse/fuse_client.hpp>
struct
lo_inode
{
static
struct
fuse_lowlevel_ops
ll_ops
;
struct
lo_inode
*
next
;
/* protected by lo->mutex */
static
std
::
mutex
ino_mutex
;
struct
lo_inode
*
prev
;
/* protected by lo->mutex */
static
std
::
unordered_map
<
fuse_ino_t
,
Inode
>
ino_map
;
int
fd
;
static
std
::
unordered_map
<
std
::
string
,
fuse_ino_t
>
path_map
;
ino_t
ino
;
static
fuse_ino_t
next_ino
=
2
;
// reserve 1 for root
dev_t
dev
;
uint64_t
refcount
;
/* protected by lo->mutex */
static
fuse_ino_t
};
alloc_inode
(
const
std
::
string
&
path
)
{
std
::
lock_guard
<
std
::
mutex
>
lk
(
ino_mutex
);
enum
{
fuse_ino_t
ino
=
next_ino
++
;
CACHE_NEVER
,
ino_map
[
ino
]
=
{
path
,
{},
1
};
CACHE_NORMAL
,
path_map
[
path
]
=
ino
;
CACHE_ALWAYS
,
return
ino
;
};
}
struct
lo_data
{
static
Inode
*
pthread_mutex_t
mutex
;
get_inode
(
fuse_ino_t
ino
)
{
int
debug
;
std
::
lock_guard
<
std
::
mutex
>
lk
(
ino_mutex
);
int
writeback
;
auto
it
=
ino_map
.
find
(
ino
);
int
flock
;
return
it
!=
ino_map
.
end
()
?
&
it
->
second
:
nullptr
;
int
xattr
;
}
char
*
source
;
double
timeout
;
static
struct
u_data
*
int
cache
;
udata
(
fuse_req_t
req
)
{
int
timeout_set
;
return
(
struct
u_data
*
)
fuse_req_userdata
(
req
);
struct
lo_inode
root
;
/* protected by lo->mutex */
}
};
static
const
struct
fuse_opt
lo_opts
[]
=
{
static
const
struct
fuse_opt
lo_opts
[]
=
{
{
"writeback"
,
offsetof
(
struct
lo_data
,
writeback
),
1
},
{
"writeback"
,
offsetof
(
struct
u_data
,
writeback
),
1
},
{
"no_writeback"
,
offsetof
(
struct
lo_data
,
writeback
),
0
},
{
"no_writeback"
,
offsetof
(
struct
u_data
,
writeback
),
0
},
{
"source=%s"
,
offsetof
(
struct
lo_data
,
source
),
0
},
{
"flock"
,
offsetof
(
struct
u_data
,
flock
),
1
},
{
"flock"
,
offsetof
(
struct
lo_data
,
flock
),
1
},
{
"no_flock"
,
offsetof
(
struct
u_data
,
flock
),
0
},
{
"no_flock"
,
offsetof
(
struct
lo_data
,
flock
),
0
},
{
"xattr"
,
offsetof
(
struct
u_data
,
xattr
),
1
},
{
"xattr"
,
offsetof
(
struct
lo_data
,
xattr
),
1
},
{
"no_xattr"
,
offsetof
(
struct
u_data
,
xattr
),
0
},
{
"no_xattr"
,
offsetof
(
struct
lo_data
,
xattr
),
0
},
{
"timeout=%lf"
,
offsetof
(
struct
u_data
,
timeout
),
0
},
{
"timeout=%lf"
,
offsetof
(
struct
lo_data
,
timeout
),
0
},
{
"timeout="
,
offsetof
(
struct
u_data
,
timeout_set
),
1
},
{
"timeout="
,
offsetof
(
struct
lo_data
,
timeout_set
),
1
},
{
"cache=never"
,
offsetof
(
struct
u_data
,
cache
),
CACHE_NEVER
},
{
"cache=never"
,
offsetof
(
struct
lo_data
,
cache
),
CACHE_NEVER
},
{
"cache=auto"
,
offsetof
(
struct
u_data
,
cache
),
CACHE_NORMAL
},
{
"cache=auto"
,
offsetof
(
struct
lo_data
,
cache
),
CACHE_NORMAL
},
{
"cache=always"
,
offsetof
(
struct
u_data
,
cache
),
CACHE_ALWAYS
},
{
"cache=always"
,
offsetof
(
struct
lo_data
,
cache
),
CACHE_ALWAYS
},
FUSE_OPT_END
};
FUSE_OPT_END
};
...
@@ -99,85 +97,58 @@ passthrough_ll_help(void) {
...
@@ -99,85 +97,58 @@ passthrough_ll_help(void) {
" -o cache=always Cache always
\n
"
);
" -o cache=always Cache always
\n
"
);
}
}
static
struct
lo_data
*
lo_data
(
fuse_req_t
req
)
{
return
(
struct
lo_data
*
)
fuse_req_userdata
(
req
);
}
static
struct
lo_inode
*
lo_inode
(
fuse_req_t
req
,
fuse_ino_t
ino
)
{
if
(
ino
==
FUSE_ROOT_ID
)
return
&
lo_data
(
req
)
->
root
;
else
return
(
struct
lo_inode
*
)
(
uintptr_t
)
ino
;
}
static
int
lo_fd
(
fuse_req_t
req
,
fuse_ino_t
ino
)
{
return
lo_inode
(
req
,
ino
)
->
fd
;
}
static
bool
lo_debug
(
fuse_req_t
req
)
{
return
lo_data
(
req
)
->
debug
!=
0
;
}
static
void
static
void
lo_init
(
void
*
userdata
,
struct
fuse_conn_info
*
conn
)
{
init_handler
(
void
*
userdata
,
struct
fuse_conn_info
*
conn
)
{
// TODO init gkfs
fuse_log
(
FUSE_LOG_DEBUG
,
"init handler
\n
"
);
struct
lo_data
*
lo
=
(
struct
lo_data
*
)
userdata
;
// struct u_data* lo = (struct u_data*) userdata;
bool
has_flag
;
// bool has_flag;
if
(
lo
->
writeback
)
{
// TODO check other capabilities e.g. FUSE_CAP_READDIRPLUS
has_flag
=
fuse_set_feature_flag
(
conn
,
FUSE_CAP_WRITEBACK_CACHE
);
// if(lo->writeback) {
if
(
lo
->
debug
&&
has_flag
)
// has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
fuse_log
(
FUSE_LOG_DEBUG
,
"lo_init: activating writeback
\n
"
);
// if(lo->debug && has_flag)
}
// fuse_log(FUSE_LOG_DEBUG, "init_handler: activating writeback\n");
if
(
lo
->
flock
&&
conn
->
capable
&
FUSE_CAP_FLOCK_LOCKS
)
{
// }
has_flag
=
fuse_set_feature_flag
(
conn
,
FUSE_CAP_FLOCK_LOCKS
);
// if(lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
if
(
lo
->
debug
&&
has_flag
)
// has_flag = fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS);
fuse_log
(
FUSE_LOG_DEBUG
,
"lo_init: activating flock locks
\n
"
);
// if(lo->debug && has_flag)
}
// fuse_log(FUSE_LOG_DEBUG, "init_handler: activating flock
// locks\n");
// }
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn
->
no_interrupt
=
1
;
//
conn->no_interrupt = 1;
}
}
// Simplified inode structure
static
void
struct
Inode
{
destroy_handler
(
void
*
userdata
)
{
std
::
string
path
;
fuse_log
(
FUSE_LOG_DEBUG
,
"destroy handler
\n
"
);
struct
stat
st
;
// userdata is GekkoFuse* if passed
uint64_t
lookup_count
;
};
static
std
::
mutex
ino_mutex
;
static
std
::
unordered_map
<
fuse_ino_t
,
Inode
>
ino_map
;
static
fuse_ino_t
next_ino
=
2
;
// reserve 1 for root
static
fuse_ino_t
alloc_inode
(
const
std
::
string
&
path
)
{
std
::
lock_guard
<
std
::
mutex
>
lk
(
ino_mutex
);
fuse_ino_t
ino
=
next_ino
++
;
ino_map
[
ino
]
=
{
path
,
{},
1
};
return
ino
;
}
static
Inode
*
get_inode
(
fuse_ino_t
ino
)
{
std
::
lock_guard
<
std
::
mutex
>
lk
(
ino_mutex
);
auto
it
=
ino_map
.
find
(
ino
);
return
it
!=
ino_map
.
end
()
?
&
it
->
second
:
nullptr
;
}
}
static
void
static
void
lookup_handler
(
fuse_req_t
req
,
fuse_ino_t
parent
,
const
char
*
name
)
{
lookup_handler
(
fuse_req_t
req
,
fuse_ino_t
parent
,
const
char
*
name
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"lookup handler ino %u
\n
"
,
parent
);
fuse_log
(
FUSE_LOG_DEBUG
,
"lookup handler ino %u
\n
"
,
parent
);
auto
*
ud
=
udata
(
req
);
auto
*
parent_inode
=
get_inode
(
parent
);
auto
*
parent_inode
=
get_inode
(
parent
);
if
(
!
parent_inode
)
{
if
(
!
parent_inode
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"this error 1
\n
"
,
parent
);
fuse_reply_err
(
req
,
ENOENT
);
fuse_reply_err
(
req
,
ENOENT
);
return
;
return
;
}
}
std
::
string
child
=
parent_inode
->
path
+
name
;
std
::
string
child
=
parent_inode
->
path
+
name
;
fuse_log
(
FUSE_LOG_DEBUG
,
"lookup %s
\n
"
,
child
.
c_str
());
fuse_log
(
FUSE_LOG_DEBUG
,
"lookup %s
\n
"
,
child
.
c_str
());
// See if we already have this path
auto
it
=
path_map
.
find
(
child
);
fuse_ino_t
ino
;
if
(
it
!=
path_map
.
end
())
{
ino
=
it
->
second
;
ino_map
[
ino
].
lookup_count
++
;
}
else
{
ino
=
alloc_inode
(
child
);
}
struct
stat
st
;
struct
stat
st
;
int
rc
=
gkfs
::
syscall
::
gkfs_stat
(
child
,
&
st
);
int
rc
=
gkfs
::
syscall
::
gkfs_stat
(
child
,
&
st
);
if
(
rc
<
0
)
{
if
(
rc
<
0
)
{
...
@@ -185,19 +156,19 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
...
@@ -185,19 +156,19 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
fuse_reply_err
(
req
,
ENOENT
);
fuse_reply_err
(
req
,
ENOENT
);
return
;
return
;
}
}
fuse_ino_t
ino
=
alloc_inode
(
child
);
ino_map
[
ino
].
st
=
st
;
ino_map
[
ino
].
st
=
st
;
fuse_entry_param
e
=
{};
fuse_entry_param
e
=
{};
e
.
ino
=
ino
;
e
.
ino
=
ino
;
e
.
attr
=
st
;
e
.
attr
=
st
;
e
.
attr_timeout
=
1.0
;
e
.
attr_timeout
=
ud
->
timeout
;
e
.
entry_timeout
=
1.0
;
e
.
entry_timeout
=
ud
->
timeout
;
fuse_reply_entry
(
req
,
&
e
);
fuse_reply_entry
(
req
,
&
e
);
}
}
static
void
static
void
getattr_handler
(
fuse_req_t
req
,
fuse_ino_t
ino
,
struct
fuse_file_info
*
fi
)
{
getattr_handler
(
fuse_req_t
req
,
fuse_ino_t
ino
,
struct
fuse_file_info
*
fi
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"getattr handler
\n
"
);
fuse_log
(
FUSE_LOG_DEBUG
,
"getattr handler
\n
"
);
auto
*
ud
=
udata
(
req
);
auto
*
inode
=
get_inode
(
ino
);
auto
*
inode
=
get_inode
(
ino
);
if
(
!
inode
)
{
if
(
!
inode
)
{
fuse_reply_err
(
req
,
ENOENT
);
fuse_reply_err
(
req
,
ENOENT
);
...
@@ -212,39 +183,42 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
...
@@ -212,39 +183,42 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
return
;
return
;
}
}
inode
->
st
=
st
;
inode
->
st
=
st
;
fuse_reply_attr
(
req
,
&
st
,
1.0
);
fuse_reply_attr
(
req
,
&
st
,
ud
->
timeout
);
}
}
static
void
static
void
setattr_handler
(
fuse_req_t
req
,
fuse_ino_t
ino
,
struct
stat
*
attr
,
int
to_set
,
setattr_handler
(
fuse_req_t
req
,
fuse_ino_t
ino
,
struct
stat
*
attr
,
int
to_set
,
struct
fuse_file_info
*
fi
)
{
struct
fuse_file_info
*
fi
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"setattr handler
\n
"
);
fuse_log
(
FUSE_LOG_DEBUG
,
"setattr handler ino %u
\n
"
,
ino
);
auto
*
ud
=
udata
(
req
);
auto
*
inode
=
get_inode
(
ino
);
auto
*
inode
=
get_inode
(
ino
);
if
(
!
inode
)
{
if
(
!
inode
)
{
fuse_reply_err
(
req
,
ENOENT
);
fuse_reply_err
(
req
,
ENOENT
);
return
;
return
;
}
}
// TODO how to change attr?
int
rc
=
0
;
if
(
to_set
&
FUSE_SET_ATTR_SIZE
)
{
if
(
rc
)
{
off_t
new_size
=
attr
->
st_size
;
fuse_reply_err
(
req
,
rc
);
int
res
=
gkfs
::
syscall
::
gkfs_truncate
(
inode
->
path
,
new_size
);
if
(
res
<
0
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"setattr truncate failed on %s
\n
"
,
inode
->
path
.
c_str
());
fuse_reply_err
(
req
,
EIO
);
return
;
return
;
}
}
// TODO thats not in gkfs!!!
// Update cached stat so users see the new size
inode
->
st
.
st_size
=
new_size
;
}
if
(
to_set
&
FUSE_SET_ATTR_ATIME
)
inode
->
st
.
st_atim
=
attr
->
st_atim
;
inode
->
st
.
st_atim
=
attr
->
st_atim
;
inode
->
st
.
st_blksize
=
attr
->
st_blksize
;
if
(
to_set
&
FUSE_SET_ATTR_MTIME
)
inode
->
st
.
st_blocks
=
attr
->
st_blocks
;
inode
->
st
.
st_ctim
=
attr
->
st_ctim
;
inode
->
st
.
st_dev
=
attr
->
st_dev
;
inode
->
st
.
st_gid
=
attr
->
st_gid
;
inode
->
st
.
st_ino
=
attr
->
st_ino
;
inode
->
st
.
st_mode
=
attr
->
st_mode
;
inode
->
st
.
st_mtim
=
attr
->
st_mtim
;
inode
->
st
.
st_mtim
=
attr
->
st_mtim
;
inode
->
st
.
st_nlink
=
attr
->
st_nlink
;
inode
->
st
.
st_rdev
=
attr
->
st_rdev
;
// TODO because we cannot save the attributes in gekko, we just return the
inode
->
st
.
st_size
=
attr
->
st_size
;
// buffered results of stat
inode
->
st
.
st_uid
=
attr
->
st_uid
;
fuse_reply_attr
(
req
,
&
inode
->
st
,
ud
->
timeout
)
;
fuse_reply_attr
(
req
,
&
inode
->
st
,
1.0
)
;
return
;
}
}
static
void
static
void
...
@@ -319,6 +293,7 @@ static void
...
@@ -319,6 +293,7 @@ static void
create_handler
(
fuse_req_t
req
,
fuse_ino_t
parent
,
const
char
*
name
,
mode_t
mode
,
create_handler
(
fuse_req_t
req
,
fuse_ino_t
parent
,
const
char
*
name
,
mode_t
mode
,
struct
fuse_file_info
*
fi
)
{
struct
fuse_file_info
*
fi
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"create handler
\n
"
);
fuse_log
(
FUSE_LOG_DEBUG
,
"create handler
\n
"
);
auto
*
ud
=
udata
(
req
);
auto
*
parent_inode
=
get_inode
(
parent
);
auto
*
parent_inode
=
get_inode
(
parent
);
if
(
!
parent_inode
)
{
if
(
!
parent_inode
)
{
fuse_reply_err
(
req
,
ENOENT
);
fuse_reply_err
(
req
,
ENOENT
);
...
@@ -346,12 +321,13 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode,
...
@@ -346,12 +321,13 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode,
return
;
return
;
}
}
fuse_ino_t
ino
=
alloc_inode
(
path
);
fuse_ino_t
ino
=
alloc_inode
(
path
);
fuse_log
(
FUSE_LOG_DEBUG
,
"create new inode ino %i
\n
"
,
ino
);
ino_map
[
ino
].
st
=
st
;
ino_map
[
ino
].
st
=
st
;
fuse_entry_param
e
=
{};
fuse_entry_param
e
=
{};
e
.
ino
=
ino
;
e
.
ino
=
ino
;
e
.
attr
=
st
;
e
.
attr
=
st
;
e
.
attr_timeout
=
1.0
;
e
.
attr_timeout
=
ud
->
timeout
;
e
.
entry_timeout
=
1.0
;
e
.
entry_timeout
=
ud
->
timeout
;
fuse_reply_create
(
req
,
&
e
,
fi
);
fuse_reply_create
(
req
,
&
e
,
fi
);
}
}
...
@@ -427,7 +403,7 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
...
@@ -427,7 +403,7 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
}
}
size_t
bytes_filled
=
0
;
size_t
bytes_filled
=
0
;
off
_t
pos
=
off
;
size
_t
pos
=
off
;
while
(
pos
<
open_dir
->
size
())
{
while
(
pos
<
open_dir
->
size
())
{
auto
de
=
open_dir
->
getdent
(
pos
);
auto
de
=
open_dir
->
getdent
(
pos
);
...
@@ -456,83 +432,109 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
...
@@ -456,83 +432,109 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
free
(
buf
);
free
(
buf
);
}
}
/// releases file descriptor, not connected to lookup_count
static
void
static
void
init_handler
(
void
*
userdata
,
struct
fuse_conn_info
*
conn
)
{
release_handler
(
fuse_req_t
req
,
fuse_ino_t
ino
,
struct
fuse_file_info
*
fi
)
{
// userdata is GekkoFuse* if passed
fuse_log
(
FUSE_LOG_DEBUG
,
"release handler
\n
"
);
// optional: adjust conn->max_write, enable writeback etc.
auto
*
inode
=
get_inode
(
ino
);
}
if
(
!
inode
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"release here
\n
"
);
static
const
struct
fuse_lowlevel_ops
lo_oper
=
{
fuse_reply_err
(
req
,
ENOENT
);
.
init
=
init_handler
,
// lo_init,
return
;
//.destroy = lo_destroy,
}
.
lookup
=
lookup_handler
,
int
lc
=
gkfs
::
syscall
::
gkfs_close
(
fi
->
fh
);
//.forget = lo_forget,
if
(
lc
<
0
)
{
.
getattr
=
getattr_handler
,
fuse_log
(
FUSE_LOG_DEBUG
,
"release there
\n
"
);
.
setattr
=
setattr_handler
,
fuse_reply_err
(
req
,
1
);
//.readlink = lo_readlink,
return
;
//.mknod = lo_mknod,
}
//.mkdir = lo_mkdir,
fuse_log
(
FUSE_LOG_DEBUG
,
"release success
\n
"
);
//.unlink = lo_unlink,
fuse_reply_err
(
req
,
0
);
//.rmdir = lo_rmdir,
}
//.symlink = lo_symlink,
//.rename = lo_rename,
//.link = lo_link,
.
open
=
open_handler
,
.
read
=
read_handler
,
.
write
=
write_handler
,
//.flush = lo_flush,
//.release = lo_release,
//.fsync = lo_fsync,
.
opendir
=
opendir_handler
,
.
readdir
=
readdir_handler
,
//.releasedir = lo_releasedir,
//.fsyncdir = lo_fsyncdir,
//.statfs = lo_statfs,
//.setxattr = lo_setxattr,
//.getxattr = lo_getxattr,
//.listxattr = lo_listxattr,
//.removexattr = lo_removexattr,
// access
.
create
=
create_handler
,
// getlk
// setlk
// bmap
// ioctl
//.write_buf = lo_write_buf,
// poll
// retrive_reply
//.forget_multi = lo_forget_multi,
//.flock = lo_flock,
//.fallocate = lo_fallocate,
//.readdirplus = lo_readdirplus,
#ifdef HAVE_COPY_FILE_RANGE
//.copy_file_range = lo_copy_file_range,
#endif
//.lseek = lo_lseek,
//.tmpfile = lo_tmpfile,
#ifdef HAVE_STATX
//.statx = lo_statx,
#endif
};
int
/// decrement lookup count
main
(
int
argc
,
char
*
argv
[])
{
static
void
struct
fuse_args
args
=
FUSE_ARGS_INIT
(
argc
,
argv
);
forget_handler
(
fuse_req_t
req
,
fuse_ino_t
ino
,
uint64_t
nlookup
)
{
struct
fuse_session
*
se
;
fuse_log
(
FUSE_LOG_DEBUG
,
"forget handler
\n
"
);
struct
fuse_cmdline_opts
opts
;
struct
fuse_loop_config
*
config
;
struct
lo_data
lo
=
{.
debug
=
0
,
.
writeback
=
0
};
int
ret
=
-
1
;
/* Don't mask creation mode, kernel already did that */
auto
it
=
ino_map
.
find
(
ino
);
umask
(
0
);
// TODO do we need this and why?
if
(
it
==
ino_map
.
end
())
{
fuse_reply_none
(
req
);
return
;
}
Inode
&
inode
=
it
->
second
;
if
(
inode
.
lookup_count
>
nlookup
)
inode
.
lookup_count
-=
nlookup
;
else
inode
.
lookup_count
=
0
;
pthread_mutex_init
(
&
lo
.
mutex
,
NULL
);
if
(
inode
.
lookup_count
==
0
)
{
// && inode.open_count == 0
lo
.
root
.
next
=
lo
.
root
.
prev
=
&
lo
.
root
;
path_map
.
erase
(
inode
.
path
);
lo
.
root
.
fd
=
-
1
;
ino_map
.
erase
(
it
);
lo
.
cache
=
CACHE_NORMAL
;
fuse_log
(
FUSE_LOG_DEBUG
,
"reached lookup_count 0
\n
"
);
}
fuse_reply_none
(
req
);
// fuse_reply_err(req, 0);
}
// init gekkofs
static
void
flush_handler
(
fuse_req_t
req
,
fuse_ino_t
ino
,
struct
fuse_file_info
*
fi
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"flush handler
\n
"
);
auto
*
inode
=
get_inode
(
ino
);
if
(
!
inode
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"flush here
\n
"
);
fuse_reply_err
(
req
,
ENOENT
);
return
;
}
int
lc
=
gkfs
::
syscall
::
gkfs_fsync
(
fi
->
fh
);
if
(
lc
<
0
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"flush there
\n
"
);
fuse_reply_err
(
req
,
1
);
return
;
}
fuse_log
(
FUSE_LOG_DEBUG
,
"flush success
\n
"
);
fuse_reply_err
(
req
,
0
);
}
static
void
mkdir_handler
(
fuse_req_t
req
,
fuse_ino_t
parent
,
const
char
*
name
,
mode_t
mode
)
{
auto
*
ud
=
udata
(
req
);
auto
*
parent_inode
=
get_inode
(
parent
);
if
(
!
parent_inode
)
{
fuse_reply_err
(
req
,
ENOENT
);
return
;
}
std
::
string
path
=
parent_inode
->
path
+
name
;
int
rc
=
gkfs
::
syscall
::
gkfs_create
(
path
,
mode
|
S_IFDIR
);
if
(
rc
==
-
1
)
{
fuse_reply_err
(
req
,
1
);
return
;
}
struct
stat
st
;
int
sc
=
gkfs
::
syscall
::
gkfs_stat
(
path
,
&
st
);
if
(
sc
==
-
1
)
{
fuse_log
(
FUSE_LOG_DEBUG
,
"thats why its not allowed
\n
"
);
fuse_reply_err
(
req
,
1
);
return
;
}
fuse_ino_t
ino
=
alloc_inode
(
path
);
fuse_log
(
FUSE_LOG_DEBUG
,
"create new inode ino %i
\n
"
,
ino
);
ino_map
[
ino
].
st
=
st
;
fuse_entry_param
e
=
{};
e
.
ino
=
ino
;
e
.
attr
=
st
;
e
.
attr_timeout
=
ud
->
timeout
;
e
.
entry_timeout
=
ud
->
timeout
;
fuse_reply_entry
(
req
,
&
e
);
fuse_log
(
FUSE_LOG_DEBUG
,
"flush success
\n
"
);
fuse_reply_err
(
req
,
0
);
}
static
void
init_gekkofs
()
{
// TODO how to handle mount point
// TODO how to handle mount point
int
res
=
gkfs_init
();
int
res
=
gkfs_init
();
if
(
res
!=
0
)
{
if
(
res
!=
0
)
{
...
@@ -556,7 +558,107 @@ main(int argc, char* argv[]) {
...
@@ -556,7 +558,107 @@ main(int argc, char* argv[]) {
ino_map
[
FUSE_ROOT_ID
]
=
{
root_path
,
{},
1
};
ino_map
[
FUSE_ROOT_ID
]
=
{
root_path
,
{},
1
};
ino_map
[
FUSE_ROOT_ID
].
st
=
st
;
ino_map
[
FUSE_ROOT_ID
].
st
=
st
;
std
::
cout
<<
"root node allocated"
<<
std
::
endl
;
std
::
cout
<<
"root node allocated"
<<
std
::
endl
;
}
static
void
init_ll_ops
(
fuse_lowlevel_ops
*
ops
)
{
// file
ops
->
getattr
=
getattr_handler
;
ops
->
setattr
=
setattr_handler
;
ops
->
open
=
open_handler
;
ops
->
create
=
create_handler
;
// ops->unlink
ops
->
forget
=
forget_handler
;
// ops->forget_multi
// ops->readlink
// ops->mknod
// ops->symlink
// ops->rename
// ops->link
ops
->
flush
=
flush_handler
;
ops
->
release
=
release_handler
;
// ops->fsync
// ops->write_buf
// xattr
// ops->setxattr
// ops->getxattr
// ops->listxattr
// ops->removexattr
// directory
ops
->
lookup
=
lookup_handler
;
ops
->
mkdir
=
mkdir_handler
;
// ops->rmdir
ops
->
readdir
=
readdir_handler
;
ops
->
opendir
=
opendir_handler
;
// ops->releasedir
// ops->fsyncdir = nullptr;
// ops->readdirplus
// I/O
ops
->
write
=
write_handler
;
ops
->
read
=
read_handler
;
// permission
// ops->access
// misc
ops
->
init
=
init_handler
;
ops
->
destroy
=
destroy_handler
;
ops
->
statfs
=
nullptr
;
// ops->flock
// ops->getlk
// ops->setlk
// ops->bmap
// ops->ioctl
// ops->poll
// ops->retrive_reply
// ops->fallocate
// ops->copy_file_range
// ops->lseek
// ops->tmpfile
// ops->statx
}
void
err_cleanup1
(
fuse_cmdline_opts
opts
,
fuse_args
&
args
)
{
free
(
opts
.
mountpoint
);
fuse_opt_free_args
(
&
args
);
std
::
cout
<<
"# Resources released"
<<
std
::
endl
;
}
void
err_cleanup2
(
fuse_session
&
se
)
{
fuse_session_destroy
(
&
se
);
std
::
cout
<<
"# Fuse session destroyed"
<<
std
::
endl
;
}
void
err_cleanup3
(
fuse_session
&
se
)
{
fuse_remove_signal_handlers
(
&
se
);
std
::
cout
<<
"# Signal handlers removed"
<<
std
::
endl
;
}
int
main
(
int
argc
,
char
*
argv
[])
{
init_ll_ops
(
&
ll_ops
);
struct
fuse_args
args
=
FUSE_ARGS_INIT
(
argc
,
argv
);
struct
fuse_session
*
se
;
struct
fuse_cmdline_opts
opts
;
struct
fuse_loop_config
*
config
;
struct
u_data
ud
{};
ud
.
debug
=
0
;
ud
.
writeback
=
0
;
int
ret
=
-
1
;
/* Don't mask creation mode, kernel already did that */
umask
(
0
);
// TODO do we need this and why?
pthread_mutex_init
(
&
ud
.
mutex
,
NULL
);
ud
.
cache
=
CACHE_NORMAL
;
if
(
fuse_parse_cmdline
(
&
args
,
&
opts
)
!=
0
)
if
(
fuse_parse_cmdline
(
&
args
,
&
opts
)
!=
0
)
return
1
;
return
1
;
...
@@ -565,86 +667,69 @@ main(int argc, char* argv[]) {
...
@@ -565,86 +667,69 @@ main(int argc, char* argv[]) {
fuse_cmdline_help
();
fuse_cmdline_help
();
fuse_lowlevel_help
();
fuse_lowlevel_help
();
passthrough_ll_help
();
passthrough_ll_help
();
ret
=
0
;
err_cleanup1
(
opts
,
args
)
;
goto
err_out1
;
return
0
;
}
else
if
(
opts
.
show_version
)
{
}
else
if
(
opts
.
show_version
)
{
printf
(
"FUSE library version %s
\n
"
,
fuse_pkgversion
());
printf
(
"FUSE library version %s
\n
"
,
fuse_pkgversion
());
fuse_lowlevel_version
();
fuse_lowlevel_version
();
ret
=
0
;
ret
=
0
;
goto
err_out1
;
err_cleanup1
(
opts
,
args
);
return
0
;
}
}
if
(
opts
.
mountpoint
==
NULL
)
{
if
(
opts
.
mountpoint
==
NULL
)
{
printf
(
"usage: %s [options] <mountpoint>
\n
"
,
argv
[
0
]);
printf
(
"usage: %s [options] <mountpoint>
\n
"
,
argv
[
0
]);
printf
(
" %s --help
\n
"
,
argv
[
0
]);
printf
(
" %s --help
\n
"
,
argv
[
0
]);
ret
=
1
;
ret
=
1
;
goto
err_out1
;
err_cleanup1
(
opts
,
args
);
return
0
;
}
}
if
(
fuse_opt_parse
(
&
args
,
&
lo
,
lo_opts
,
NULL
)
==
-
1
)
if
(
fuse_opt_parse
(
&
args
,
&
ud
,
lo_opts
,
NULL
)
==
-
1
)
return
1
;
return
1
;
lo
.
debug
=
opts
.
debug
;
ud
.
debug
=
opts
.
debug
;
lo
.
root
.
refcount
=
2
;
if
(
!
ud
.
timeout_set
)
{
if
(
lo
.
source
)
{
switch
(
ud
.
cache
)
{
struct
stat
stat
;
int
res
;
res
=
lstat
(
lo
.
source
,
&
stat
);
if
(
res
==
-
1
)
{
fuse_log
(
FUSE_LOG_ERR
,
"failed to stat source (
\"
%s
\"
): %m
\n
"
,
lo
.
source
);
exit
(
1
);
}
if
(
!
S_ISDIR
(
stat
.
st_mode
))
{
fuse_log
(
FUSE_LOG_ERR
,
"source is not a directory
\n
"
);
exit
(
1
);
}
}
else
{
lo
.
source
=
strdup
(
"/"
);
if
(
!
lo
.
source
)
{
fuse_log
(
FUSE_LOG_ERR
,
"fuse: memory allocation failed
\n
"
);
exit
(
1
);
}
}
if
(
!
lo
.
timeout_set
)
{
switch
(
lo
.
cache
)
{
case
CACHE_NEVER
:
case
CACHE_NEVER
:
lo
.
timeout
=
0.0
;
ud
.
timeout
=
0.0
;
break
;
break
;
case
CACHE_NORMAL
:
case
CACHE_NORMAL
:
lo
.
timeout
=
1.0
;
ud
.
timeout
=
1.0
;
break
;
break
;
case
CACHE_ALWAYS
:
case
CACHE_ALWAYS
:
lo
.
timeout
=
86400.0
;
ud
.
timeout
=
86400.0
;
break
;
break
;
}
}
}
else
if
(
lo
.
timeout
<
0
)
{
}
else
if
(
ud
.
timeout
<
0
)
{
fuse_log
(
FUSE_LOG_ERR
,
"timeout is negative (%lf)
\n
"
,
lo
.
timeout
);
fuse_log
(
FUSE_LOG_ERR
,
"timeout is negative (%lf)
\n
"
,
ud
.
timeout
);
exit
(
1
);
exit
(
1
);
}
}
// TODO do we still want this? what do we want?
fuse_log
(
FUSE_LOG_DEBUG
,
"hier 1
\n
"
);
lo
.
root
.
fd
=
gkfs
::
syscall
::
gkfs_open
(
lo
.
source
,
755
,
O_PATH
);
if
(
lo
.
root
.
fd
==
-
1
)
{
fuse_log
(
FUSE_LOG_ERR
,
"open(
\"
%s
\"
, O_PATH): %m
\n
"
,
lo
.
source
);
exit
(
1
);
}
fuse_log
(
FUSE_LOG_DEBUG
,
"hier 2
\n
"
);
init_gekkofs
();
se
=
fuse_session_new
(
&
args
,
&
lo_oper
,
sizeof
(
lo_oper
),
&
lo
);
if
(
se
==
NULL
)
goto
err_out1
;
if
(
fuse_set_signal_handlers
(
se
)
!=
0
)
se
=
fuse_session_new
(
&
args
,
&
ll_ops
,
sizeof
(
ll_ops
),
&
ud
);
goto
err_out2
;
if
(
se
==
nullptr
)
{
err_cleanup1
(
opts
,
args
);
return
0
;
}
if
(
fuse_session_mount
(
se
,
opts
.
mountpoint
)
!=
0
)
if
(
fuse_set_signal_handlers
(
se
)
!=
0
)
{
goto
err_out3
;
err_cleanup2
(
*
se
);
err_cleanup1
(
opts
,
args
);
return
0
;
}
if
(
fuse_session_mount
(
se
,
opts
.
mountpoint
)
!=
0
)
{
err_cleanup3
(
*
se
);
err_cleanup2
(
*
se
);
err_cleanup1
(
opts
,
args
);
return
0
;
}
fuse_daemonize
(
opts
.
foreground
);
fuse_daemonize
(
opts
.
foreground
);
...
@@ -668,20 +753,5 @@ main(int argc, char* argv[]) {
...
@@ -668,20 +753,5 @@ main(int argc, char* argv[]) {
}
}
fuse_session_unmount
(
se
);
fuse_session_unmount
(
se
);
err_out3
:
fuse_log
(
FUSE_LOG_DEBUG
,
"hier 3
\n
"
);
fuse_remove_signal_handlers
(
se
);
err_out2
:
fuse_log
(
FUSE_LOG_DEBUG
,
"hier 4
\n
"
);
fuse_session_destroy
(
se
);
err_out1
:
fuse_log
(
FUSE_LOG_DEBUG
,
"hier 5
\n
"
);
free
(
opts
.
mountpoint
);
fuse_opt_free_args
(
&
args
);
if
(
lo
.
root
.
fd
>=
0
)
close
(
lo
.
root
.
fd
);
free
(
lo
.
source
);
return
ret
?
1
:
0
;
return
ret
?
1
:
0
;
}
}
tests/integration/CMakeLists.txt
View file @
37d6d804
...
@@ -143,6 +143,15 @@ if (GKFS_RENAME_SUPPORT)
...
@@ -143,6 +143,15 @@ if (GKFS_RENAME_SUPPORT)
)
)
endif
()
endif
()
if
(
GKFS_BUILD_FUSE
)
gkfs_add_python_test
(
NAME test_fuse_client
PYTHON_VERSION 3.6
WORKING_DIRECTORY
${
PROJECT_SOURCE_DIR
}
/tests/integration
SOURCE fuse/
)
endif
()
if
(
GKFS_INSTALL_TESTS
)
if
(
GKFS_INSTALL_TESTS
)
install
(
DIRECTORY harness
install
(
DIRECTORY harness
DESTINATION
${
CMAKE_INSTALL_DATAROOTDIR
}
/gkfs/tests/integration
DESTINATION
${
CMAKE_INSTALL_DATAROOTDIR
}
/gkfs/tests/integration
...
...
tests/integration/conftest.py
View file @
37d6d804
...
@@ -34,7 +34,7 @@ from pathlib import Path
...
@@ -34,7 +34,7 @@ from pathlib import Path
from
harness.logger
import
logger
,
initialize_logging
,
finalize_logging
from
harness.logger
import
logger
,
initialize_logging
,
finalize_logging
from
harness.cli
import
add_cli_options
,
set_default_log_formatter
from
harness.cli
import
add_cli_options
,
set_default_log_formatter
from
harness.workspace
import
Workspace
,
FileCreator
from
harness.workspace
import
Workspace
,
FileCreator
from
harness.gkfs
import
Daemon
,
Client
,
ClientLibc
,
Proxy
,
ShellClient
,
ShellClientLibc
,
FwdDaemon
,
FwdClient
,
ShellFwdClient
,
FwdDaemonCreator
,
FwdClientCreator
from
harness.gkfs
import
Daemon
,
Client
,
ClientLibc
,
Proxy
,
ShellClient
,
ShellClientLibc
,
FwdDaemon
,
FwdClient
,
ShellFwdClient
,
FwdDaemonCreator
,
FwdClientCreator
,
FuseClient
from
harness.reporter
import
report_test_status
,
report_test_headline
,
report_assertion_pass
from
harness.reporter
import
report_test_status
,
report_test_headline
,
report_assertion_pass
def
pytest_configure
(
config
):
def
pytest_configure
(
config
):
...
@@ -225,3 +225,15 @@ def gkfwd_client_factory(test_workspace):
...
@@ -225,3 +225,15 @@ def gkfwd_client_factory(test_workspace):
"""
"""
return
FwdClientCreator
(
test_workspace
)
return
FwdClientCreator
(
test_workspace
)
@pytest.fixture
def
fuse_client
(
test_workspace
):
"""
Sets up a gekkofs fuse client environment so that
operations (system calls, library calls, ...) can
be requested from a co-running daemon.
"""
fuse_client
=
FuseClient
(
test_workspace
)
yield
fuse_client
.
run
()
fuse_client
.
shutdown
()
tests/integration/fuse/test_basic_operations.py
0 → 100644
View file @
37d6d804
################################################################################
# Copyright 2018-2025, Barcelona Supercomputing Center (BSC), Spain #
# Copyright 2015-2025, Johannes Gutenberg Universitaet Mainz, Germany #
# #
# This software was partially supported by the #
# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). #
# #
# This software was partially supported by the #
# ADA-FS project under the SPPEXA project funded by the DFG. #
# #
# This file is part of GekkoFS. #
# #
# GekkoFS is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# GekkoFS is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with GekkoFS. If not, see <https://www.gnu.org/licenses/>. #
# #
# SPDX-License-Identifier: GPL-3.0-or-later #
################################################################################
import
harness
from
pathlib
import
Path
import
errno
import
stat
import
os
import
ctypes
import
sh
import
sys
import
pytest
import
time
from
harness.logger
import
logger
nonexisting
=
"
nonexisting
"
def
test_read
(
gkfs_daemon
,
fuse_client
):
file
=
gkfs_daemon
.
mountdir
/
"
file
"
file2
=
gkfs_daemon
.
mountdir
/
"
file2
"
dir
=
gkfs_daemon
.
mountdir
/
"
dir
"
sh
.
bash
(
"
-c
"
,
"
echo baum >
"
+
str
(
file
))
assert
sh
.
ls
(
fuse_client
.
mountdir
)
==
"
file
\n
"
assert
sh
.
cat
(
file
)
==
"
baum
\n
"
sh
.
touch
(
str
(
file2
))
assert
sh
.
wc
(
"
-c
"
,
str
(
file2
))
==
"
0
"
+
str
(
file2
)
+
"
\n
"
sh
.
truncate
(
"
-s
"
,
"
20
"
,
str
(
file2
))
assert
sh
.
wc
(
"
-c
"
,
str
(
file2
))
==
"
20
"
+
str
(
file2
)
+
"
\n
"
sh
.
mkdir
(
str
(
dir
))
tests/integration/harness/gkfs.py
View file @
37d6d804
...
@@ -74,6 +74,7 @@ gkfwd_client_log_level = 'all'
...
@@ -74,6 +74,7 @@ gkfwd_client_log_level = 'all'
gkfwd_client_log_syscall_filter
=
'
epoll_wait,epoll_create
'
gkfwd_client_log_syscall_filter
=
'
epoll_wait,epoll_create
'
gkfwd_daemon_active_log_pattern
=
r
'
Startup successful. Daemon is ready.
'
gkfwd_daemon_active_log_pattern
=
r
'
Startup successful. Daemon is ready.
'
gkfs_fuse_client
=
'
fuse_client
'
def
get_ip_addr
(
iface
):
def
get_ip_addr
(
iface
):
return
netifaces
.
ifaddresses
(
iface
)[
netifaces
.
AF_INET
][
0
][
'
addr
'
]
return
netifaces
.
ifaddresses
(
iface
)[
netifaces
.
AF_INET
][
0
][
'
addr
'
]
...
@@ -1637,3 +1638,78 @@ class ShellFwdClient:
...
@@ -1637,3 +1638,78 @@ class ShellFwdClient:
@property
@property
def
cwd
(
self
):
def
cwd
(
self
):
return
self
.
_workspace
.
twd
return
self
.
_workspace
.
twd
class
FuseClient
:
def
__init__
(
self
,
workspace
):
self
.
_workspace
=
workspace
#self._cmd = sh.Command("printenv", ["/usr/bin/"])#self._workspace.bindirs)
self
.
_cmd
=
sh
.
Command
(
gkfs_fuse_client
,
self
.
_workspace
.
bindirs
)
self
.
_env
=
os
.
environ
.
copy
()
self
.
_metadir
=
self
.
rootdir
libdirs
=
'
:
'
.
join
(
filter
(
None
,
[
os
.
environ
.
get
(
'
LD_LIBRARY_PATH
'
,
''
)]
+
[
str
(
p
)
for
p
in
self
.
_workspace
.
libdirs
]))
self
.
_patched_env
=
{
'
LD_LIBRARY_PATH
'
:
libdirs
,
'
GKFS_HOSTS_FILE
'
:
str
(
self
.
cwd
/
gkfs_hosts_file
),
'
LIBGKFS_HOSTS_FILE
'
:
str
(
self
.
cwd
/
gkfs_hosts_file
),
# TODO wtf why? see gkfs::env::HOSTS_FILE
}
self
.
_env
.
update
(
self
.
_patched_env
)
def
run
(
self
):
args
=
[
"
-f
"
,
"
-s
"
,
self
.
_workspace
.
mountdir
,
"
-o
"
,
"
auto_unmount
"
]
print
(
f
"
spawning fuse client
"
)
print
(
f
"
cmdline:
{
self
.
_cmd
}
"
+
"
"
.
join
(
map
(
str
,
args
)))
print
(
f
"
patched env:
\n
{
pformat
(
self
.
_patched_env
)
}
"
)
self
.
_proc
=
self
.
_cmd
(
args
,
_env
=
self
.
_env
,
_out
=
'
/dev/null
'
,
_err
=
'
/dev/null
'
,
_bg
=
True
,
_ok_code
=
list
(
range
(
0
,
256
))
)
print
(
f
"
fuse client process spawned (PID=
{
self
.
_proc
.
pid
}
)
"
)
time
.
sleep
(
2
)
# give fuse time to register mount
return
self
def
shutdown
(
self
):
try
:
self
.
_proc
.
terminate
()
err
=
self
.
_proc
.
wait
()
except
sh
.
SignalException_SIGTERM
:
pass
except
Exception
:
raise
@property
def
cwd
(
self
):
return
self
.
_workspace
.
twd
@property
def
rootdir
(
self
):
return
self
.
_workspace
.
rootdir
@property
def
mountdir
(
self
):
return
self
.
_workspace
.
mountdir
@property
def
bindirs
(
self
):
return
self
.
_workspace
.
bindirs
@property
def
logdir
(
self
):
return
self
.
_workspace
.
logdir
@property
def
env
(
self
):
return
self
.
_env