FILES_MAX_BYTES) throw new RuntimeException('Files must be between 1 byte and 50 MiB.'); if (count($store['items']) >= FILES_MAX_COUNT) throw new RuntimeException('The 500-file limit has been reached.'); $used = array_sum(array_column($store['items'], 'size')); // Include orphaned files left by interrupted requests in the storage budget. $actual = 0; foreach (glob(__DIR__ . '/data/uploads/*.bin') ?: [] as $stored) { $bytes = filesize($stored); if ($bytes === false) throw new RuntimeException('Cannot measure stored files.'); $actual += $bytes; } if (max($used, $actual) + $size > FILES_TOTAL_BYTES) throw new RuntimeException('The 1 GiB storage budget would be exceeded.'); $free = disk_free_space(__DIR__ . '/data'); if ($free === false || $free < $size + 10 * 1024 * 1024) throw new RuntimeException('Not enough free disk space.'); $key = bin2hex(random_bytes(16)); $destination = __DIR__ . '/data/uploads/' . $key . '.bin'; if (!move_uploaded_file($temp, $destination)) throw new RuntimeException('Cannot store uploaded file.'); try { if (!chmod($destination, 0640)) throw new RuntimeException('Cannot set uploaded file permissions.'); $store['items'][$key] = ['name' => files_name(files_string($upload['name'] ?? null)), 'size' => $size, 'time' => time()]; $store['last_upload'] = time(); files_write($store); } catch (Throwable $ex) { if (!@unlink($destination)) throw new RuntimeException('Save failed and cleanup also failed. Check data/uploads for an orphan file.'); throw $ex; } return ['success', 'Uploaded: ' . $store['items'][$key]['name']]; } finally { flock($lock, LOCK_UN); fclose($lock); } } $filesNotice = null; $filesItems = []; $filesReady = true; try { $filesNotice = files_handle((bool)$isAdmin, (string)($csrf ?? '')); } catch (Throwable $filesException) { $filesNotice = ['danger', $filesException->getMessage()]; } try { $filesLock = files_lock(LOCK_SH); try { $filesStore = files_read(); $filesItems = $filesStore['items']; } finally { flock($filesLock, LOCK_UN); fclose($filesLock); } uasort($filesItems, fn($filesLeft, $filesRight) => $filesRight['time'] <=> $filesLeft['time']); } catch (Throwable $filesException) { $filesReady = false; $filesNotice = ['danger', $filesException->getMessage()]; } ?>

File sharing

Public uploads and downloads. Maximum 50 MiB per file, 500 files, 1 GiB total. One upload every 10 seconds across the site. Downloads are not scanned for malware.

No shared files yet.