or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compression.mdcore-io.mdfilesystem.mdhashing.mdindex.mdutilities.md

filesystem.mddocs/

0

# File System Operations

1

2

This document covers Okio's cross-platform file system APIs, including path manipulation, file I/O operations, directory management, and metadata handling.

3

4

## Path

5

6

Path represents a hierarchical address on a file system, supporting both UNIX and Windows path conventions.

7

8

### Path Creation and Properties

9

10

```kotlin { .api }

11

expect class Path {

12

// Core properties

13

val root: Path? // Root path (null for relative paths)

14

val segments: List<String> // Path components

15

val isAbsolute: Boolean // True if path is absolute

16

val isRelative: Boolean // True if path is relative

17

val volumeLetter: Char? // Windows volume letter (e.g., 'C')

18

19

// Path components

20

val name: String // Last segment (file/directory name)

21

val parent: Path? // Parent path (null for roots)

22

val isRoot: Boolean // True if this equals root

23

24

// Path operations

25

operator fun div(child: String): Path

26

operator fun div(child: ByteString): Path

27

operator fun div(child: Path): Path

28

29

fun resolve(child: String, normalize: Boolean = false): Path

30

fun resolve(child: ByteString, normalize: Boolean = false): Path

31

fun resolve(child: Path, normalize: Boolean = false): Path

32

33

fun relativeTo(other: Path): Path

34

fun normalized(): Path

35

36

// Comparison and string representation

37

override fun equals(other: Any?): Boolean

38

override fun hashCode(): Int

39

override fun toString(): String

40

41

companion object {

42

val DIRECTORY_SEPARATOR: String

43

}

44

}

45

```

46

47

### Path Extensions

48

49

```kotlin { .api }

50

// String to Path conversion

51

fun String.toPath(normalize: Boolean = false): Path

52

```

53

54

### Usage Examples

55

56

```kotlin

57

// Creating paths

58

val absolutePath = "/Users/john/documents/file.txt".toPath()

59

val relativePath = "documents/file.txt".toPath()

60

61

// Path properties

62

println("Is absolute: ${absolutePath.isAbsolute}") // true

63

println("Root: ${absolutePath.root}") // "/"

64

println("Name: ${absolutePath.name}") // "file.txt"

65

println("Parent: ${absolutePath.parent}") // "/Users/john/documents"

66

println("Segments: ${absolutePath.segments}") // ["Users", "john", "documents", "file.txt"]

67

68

// Path manipulation

69

val documentsDir = "/Users/john".toPath()

70

val fileInDocs = documentsDir / "documents" / "file.txt"

71

val backupFile = fileInDocs.parent!! / "backup" / fileInDocs.name

72

73

// Relative paths

74

val base = "/Users/john".toPath()

75

val target = "/Users/john/documents/file.txt".toPath()

76

val relative = target.relativeTo(base) // "documents/file.txt"

77

78

// Normalization

79

val messyPath = "/Users/john/../john/./documents/../documents/file.txt".toPath()

80

val cleanPath = messyPath.normalized() // "/Users/john/documents/file.txt"

81

```

82

83

## FileSystem

84

85

FileSystem provides read and write access to a hierarchical collection of files and directories.

86

87

### Core FileSystem Operations

88

89

```kotlin { .api }

90

expect abstract class FileSystem {

91

// Path resolution

92

abstract fun canonicalize(path: Path): Path

93

94

// Metadata operations

95

fun metadata(path: Path): FileMetadata

96

abstract fun metadataOrNull(path: Path): FileMetadata?

97

fun exists(path: Path): Boolean

98

99

// Directory listing

100

abstract fun list(dir: Path): List<Path>

101

abstract fun listOrNull(dir: Path): List<Path>?

102

open fun listRecursively(dir: Path, followSymlinks: Boolean = false): Sequence<Path>

103

104

// File I/O

105

abstract fun openReadOnly(file: Path): FileHandle

106

abstract fun openReadWrite(

107

file: Path,

108

mustCreate: Boolean = false,

109

mustExist: Boolean = false

110

): FileHandle

111

112

abstract fun source(file: Path): Source

113

inline fun <T> read(file: Path, readerAction: BufferedSource.() -> T): T

114

115

abstract fun sink(file: Path, mustCreate: Boolean = false): Sink

116

inline fun <T> write(

117

file: Path,

118

mustCreate: Boolean = false,

119

writerAction: BufferedSink.() -> T

120

): T

121

122

abstract fun appendingSink(file: Path, mustExist: Boolean = false): Sink

123

124

// Directory operations

125

abstract fun createDirectory(dir: Path, mustCreate: Boolean = false)

126

fun createDirectories(dir: Path, mustCreate: Boolean = false)

127

128

// File management

129

abstract fun atomicMove(source: Path, target: Path)

130

open fun copy(source: Path, target: Path)

131

abstract fun delete(path: Path, mustExist: Boolean = false)

132

open fun deleteRecursively(fileOrDirectory: Path, mustExist: Boolean = false)

133

134

// Symbolic links

135

abstract fun createSymlink(source: Path, target: Path)

136

137

companion object {

138

val SYSTEM_TEMPORARY_DIRECTORY: Path

139

val SYSTEM: FileSystem

140

}

141

}

142

```

