# How the Trash Feature is Implemented Across Different Operating Systems in Superfile

> Discover how Superfile implements its trash feature across Linux macOS and Windows using native APIs and a fallback for permanent deletion. Learn about its cross-platform deletion strategy.

- Repository: [Yorukot/superfile](https://github.com/yorukot/superfile)
- Tags: internals
- Published: 2026-07-28

---

**Superfile uses a build-tag-selected abstraction layer in `src/internal/trash` to delegate deletion operations to native platform APIs—FreeDesktop.org on Linux, Foundation Framework on macOS, and COM IFileOperation on Windows—falling back to permanent deletion when trash is unavailable.**

The open-source terminal file manager superfile (`yorukot/superfile`) implements cross-platform trash functionality through a modular Go architecture. Rather than simply deleting files permanently, the application detects the host operating system at compile time and routes trash operations to the appropriate native backend, ensuring users get expected OS behavior regardless of platform.

## Build-Time Architecture and API Surface

Superfile abstracts the system trash behind a minimal public API defined in [`src/internal/trash/trash.go`](https://github.com/yorukot/superfile/blob/main/src/internal/trash/trash.go). The package exposes two primary functions that every platform must implement:

- **`Available(path string) bool`** – Reports whether trash operations are supported for the given path.
- **`Move(path string) (Result, error)`** – Executes the actual trash operation and returns metadata about the action.

The appropriate implementation is selected at compile time using Go build tags. Each operating system provides a specific file that satisfies this interface:

- **Linux**: [`trash_linux.go`](https://github.com/yorukot/superfile/blob/main/trash_linux.go) (FreeDesktop.org spec)
- **macOS**: [`trash_darwin.go`](https://github.com/yorukot/superfile/blob/main/trash_darwin.go) (with CGO) or [`trash_darwin_nocgo.go`](https://github.com/yorukot/superfile/blob/main/trash_darwin_nocgo.go) (fallback)
- **Windows**: [`trash_windows.go`](https://github.com/yorukot/superfile/blob/main/trash_windows.go) (COM interface)
- **Other platforms**: [`trash_unsupported.go`](https://github.com/yorukot/superfile/blob/main/trash_unsupported.go) (returns `ErrUnsupported`)

## Linux Implementation: FreeDesktop.org Compliance

On Linux, superfile follows the [FreeDesktop.org Trash specification](https://specifications.freedesktop.org/trash-spec/trashspec-1.0.html). The implementation resides in [`src/internal/trash/trash_linux.go`](https://github.com/yorukot/superfile/blob/main/src/internal/trash/trash_linux.go) and handles cross-filesystem edge cases while maintaining metadata integrity.

### Trash Directory Selection

The Linux backend selects the appropriate trash directory based on the file's location. For files on the same filesystem as the user's home directory, it uses `~/.local/share/Trash`. For files on mounted drives, it creates a `.Trash-<uid>` directory at the filesystem root to avoid cross-device moves.

```go
// Reserve a unique name and paths for .trashinfo and the file itself
trashName, infoPath, filesPath, err := reserveTrashInfo(trashDir, srcAbs)
if err != nil {
    return Result{}, err
}

// Write the .trashinfo metadata file
if err := writeTrashInfo(infoPath, srcAbs, deletionTime); err != nil {
    return Result{}, err
}

// Move the actual file into trash files directory
if err := movePath(srcAbs, filesPath); err != nil {
    return Result{}, err
}

```

The `writeTrashInfo` function generates a `.trashinfo` file containing the original path and deletion timestamp, allowing restoration tools to return the file to its original location. The backend sets `StrictlyRecycled: true` and returns `BackendFreeDesktop` in the Result struct upon success.

## macOS Implementation: Foundation Framework Integration

macOS support depends on CGO availability. When compiled with CGO enabled, [`trash_darwin.go`](https://github.com/yorukot/superfile/blob/main/trash_darwin.go) imports the Foundation framework and calls `FileManager.trashItem(at:)` through C wrappers.

### Native API Delegation

The implementation marshals the absolute path to a C string and invokes the native Objective-C trash API:

```go
cPath := C.CString(absPath)
defer C.free(unsafe.Pointer(cPath))

result := C.spf_trash_item(cPath)  // Calls Foundation FileManager
if result.errorMessage != nil {
    return Result{
        OriginalPath: absPath,
        Backend:      BackendMacOS,
    }, errors.New(C.GoString(result.errorMessage))
}

trashedPath := ""
if result.trashedPath != nil {
    trashedPath = C.GoString(result.trashedPath)
}

return Result{
    OriginalPath:     absPath,
    TrashedPath:      trashedPath,
    Backend:          BackendMacOS,
    StrictlyRecycled: true,
}, nil

```

If CGO is disabled, [`trash_darwin_nocgo.go`](https://github.com/yorukot/superfile/blob/main/trash_darwin_nocgo.go) provides a stub implementation that returns `ErrUnsupported`, forcing superfile to fall back to permanent deletion.

## Windows Implementation: COM IFileOperation

The Windows backend in [`trash_windows.go`](https://github.com/yorukot/superfile/blob/main/trash_windows.go) utilizes the Component Object Model (COM) to access the shell's recycle functionality. This ensures that trashed files appear in the Windows Recycle Bin with full undo support.

### Secure Recycling via Shell APIs

The implementation uses `IFileOperation` with the `FOF_ALLOWUNDO` flag to enable restoration:

```go
absPath, _ := filepath.Abs(path)
result := Result{
    OriginalPath: absPath,
    Backend:      BackendWindows,
}

errCh := make(chan error, 1)
go func() {
    runtime.LockOSThread()  // COM requires threaded apartments
    defer runtime.UnlockOSThread()
    errCh <- recycleWithIFileOperation(absPath)
}()

if err := <-errCh; err != nil {
    return result, err
}

result.StrictlyRecycled = true
return result, nil

```

The `recycleWithIFileOperation` function calls `SHCreateItemFromParsingName` to obtain an `IShellItem`, then invokes `DeleteItem` followed by `PerformOperations` to execute the recycle action atomically.

## Integration and Fallback Handling

Superfile integrates the trash system within [`src/internal/handle_file_operations.go`](https://github.com/yorukot/superfile/blob/main/src/internal/handle_file_operations.go). The `makeDeleteProcessor` function constructs the deletion strategy based on user preferences and platform capability:

```go
deleteFunc := os.RemoveAll  // Default: permanent delete
if useTrash {
    deleteFunc = func(item string) error {
        _, err := trash.Move(item)
        return err
    }
}

for _, item := range items {
    if err := deleteFunc(item); err != nil {
        // Handle error: log and continue or abort based on config
    }
}

```

Before attempting trash operations, the code checks `trash.Available(path)`. If the platform reports `ErrUnsupported` or `Available` returns false—such as on non-CGO macOS builds or unsupported operating systems—superfile gracefully degrades to `os.RemoveAll` for permanent deletion.

## Summary

- Superfile implements cross-platform trash support through the `src/internal/trash` package using Go build tags for compile-time selection.
- **Linux** follows the FreeDesktop.org specification, creating `.trashinfo` metadata and managing per-filesystem trash directories.
- **macOS** delegates to the Foundation Framework via CGO when available, providing native Trash integration.
- **Windows** uses COM `IFileOperation` with `FOF_ALLOWUNDO` to ensure files appear in the Recycle Bin.
- The system gracefully falls back to permanent deletion when `Available()` returns false or `ErrUnsupported` is encountered.

## Frequently Asked Questions

### How does superfile handle trash on network drives or external mounts?

On Linux, superfile detects cross-filesystem paths and creates a `.Trash-<uid>` directory at the mount point's root, adhering to the FreeDesktop.org specification for external media. This prevents slow cross-device copying and ensures metadata is stored with the trashed files. macOS and Windows handle this automatically through their native APIs.

### Can I restore files from trash using superfile?

The current implementation focuses on moving files to trash rather than restoration. While the Linux backend writes proper `.trashinfo` metadata that standard Linux file managers can use for restoration, superfile itself does not currently provide a built-in restore function. You must use your operating system's native trash/recycle interface to recover files.

### What happens if I compile superfile without CGO on macOS?

If you build superfile with `CGO_ENABLED=0` on macOS, the build system selects [`trash_darwin_nocgo.go`](https://github.com/yorukot/superfile/blob/main/trash_darwin_nocgo.go), which returns `ErrUnsupported` for all trash operations. In this configuration, deletion operations fall back to permanent removal via `os.RemoveAll`, and files will not appear in the macOS Trash.

### Why does Windows use a goroutine for trash operations?

The Windows implementation locks the OS thread (`runtime.LockOSThread()`) because COM operations require a consistent thread apartment state. Running the `recycleWithIFileOperation` function in a dedicated goroutine with thread locking prevents interference from Go's runtime thread scheduling, ensuring the COM initialize/uninitialize calls occur on the same thread and avoid undefined behavior.