4040import re
4141import stat
4242import sys
43+ import time
4344
4445try :
4546 from time import time_ns
6061except 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
100124def __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+
143178def __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
0 commit comments