143

144

### Usage Examples

145

146

```kotlin

147

// Basic file operations

148

val fs = FileSystem.SYSTEM

149

val filePath = "/tmp/example.txt".toPath()

150

151

// Write to file

152

fs.write(filePath) {

153

writeUtf8("Hello, Okio FileSystem!")

154

writeUtf8("\n")

155

writeUtf8("This is a test file.")

156

}

157

158

// Read from file

159

val content = fs.read(filePath) {

160

readUtf8()

161

}

162

println("File content: $content")

163

164

// Check if file exists

165

if (fs.exists(filePath)) {

166

val metadata = fs.metadata(filePath)

167

println("File size: ${metadata.size} bytes")

168

println("Last modified: ${metadata.lastModifiedAtMillis}")

169

}

170

171

// Directory operations

172

val dirPath = "/tmp/test-directory".toPath()

173

fs.createDirectory(dirPath)

174

175

// List directory contents

176

val files = fs.list(dirPath)

177

files.forEach { path ->

178

println("Found: ${path.name}")

179

}

180

181

// Recursive directory listing

182

val homeDir = System.getProperty("user.home").toPath()

183

fs.listRecursively(homeDir / "Documents")

184

.take(10)

185

.forEach { path ->

186

println("File: $path")

187

}

188

189

// Copy and move operations

190

val sourceFile = "/tmp/source.txt".toPath()

191

val targetFile = "/tmp/target.txt".toPath()

192

193

fs.write(sourceFile) { writeUtf8("Source content") }

194

fs.copy(sourceFile, targetFile)

195

fs.atomicMove(targetFile, "/tmp/moved.txt".toPath())

196

197

// Cleanup

198

fs.delete(sourceFile)

199

fs.delete("/tmp/moved.txt".toPath())

200

fs.deleteRecursively(dirPath)

201

```

202

203

## FileHandle

204

205

FileHandle provides both streaming and random access I/O for open files.

206

207

### FileHandle API

208

209

```kotlin { .api }

210

abstract class FileHandle : Closeable {

211

// Properties

212

val readWrite: Boolean

213

214

// Random access I/O

215

fun read(fileOffset: Long, array: ByteArray, arrayOffset: Int = 0, byteCount: Int = array.size): Int

216

fun read(fileOffset: Long, sink: Buffer, byteCount: Long): Long

217

218

fun write(fileOffset: Long, array: ByteArray, arrayOffset: Int = 0, byteCount: Int = array.size)

219

fun write(fileOffset: Long, source: Buffer, byteCount: Long)

220

221

// File properties

222

fun size(): Long

223

fun resize(size: Long)

224

fun flush()

225

226

// Streaming I/O

227

fun source(fileOffset: Long = 0L): Source

228

fun sink(fileOffset: Long = 0L): Sink

229

fun appendingSink(): Sink

230

231

// Position management

232

fun position(source: Source): Long

233

fun reposition(source: Source, position: Long)

234

fun position(sink: Sink): Long

235

fun reposition(sink: Sink, position: Long)

236

237

override fun close()

238

}

239

```

240

241

### Usage Examples

242

243

```kotlin

244

// Random access file I/O

245

val fs = FileSystem.SYSTEM

246

val file = "/tmp/random-access.txt".toPath()

247

248

// Create file with some content

249

fs.write(file) {

250

writeUtf8("0123456789ABCDEF")

251

}

252

253

fs.openReadWrite(file).use { handle ->

254

// Read from specific position

255

val buffer = Buffer()

256

val bytesRead = handle.read(5L, buffer, 5L) // Read 5 bytes starting at offset 5

257

println("Read: ${buffer.readUtf8()}") // "56789"

258

259

// Write at specific position

260

val writeBuffer = Buffer().writeUtf8("HELLO")

261

handle.write(10L, writeBuffer, 5L) // Write "HELLO" at offset 10

262

263

// Get file size

264

println("File size: ${handle.size()}")

265

266

// Flush changes

267

handle.flush()

268

}

269

270

// Streaming with position control

271

fs.openReadOnly(file).use { handle ->

272

val source = handle.source(0L) // Start from beginning

273

val bufferedSource = source.buffer()

274

275

// Read first part

276

val firstPart = bufferedSource.readUtf8(5)

277

println("First part: $firstPart")

278

279

// Change position

280

handle.reposition(source, 10L)

281

val secondPart = bufferedSource.readUtf8(5)

282

println("Second part: $secondPart")

283

}

284

```

