or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

archives.mdindex.mdmaintenance.mdmount.mdrepository.mdutilities.md

mount.mddocs/

0

# Mount and Access

1

2

Mounting archives as filesystems for browsing and selective file restoration, providing direct access to backup contents without full extraction.

3

4

```python

5

import subprocess

6

import os

7

```

8

9

## Capabilities

10

11

### Archive Mounting

12

13

Mount archives as read-only filesystems for browsing and selective file access.

14

15

```python { .api }

16

def mount_archive(repo_path: str, mount_point: str, archive_name: str = None,

17

foreground: bool = False, numeric_owner: bool = False,

18

strip_components: int = None, **options) -> None:

19

"""

20

Mount archive as filesystem.

21

22

Args:

23

repo_path: Path to repository

24

mount_point: Directory to mount at

25

archive_name: Specific archive to mount (optional, mounts all if not specified)

26

foreground: Run in foreground (default: background daemon)

27

numeric_owner: Show numeric user/group IDs

28

strip_components: Strip N leading path components

29

**options: Additional options like first, last, glob_archives

30

"""

31

cmd = ['borg', 'mount']

32

if foreground:

33

cmd.append('--foreground')

34

if numeric_owner:

35

cmd.append('--numeric-owner')

36

if strip_components:

37

cmd.extend(['--strip-components', str(strip_components)])

38

if options.get('first'):

39

cmd.extend(['--first', str(options['first'])])

40

if options.get('last'):

41

cmd.extend(['--last', str(options['last'])])

42

if options.get('glob_archives'):

43

cmd.extend(['--glob-archives', options['glob_archives']])

44

45

if archive_name:

46

cmd.append(f'{repo_path}::{archive_name}')

47

else:

48

cmd.append(repo_path)

49

cmd.append(mount_point)

50

51

subprocess.run(cmd, check=True)

52

53

def unmount_archive(mount_point: str) -> None:

54

"""

55

Unmount archive filesystem.

56

57

Args:

58

mount_point: Directory to unmount

59

"""

60

cmd = ['borg', 'umount', mount_point]

61

subprocess.run(cmd, check=True)

62

```

63

64

Usage example:

65

```python

66

import subprocess

67

import os

68

69

# Create mount point

70

os.makedirs('/mnt/backup', exist_ok=True)

71

72

# Mount specific archive

73

subprocess.run([

74

'borg', 'mount', '/backup/repo::documents-2023-12-01', '/mnt/backup'

75

], check=True)

76

77

# Browse mounted archive (now you can use regular file operations)

78

# files = os.listdir('/mnt/backup')

79

# with open('/mnt/backup/home/user/important.txt', 'r') as f:

80

# content = f.read()

81

82

# Unmount when done

83

subprocess.run(['borg', 'umount', '/mnt/backup'], check=True)

84

85

# Mount all archives (creates subdirectories for each archive)

86

subprocess.run(['borg', 'mount', '/backup/repo', '/mnt/all-backups'], check=True)

87

```

88

89

### FUSE Filesystem Features

90

91

Access mounted archives with full filesystem features including browsing, copying, and searching.

92

93

```python { .api }

94

def mount_with_options(repo_path: str, mount_point: str,

95

archive_pattern: str = None, versions_view: bool = False,

96

allow_damaged_files: bool = False) -> None:

97

"""

98

Mount with advanced FUSE options.

99

100

Args:

101

repo_path: Path to repository

102

mount_point: Directory to mount at

103

archive_pattern: Glob pattern for archive selection

104

versions_view: Enable versions view (shows all versions of files)

105

allow_damaged_files: Allow access to damaged files

106

"""

107

cmd = ['borg', 'mount']

108

if archive_pattern:

109

cmd.extend(['--glob-archives', archive_pattern])

110

if versions_view:

111

cmd.append('--versions-view')

112

if allow_damaged_files:

113

cmd.append('--consider-damaged-chunks')

114

115

cmd.extend([repo_path, mount_point])

116

subprocess.run(cmd, check=True)

117

```

118

119

Usage example:

120

```python

121

import subprocess

122

import os

123

import shutil

124

125

# Mount with pattern matching

126

subprocess.run([

127

'borg', 'mount', '--glob-archives=documents-*',

128

'/backup/repo', '/mnt/documents'

129

], check=True)

130

131

# Copy specific files from mounted archive

132

source_path = '/mnt/documents/documents-2023-12-01/home/user/important.txt'

133

if os.path.exists(source_path):

134

shutil.copy2(source_path, '/restore/important.txt')

135

136

# Search for files in mounted archive

137

def find_files(mount_path, pattern):

138

"""Find files matching pattern in mounted archive"""

139

matches = []

140

for root, dirs, files in os.walk(mount_path):

141

for file in files:

142

if pattern in file:

143

matches.append(os.path.join(root, file))

144

return matches

145

146

# Find all PDF files

147

pdf_files = find_files('/mnt/documents', '.pdf')

148

149

# Unmount

150

subprocess.run(['borg', 'umount', '/mnt/documents'], check=True)

151

```

152

153

### Repository Web Interface

154

155

Serve repository contents via HTTP for web-based browsing (if borg-web or similar tools are available).

