Skip to content

Commit 2d9d38a

Browse files
committed
fixed "final" bug, now passing both posix and stress tests
2 parents 9bd65b8 + a0d8d07 commit 2d9d38a

File tree

9 files changed

+279
-35
lines changed

9 files changed

+279
-35
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,4 @@ test_suite
9595
test_mount
9696
test_logs
9797
fsx-linux*
98+
screenshots/

makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,16 @@ destroy_childfs:
7373
python3 -c 'import tests; tests.lib.quick_cli_destroy_childfs()'
7474

7575
test:
76+
make test_posix
77+
make test_stress
78+
79+
test_posix:
7680
# make docu
7781
-rm tests/__pycache__/*.pyc
7882
-rm tests/lib/__pycache__/*.pyc
7983
# USAGE: make test T="-T chmod/01.t -T chmod/02.t"
8084
# REFERENCE TEST WITH EXT4: make test T="-M ext4"
8185
pytest $(T)
86+
87+
test_stress:
88+
tests/scripts/fsx

src/loggedfs/core.py

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import re
4141
import stat
4242
import sys
43+
import time
4344

4445
try:
4546
from time import time_ns
@@ -60,6 +61,29 @@
6061
except ImportError:
6162
fuse_features = {}
6263

64+
65+
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
66+
# LOGGING: Support nano-second timestamps
67+
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
68+
69+
class _LogRecord_ns_(logging.LogRecord):
70+
def __init__(self, *args, **kwargs):
71+
self.created_ns = time_ns() # Fetch precise timestamp
72+
super().__init__(*args, **kwargs)
73+
74+
class _Formatter_ns_(logging.Formatter):
75+
default_nsec_format = '%s,%09d'
76+
def formatTime(self, record, datefmt=None):
77+
if datefmt is not None: # Do not handle custom formats here ...
78+
return super().formatTime(record, datefmt) # ... leave to original implementation
79+
ct = self.converter(record.created_ns / 1e9)
80+
t = time.strftime(self.default_time_format, ct)
81+
s = self.default_nsec_format % (t, record.created_ns - (record.created_ns // 10**9) * 10**9)
82+
return s
83+
84+
logging.setLogRecordFactory(_LogRecord_ns_)
85+
86+
6387
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6488
# ROUTINES
6589
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -92,9 +116,9 @@ def __format_args__(args_list, kwargs_dict, items_list, format_func):
92116

93117
for item in items_list:
94118
if isinstance(item, int):
95-
args_list[item] = format_func(args_list[item])
119+
args_list[item] = format_func(args_list[item] if item < len(args_list) else -10)
96120
elif isinstance(item, str):
97-
kwargs_dict[item] = format_func(kwargs_dict[item])
121+
kwargs_dict[item] = format_func(kwargs_dict.get(item, -11))
98122

99123

100124
def __get_abs_path__(args_list, kwargs_dict, path_item_list, abs_func):
@@ -140,10 +164,22 @@ def __get_user_name_from_uid__(uid):
140164
return '[uid: omitted argument]'
141165

142166

167+
def __get_fh_from_fip__(fip):
168+
169+
if fip is None:
170+
return -1
171+
if not hasattr(fip, 'fh'):
172+
return -2
173+
if not isinstance(fip.fh, int):
174+
return -3
175+
return fip.fh
176+
177+
143178
def __log__(
144179
format_pattern = '',
145180
abs_path_fields = None, length_fields = None,
146181
uid_fields = None, gid_fields = None,
182+
fip_fields = None,
147183
path_filter_field = 0
148184
):
149185

@@ -155,6 +191,8 @@ def __log__(
155191
uid_fields = []
156192
if gid_fields is None:
157193
gid_fields = []
194+
if fip_fields is None:
195+
fip_fields = []
158196

159197
def wrapper(func):
160198

@@ -181,7 +219,8 @@ def wrapped(self, *func_args, **func_kwargs):
181219
(abs_path_fields, lambda x: self._full_path(x)),
182220
(length_fields, lambda x: len(x)),
183221
(uid_fields, lambda x: '%s(%d)' % (__get_user_name_from_uid__(x), x)),
184-
(gid_fields, lambda x: '%s(%d)' % (__get_group_name_from_gid__(x), x))
222+
(gid_fields, lambda x: '%s(%d)' % (__get_group_name_from_gid__(x), x)),
223+
(fip_fields, lambda x: '%d' % __get_fh_from_fip__(x))
185224
]:
186225
__format_args__(func_args_f, func_kwargs_f, field_list, format_func)
187226

@@ -346,8 +385,8 @@ def __init__(self,
346385

347386
def _init_logger(self, log_enabled, log_file, log_syslog, log_printprocessname):
348387

349-
log_formater = logging.Formatter('%(asctime)s (%(name)s) %(message)s')
350-
log_formater_short = logging.Formatter('%(message)s')
388+
log_formater = _Formatter_ns_('%(asctime)s (%(name)s) %(message)s')
389+
log_formater_short = _Formatter_ns_('%(message)s')
351390

352391
self._log_printprocessname = bool(log_printprocessname)
353392

@@ -461,7 +500,7 @@ def destroy(self, path):
461500
os.close(self.root_path_fd)
462501

463502

464-
@__log__(format_pattern = '{0}', abs_path_fields = [0])
503+
@__log__(format_pattern = '{0} (fh={1})', abs_path_fields = [0], fip_fields = [1])
465504
def getattr(self, path, fip):
466505

467506
if not fip:
@@ -492,9 +531,11 @@ def flush(self, path, fip):
492531

493532

494533
# Ugly HACK, addressing https://github.com/fusepy/fusepy/issues/81 ????????
534+
@__log__(format_pattern = '{0} (fh={2})', abs_path_fields = [0], fip_fields = [2])
495535
def fsync(self, path, datasync, fip):
496536

497-
raise FuseOSError(errno.ENOSYS)
537+
# raise FuseOSError(errno.ENOSYS)
538+
return 0
498539

499540

500541
@__log__(format_pattern = '{0}')
@@ -563,15 +604,15 @@ def mknod(self, path, mode, dev):
563604
os.chmod(rel_path, mode, dir_fd = self.root_path_fd) # HACK should be lchmod, which is only available on BSD
564605

565606

566-
@__log__(format_pattern = '({1}) {0}', abs_path_fields = [0])
607+
@__log__(format_pattern = '({1}) {0} (fh={1})', abs_path_fields = [0], fip_fields = [1])
567608
def open(self, path, fip):
568609

569610
fip.fh = os.open(self._rel_path(path), fip.flags, dir_fd = self.root_path_fd)
570611

571612
return 0 # Must return handle or zero # TODO ?
572613

573614

574-
@__log__(format_pattern = '{1} bytes from {0} at offset {2}', abs_path_fields = [0])
615+
@__log__(format_pattern = '{1} bytes from {0} at offset {2} (fh={3})', abs_path_fields = [0], fip_fields = [3])
575616
def read(self, path, length, offset, fip):
576617

577618
# ret is a bytestring!
@@ -607,9 +648,11 @@ def readlink(self, path):
607648

608649

609650
# Ugly HACK, addressing https://github.com/fusepy/fusepy/issues/81
651+
@__log__(format_pattern = '{0} (fh={1})', abs_path_fields = [0], fip_fields = [1])
610652
def release(self, path, fip):
611653

612-
raise FuseOSError(errno.ENOSYS)
654+
# raise FuseOSError(errno.ENOSYS)
655+
os.close(fip.fh)
613656

614657

615658
@__log__(format_pattern = '{0} to {1}', abs_path_fields = [0, 1])
@@ -648,24 +691,12 @@ def symlink(self, target_path, source_path):
648691
os.chown(target_rel_path, uid, gid, dir_fd = self.root_path_fd, follow_symlinks = False)
649692

650693

651-
@__log__(format_pattern = '{0} to {1} bytes', abs_path_fields = [0])
694+
@__log__(format_pattern = '{0} to {1} bytes (fh={fip})', abs_path_fields = [0], fip_fields = ['fip'])
652695
def truncate(self, path, length, fip = None):
653696

654697
if fip is None:
655698

656-
try:
657-
fh = os.open(
658-
self._rel_path(path),
659-
flags = os.O_WRONLY | os.O_TRUNC,
660-
dir_fd = self.root_path_fd
661-
)
662-
except FileNotFoundError:
663-
raise FuseOSError(errno.ENOENT)
664-
665-
ret = os.ftruncate(fh, length)
666-
os.close(fh)
667-
668-
return ret
699+
os.truncate(self._rel_path(path), length)
669700

670701
else:
671702

@@ -704,15 +735,11 @@ def _fix_time_(atime, mtime):
704735
os.utime(relpath, times = times, dir_fd = self.root_path_fd, follow_symlinks = False)
705736

706737

707-
@__log__(format_pattern = '{1} bytes to {0} at offset {2}', abs_path_fields = [0], length_fields = [1])
738+
@__log__(format_pattern = '{1} bytes to {0} at offset {2} (fh={3})', abs_path_fields = [0], length_fields = [1], fip_fields = [3])
708739
def write(self, path, buf, offset, fip):
709740

710741
# buf is a bytestring!
711742

712743
res = os.pwrite(fip.fh, buf, offset)
713744

714-
#os.fdatasync(fip.fh)
715-
#os.fdopen(fip.fh).flush()
716-
#os.fsync(fip.fh)
717-
718745
return res

tests/lib/base.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,19 @@ def init_d_childfs(self):
137137
open(self.fstest_log_abs_path, 'a').close() # HACK create empty fstest log file
138138

139139

140+
def assert_childfs_mountpoint(self):
141+
142+
assert is_path_mountpoint(self.mount_child_abs_path)
143+
144+
145+
def assert_parentfs_mountpoint(self):
146+
147+
if self.travis:
148+
return
149+
150+
assert is_path_mountpoint(self.mount_parent_abs_path)
151+
152+
140153
def destroy_a_childfs(self):
141154

142155
if not self.fs_type == TEST_FS_LOGGEDFS:

tests/lib/climount.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,13 @@ def quick_cli_destroy_parentfs():
7878

7979
fs = fstest_base_class()
8080
fs.init_a_members()
81+
fs.assert_parentfs_mountpoint()
8182
fs.destroy_b_parentfs()
8283

8384

8485
def quick_cli_destroy_childfs():
8586

8687
fs = fstest_base_class()
8788
fs.init_a_members()
89+
fs.assert_childfs_mountpoint()
8890
fs.destroy_a_childfs()

tests/lib/mount.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ def mount_loggedfs_python(in_abs_path, logfile, cfgfile, sudo = False):
7575
os.path.join(os.environ['VIRTUAL_ENV'], 'bin', 'loggedfs'),
7676
'-l', logfile,
7777
'-c', cfgfile,
78-
'-p', in_abs_path,
79-
'-s'
78+
'-p',
79+
'-s',
80+
in_abs_path,
8081
],
8182
return_output = True, sudo = sudo, sudo_env = sudo
8283
)

tests/lib/procio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def download_file(in_url):
176176

177177
def dump_yaml(filename, data):
178178

179-
f = open(filename, 'w+')
179+
f = open(filename, 'w')
180180
dump(data, f, Dumper = Dumper, default_flow_style = False)
181181
f.close()
182182

@@ -199,6 +199,6 @@ def read_file(filename):
199199

200200
def write_file(filename, data):
201201

202-
f = open(filename, 'w+')
202+
f = open(filename, 'w')
203203
f.write(data)
204204
f.close()

fsx renamed to tests/scripts/fsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Filesystem monitoring with Fuse and Python
55
# https://github.com/pleiszenburg/loggedfs-python
66
#
7-
# fsx: Development script for running fsx-linux
7+
# tests/scripts/fsx: Development script for running fsx-linux
88
#
99
# Copyright (C) 2017-2018 Sebastian M. Ernst <[email protected]>
1010
#
@@ -25,7 +25,7 @@ FN=iotest
2525

2626
make init
2727
cd tests/test_mount/test_child/
28-
fsx-linux -d -d -N 100 $FN -P ../../test_logs
28+
fsx-linux -d -d -N 1000 $FN -P ../../test_logs
2929
cd ../../..
3030
make destroy_childfs
3131
cp -a tests/test_mount/test_child/$FN tests/test_logs/$FN.fsxactual

0 commit comments

Comments
 (0)