285

286

## FileMetadata

287

288

FileMetadata contains information about files and directories.

289

290

### FileMetadata Structure

291

292

```kotlin { .api }

293

class FileMetadata(

294

val isRegularFile: Boolean = false,

295

val isDirectory: Boolean = false,

296

val symlinkTarget: Path? = null,

297

val size: Long? = null,

298

val createdAtMillis: Long? = null,

299

val lastModifiedAtMillis: Long? = null,

300

val lastAccessedAtMillis: Long? = null,

301

val extras: Map<KClass<*>, Any> = emptyMap()

302

) {

303

fun <T : Any> extra(type: KClass<out T>): T?

304

305

fun copy(

306

isRegularFile: Boolean = this.isRegularFile,

307

isDirectory: Boolean = this.isDirectory,

308

symlinkTarget: Path? = this.symlinkTarget,

309

size: Long? = this.size,

310

createdAtMillis: Long? = this.createdAtMillis,

311

lastModifiedAtMillis: Long? = this.lastModifiedAtMillis,

312

lastAccessedAtMillis: Long? = this.lastAccessedAtMillis,

313

extras: Map<KClass<*>, Any> = this.extras

314

): FileMetadata

315

}

316

```

317

318

### Usage Examples

319

320

```kotlin

321

val fs = FileSystem.SYSTEM

322

val file = "/tmp/metadata-test.txt".toPath()

323

324

// Create a test file

325

fs.write(file) {

326

writeUtf8("Test content for metadata")

327

}

328

329

// Get file metadata

330

val metadata = fs.metadata(file)

331

332

println("Is regular file: ${metadata.isRegularFile}")

333

println("Is directory: ${metadata.isDirectory}")

334

println("File size: ${metadata.size} bytes")

335

336

metadata.lastModifiedAtMillis?.let { timestamp ->

337

val date = java.util.Date(timestamp)

338

println("Last modified: $date")

339

}

340

341

metadata.createdAtMillis?.let { timestamp ->

342

val date = java.util.Date(timestamp)

343

println("Created: $date")

344

}

345

346

// Check for symbolic links

347

if (metadata.symlinkTarget != null) {

348

println("Symbolic link target: ${metadata.symlinkTarget}")

349

}

350

351

// Directory metadata

352

val dirPath = "/tmp".toPath()

353

val dirMetadata = fs.metadata(dirPath)

354

println("Is directory: ${dirMetadata.isDirectory}")

355

```

356

357

## ForwardingFileSystem

358

359

ForwardingFileSystem is an abstract decorator for applying monitoring, encryption, compression, or filtering to file operations.

360

361

### ForwardingFileSystem Base

362

363

```kotlin { .api }

364

abstract class ForwardingFileSystem(

365

protected val delegate: FileSystem

366

) : FileSystem {

367

// All methods delegate to the wrapped FileSystem by default

368

// Override specific methods to add custom behavior

369

370

override fun canonicalize(path: Path): Path = delegate.canonicalize(path)

371

override fun metadataOrNull(path: Path): FileMetadata? = delegate.metadataOrNull(path)

372

override fun list(dir: Path): List<Path> = delegate.list(dir)

373

// ... other delegating methods

374

}

375

```

376

377

### Custom FileSystem Example

378

379

```kotlin

380

// Example: Logging FileSystem

381

class LoggingFileSystem(delegate: FileSystem) : ForwardingFileSystem(delegate) {

382

override fun source(file: Path): Source {

383

println("Opening source for: $file")

384

return delegate.source(file)

385

}

386

387

override fun sink(file: Path, mustCreate: Boolean): Sink {

388

println("Opening sink for: $file (mustCreate: $mustCreate)")

389

return delegate.sink(file, mustCreate)

390

}

391

392

override fun delete(path: Path, mustExist: Boolean) {

393

println("Deleting: $path")

394

delegate.delete(path, mustExist)

395

}

396

}

397

398

// Usage

399

val loggingFs = LoggingFileSystem(FileSystem.SYSTEM)

400

loggingFs.write("/tmp/logged-file.txt".toPath()) {

401

writeUtf8("This write operation will be logged")

402

}

403

```

404

405

## Advanced File System Operations

406

407

### Temporary Files and Directories

408

409