156

157

```python { .api }

158

def serve_web_interface(repo_path: str, host: str = '127.0.0.1',

159

port: int = 8080, readonly: bool = True) -> None:

160

"""

161

Serve repository via web interface (requires additional tools).

162

163

Args:

164

repo_path: Path to repository

165

host: Host interface to bind to

166

port: Port to serve on

167

readonly: Serve in read-only mode

168

169

Note: This requires additional tools like borg-web or borgmatic-web

170

"""

171

# This is a conceptual example - actual implementation depends on web interface tool

172

cmd = ['borg-web', '--repo', repo_path, '--host', host, '--port', str(port)]

173

if readonly:

174

cmd.append('--readonly')

175

subprocess.run(cmd, check=True)

176

```

177

178

### Archive Browsing Utilities

179

180

Utility functions for programmatically browsing mounted archives.

181

182

```python { .api }

183

def browse_archive(mount_point: str, callback_func = None) -> dict:

184

"""

185

Programmatically browse mounted archive contents.

186

187

Args:

188

mount_point: Path to mounted archive

189

callback_func: Optional callback for each file/directory

190

191

Returns:

192

Dictionary with archive structure and file information

193

"""

194

archive_contents = {}

195

196

for root, dirs, files in os.walk(mount_point):

197

rel_root = os.path.relpath(root, mount_point)

198

archive_contents[rel_root] = {

199

'directories': dirs,

200

'files': []

201

}

202

203

for file in files:

204

file_path = os.path.join(root, file)

205

try:

206

stat = os.stat(file_path)

207

file_info = {

208

'name': file,

209

'size': stat.st_size,

210

'mtime': stat.st_mtime,

211

'mode': stat.st_mode

212

}

213

archive_contents[rel_root]['files'].append(file_info)

214

215

if callback_func:

216

callback_func(file_path, file_info)

217

except OSError:

218

# Handle potential issues with damaged files

219

pass

220

221

return archive_contents

222

223

def extract_files_from_mount(mount_point: str, file_patterns: list,

224

destination: str) -> list:

225

"""

226

Extract specific files from mounted archive.

227

228

Args:

229

mount_point: Path to mounted archive

230

file_patterns: List of file patterns to extract

231

destination: Destination directory for extracted files

232

233

Returns:

234

List of successfully extracted file paths

235

"""

236

import fnmatch

237

import shutil

238

239

extracted_files = []

240

os.makedirs(destination, exist_ok=True)

241

242

for root, dirs, files in os.walk(mount_point):

243

for file in files:

244

file_path = os.path.join(root, file)

245

rel_path = os.path.relpath(file_path, mount_point)

246

247

# Check if file matches any pattern

248

for pattern in file_patterns:

249

if fnmatch.fnmatch(rel_path, pattern) or fnmatch.fnmatch(file, pattern):

250

dest_path = os.path.join(destination, rel_path)

251

dest_dir = os.path.dirname(dest_path)

252

os.makedirs(dest_dir, exist_ok=True)

253

254

try:

255

shutil.copy2(file_path, dest_path)

256

extracted_files.append(dest_path)

257

except OSError as e:

258

print(f"Failed to extract {rel_path}: {e}")

259

break

260

261

return extracted_files

262

```

263

264

Usage example:

265

```python

266

import subprocess

267

import os

268

269

# Mount archive

270

subprocess.run(['borg', 'mount', '/backup/repo::backup-2023-12-01', '/mnt/backup'], check=True)

271

272

try:

273

# Browse archive contents

274

def file_callback(path, info):

275

if info['size'] > 1000000: # Files larger than 1MB

276

print(f"Large file: {path} ({info['size']} bytes)")

277

278

contents = browse_archive('/mnt/backup', file_callback)

279

280

# Extract specific file types

281

extracted = extract_files_from_mount('/mnt/backup', ['*.pdf', '*.doc*'], '/restore/documents')

282

print(f"Extracted {len(extracted)} files")

283

284

finally:

285

# Always unmount

286

subprocess.run(['borg', 'umount', '/mnt/backup'], check=True)

287

```

288

289

## Types

290

291

```python { .api }

292

class MountOptions:

293

"""Mount operation options"""

294

def __init__(self):

295

self.repository: str # Repository path

296

self.archive: str # Archive name (optional)

297

self.mount_point: str # Mount point directory

298

self.foreground: bool # Run in foreground

299

self.numeric_owner: bool # Show numeric IDs

300

self.strip_components: int # Strip path components

301

302

class FileInfo:

303

"""File information from mounted archive"""

304

def __init__(self):

305

self.name: str # File name

306

self.path: str # Full path

307

self.size: int # File size in bytes

308

self.mtime: float # Modification time (timestamp)

309

self.mode: int # File mode/permissions

310

self.is_directory: bool # Is directory flag

311

312

class ArchiveStructure:

313

"""Archive directory structure"""

314

def __init__(self):

315

self.directories: dict # Directory tree structure

316

self.files: list # List of all files

317

self.total_size: int # Total size of all files

318

self.file_count: int # Total number of files

319

self.directory_count: int # Total number of directories

320

```