0
# Exception Handling
1
2
Error handling mechanisms and logging utilities for FUSE filesystem implementations. These provide structured error reporting and debugging capabilities for filesystem operations.
3
4
## Capabilities
5
6
### FuseOSError Exception
7
8
The primary exception class for FUSE-related errors, providing errno-based error reporting that integrates with the FUSE kernel interface.
9
10
```python { .api }
11
class FuseOSError(OSError):
12
"""Exception class for FUSE-related OS errors."""
13
14
def __init__(self, errno):
15
"""
16
Create a FUSE OS error.
17
18
Args:
19
errno: Error number (from errno module constants)
20
"""
21
```
22
23
#### Usage Examples
24
25
```python
26
import errno
27
from mfusepy import FuseOSError, Operations
28
29
class MyFilesystem(Operations):
30
def getattr(self, path, fh=None):
31
if path not in self.files:
32
# File not found
33
raise FuseOSError(errno.ENOENT)
34
35
if not self._has_permission(path, 'read'):
36
# Permission denied
37
raise FuseOSError(errno.EACCES)
38
39
return self.files[path]
40
41
def mkdir(self, path, mode):
42
if path in self.files:
43
# File already exists
44
raise FuseOSError(errno.EEXIST)
45
46
parent = os.path.dirname(path)
47
if parent not in self.files:
48
# Parent directory doesn't exist
49
raise FuseOSError(errno.ENOENT)
50
51
# Create directory
52
self.files[path] = {'st_mode': mode | 0x4000, 'st_nlink': 2}
53
return 0
54
```
55
56
#### Common Error Codes
57
58
```python
59
import errno
60
61
# File system errors commonly used with FuseOSError:
62
errno.ENOENT # No such file or directory
63
errno.EACCES # Permission denied
64
errno.EEXIST # File exists
65
errno.EISDIR # Is a directory
66
errno.ENOTDIR # Not a directory
67
errno.ENOTEMPTY # Directory not empty
68
errno.ENOSPC # No space left on device
69
errno.EROFS # Read-only file system
70
errno.EIO # I/O error
71
errno.EFBIG # File too large
72
errno.ENOSYS # Function not implemented
73
errno.EINVAL # Invalid argument
74
```
75
76
### LoggingMixIn
77
78
A mixin class that automatically logs all filesystem operation calls and their results, useful for debugging and monitoring filesystem implementations.
79
80
```python { .api }
81
class LoggingMixIn:
82
"""Mixin class for logging all Operation callbacks."""
83
```
84
85
#### Usage Example
86
87
```python
88
from mfusepy import Operations, LoggingMixIn, FUSE
89
import logging
90
91
# Configure logging
92
logging.basicConfig(level=logging.DEBUG)
93
94
class MyFS(LoggingMixIn, Operations):
95
"""Filesystem with automatic logging."""
96
97
def __init__(self):
98
self.files = {'/': {'st_mode': 0o755 | 0x4000, 'st_nlink': 2}}
99
100
def getattr(self, path, fh=None):
101
if path in self.files:
102
return self.files[path]
103
raise FuseOSError(errno.ENOENT)
104
105
def readdir(self, path, fh):
106
return ['.', '..'] + [name[1:] for name in self.files if name != '/']
107
108
# Mount with logging - all operations will be automatically logged
109
filesystem = MyFS()
110
fuse = FUSE(filesystem, '/mnt/myfs', foreground=True)
111
112
# Log output will show:
113
# DEBUG:fuse.log-mixin:-> getattr / None
114
# DEBUG:fuse.log-mixin:<- getattr {'st_mode': 16877, 'st_nlink': 2}
115
```
116
117
## Decorators
118
119
Utility decorators for method validation and logging in filesystem implementations.
120
121
```python { .api }
122
from typing import Callable
123
124
def log_callback(method: Callable) -> Callable:
125
"""
126
Decorator that adds log output for the decorated method.
127
128
Args:
129
method: Method to decorate
130
131
Returns:
132
Decorated method with logging
133
"""
134
135
def overrides(parent_class: type) -> Callable:
136
"""
137
Decorator that checks method exists in parent class.
138
139
Args:
140
parent_class: Parent class to check against
141
142
Returns:
143
Decorator function
144
"""
145
```
146
147
### Usage Examples
148
149
```python
150
from mfusepy import Operations, log_callback, overrides
151
152
class MyFS(Operations):
153
@overrides(Operations)
154
@log_callback
155
def getattr(self, path, fh=None):
156
# Implementation with automatic validation and logging
157
return {'st_mode': 0o644, 'st_nlink': 1, 'st_size': 0}
158
159
@log_callback
160
def custom_method(self):
161
# Custom method with logging
162
return "custom result"
163
```
164
165
## Error Handling Patterns
166
167
### Basic Error Handling
168
169
```python
170
from mfusepy import Operations, FuseOSError
171
import errno
172
import os
173
174
class RobustFS(Operations):
175
def getattr(self, path, fh=None):
176
try:
177
# Attempt to get file attributes
178
stat_result = self._get_file_stats(path)
179
return {
180
'st_mode': stat_result.mode,
181
'st_size': stat_result.size,
182
'st_nlink': stat_result.nlink,
183
}
184
except FileNotFoundError:
185
raise FuseOSError(errno.ENOENT)
186
except PermissionError:
187
raise FuseOSError(errno.EACCES)
188
except Exception as e:
189
# Log unexpected errors and return generic I/O error
190
self.logger.error(f"Unexpected error in getattr({path}): {e}")
191
raise FuseOSError(errno.EIO)
192
```
193
194
### Validation and Sanitization
195
196
```python
197
class SecureFS(Operations):
198
def _validate_path(self, path):
199
"""Validate and sanitize file paths."""
200
if not path.startswith('/'):
201
raise FuseOSError(errno.EINVAL)
202
203
# Prevent directory traversal
204
normalized = os.path.normpath(path)
205
if '..' in normalized or normalized != path:
206
raise FuseOSError(errno.EACCES)
207
208
return normalized
209
210
def _check_permissions(self, path, operation):
211
"""Check if operation is permitted on path."""
212
if not self._has_permission(path, operation):
213
raise FuseOSError(errno.EACCES)
214
215
def read(self, path, size, offset, fh):
216
path = self._validate_path(path)
217
self._check_permissions(path, 'read')
218
219
try:
220
return self._read_file_data(path, size, offset)
221
except IOError:
222
raise FuseOSError(errno.EIO)
223
```
224
225
### Resource Management
226
227
```python
228
class ResourceFS(Operations):
229
def __init__(self):
230
self.open_files = {}
231
self.max_open_files = 1000
232
233
def open(self, path, flags):
234
if len(self.open_files) >= self.max_open_files:
235
raise FuseOSError(errno.EMFILE) # Too many open files
236
237
try:
238
fh = self._allocate_file_handle()
239
self.open_files[fh] = self._open_file(path, flags)
240
return fh
241
except IOError:
242
raise FuseOSError(errno.EIO)
243
244
def release(self, path, fh):
245
try:
246
if fh in self.open_files:
247
self.open_files[fh].close()
248
del self.open_files[fh]
249
return 0
250
except Exception as e:
251
self.logger.warning(f"Error closing file handle {fh}: {e}")
252
return 0 # Don't propagate errors from release
253
```
254
255
## Logging Configuration
256
257
```python
258
import logging
259
from mfusepy import LoggingMixIn, Operations, FUSE
260
261
# Configure different log levels for different components
262
logging.getLogger('fuse').setLevel(logging.INFO)
263
logging.getLogger('fuse.log-mixin').setLevel(logging.DEBUG)
264
265
# Custom formatter for FUSE logs
266
formatter = logging.Formatter(
267
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
268
)
269
270
handler = logging.StreamHandler()
271
handler.setFormatter(formatter)
272
273
logger = logging.getLogger('fuse')
274
logger.addHandler(handler)
275
276
class MyFS(LoggingMixIn, Operations):
277
# Your filesystem implementation
278
pass
279
```