```kotlin

410

// Working with temporary directory

411

val tempDir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY

412

val tempFile = tempDir / "okio-temp-${System.currentTimeMillis()}.txt"

413

414

FileSystem.SYSTEM.write(tempFile) {

415

writeUtf8("Temporary file content")

416

}

417

418

// Cleanup temporary file

419

FileSystem.SYSTEM.delete(tempFile)

420

```

421

422

### Atomic Operations

423

424

```kotlin

425

val fs = FileSystem.SYSTEM

426

val sourceFile = "/tmp/source.txt".toPath()

427

val targetFile = "/tmp/target.txt".toPath()

428

429

// Create source file

430

fs.write(sourceFile) {

431

writeUtf8("Important data that must be moved atomically")

432

}

433

434

// Atomic move ensures no partial state

435

fs.atomicMove(sourceFile, targetFile)

436

437

// Source file no longer exists, target file has the content

438

println("Source exists: ${fs.exists(sourceFile)}") // false

439

println("Target exists: ${fs.exists(targetFile)}") // true

440

```

441

442

### Recursive Operations

443

444

```kotlin

445

val fs = FileSystem.SYSTEM

446

val baseDir = "/tmp/recursive-test".toPath()

447

448

// Create directory structure

449

fs.createDirectories(baseDir / "level1" / "level2" / "level3")

450

451

// Create files at various levels

452

fs.write(baseDir / "root-file.txt") { writeUtf8("Root level") }

453

fs.write(baseDir / "level1" / "level1-file.txt") { writeUtf8("Level 1") }

454

fs.write(baseDir / "level1" / "level2" / "level2-file.txt") { writeUtf8("Level 2") }

455

456

// List all files recursively

457

println("All files in directory tree:")

458

fs.listRecursively(baseDir).forEach { path ->

459

val metadata = fs.metadataOrNull(path)

460

val type = when {

461

metadata?.isDirectory == true -> "[DIR]"

462

metadata?.isRegularFile == true -> "[FILE]"

463

else -> "[OTHER]"

464

}

465

println("$type $path")

466

}

467

468

// Delete entire directory tree

469

fs.deleteRecursively(baseDir)

470

```

471

472

## Error Handling

473

474

File system operations can throw various exceptions:

475

476

```kotlin { .api }

477

expect open class IOException : Exception

478

expect class FileNotFoundException : IOException

479

expect class FileAlreadyExistsException : IOException

480

expect class DirectoryNotEmptyException : IOException

481

expect class AccessDeniedException : IOException

482

```

483

484

### Common Error Scenarios

485

486

```kotlin

487

val fs = FileSystem.SYSTEM

488

val nonExistentFile = "/path/that/does/not/exist.txt".toPath()

489

490

try {

491

// This will throw FileNotFoundException

492

fs.source(nonExistentFile)

493

} catch (e: FileNotFoundException) {

494

println("File not found: ${e.message}")

495

}

496

497

try {

498

// This will throw DirectoryNotEmptyException if directory has contents

499

fs.delete("/tmp".toPath())

500

} catch (e: DirectoryNotEmptyException) {

501

println("Directory not empty: ${e.message}")

502

}

503

504

// Safer operations using null-returning variants

505

val metadata = fs.metadataOrNull(nonExistentFile)

506

if (metadata != null) {

507

println("File exists with size: ${metadata.size}")

508

} else {

509

println("File does not exist")

510

}

511

512

val files = fs.listOrNull(nonExistentFile)

513

if (files != null) {

514

println("Directory contains ${files.size} items")

515

} else {

516

println("Directory does not exist or is not accessible")

517

}

518

```

519

520

## Platform-Specific Considerations

521

522

### POSIX File Systems (Unix/Linux/macOS/iOS)

523

524

- Support for symbolic links

525

- POSIX permissions in metadata extras

526

- Case-sensitive file names (usually)

527

- `/` as path separator

528

529

### File System Features by Platform

530

531

```kotlin

532

// Check platform capabilities

533

val fs = FileSystem.SYSTEM

534

val testDir = "/tmp/platform-test".toPath()

535

536

fs.createDirectory(testDir)

537

538

try {

539

// Symbolic links (POSIX systems)

540

val linkTarget = testDir / "target.txt"

541

val linkPath = testDir / "symlink.txt"

542

543

fs.write(linkTarget) { writeUtf8("Target content") }

544

fs.createSymlink(linkPath, linkTarget)

545

546

val linkMetadata = fs.metadata(linkPath)

547

if (linkMetadata.symlinkTarget != null) {

548

println("Symbolic link supported, points to: ${linkMetadata.symlinkTarget}")

549

}

550

} catch (e: Exception) {

551

println("Symbolic links not supported: ${e.message}")

552

} finally {

553

fs.deleteRecursively(testDir)

554

}

555

```