package jobs import ( "errors" "os" "path/filepath" "ddc/internal/diskspace" "ddc/internal/extract" "ddc/internal/files" ) const maxArchiveEntries = 100000 var errNotArchive = errors.New("selected file is not a supported archive") func (manager *Manager) download(job *Job) { defer job.workers.Done() if !manager.slots.acquire(job.jobContext, manager.concurrencyLimit) { return } defer manager.slots.release() job.setStatus(StatusDownloading, "") filePath, err := manager.downloader.Download(job.jobContext, job.sourceURL, job.directory, manager.reservedBytes(), job) if err != nil { manager.finishWithError(job, StatusFailed, "", err) return } job.SetFileName(filepath.Base(filePath)) if job.autoExtract && extract.IsArchive(filePath) { job.setStatus(StatusExtracting, "") destination, err := manager.extractArchive(job, filePath) if err != nil { manager.finishWithError(job, StatusReady, "Extraction failed: ", err) return } job.SetFileName(filepath.Base(destination)) } job.setStatus(StatusReady, "") } func (manager *Manager) Extract(id, relativePath string) error { job, err := manager.lookup(id) if err != nil { return err } archivePath, err := files.ResolveInside(job.directory, relativePath) if err != nil { return invalid(err) } info, err := os.Stat(archivePath) if err != nil || !info.Mode().IsRegular() || !extract.IsArchive(archivePath) { return invalid(errNotArchive) } if !job.begin(StatusReady, StatusExtracting) { return ErrJobBusy } go func() { defer job.workers.Done() if _, err := manager.extractArchive(job, archivePath); err != nil { manager.finishWithError(job, StatusReady, "Extraction failed: ", err) return } job.setStatus(StatusReady, "") }() return nil } func (manager *Manager) extractArchive(job *Job, archivePath string) (string, error) { available, err := diskspace.Available(job.directory) if err != nil { return "", err } limit := available - manager.reservedBytes() if limit <= 0 { return "", diskspace.ErrInsufficientSpace } directoryName := extract.DirectoryName(filepath.Base(archivePath)) destination := files.UniquePath(filepath.Join(filepath.Dir(archivePath), directoryName)) limits := extract.Limits{MaxBytes: limit, MaxEntries: maxArchiveEntries} job.resetProgress() if err := extract.Archive(job.jobContext, archivePath, destination, limits, job); err != nil { return "", err } return destination, os.Remove(archivePath) } func (manager *Manager) finishWithError(job *Job, status Status, prefix string, err error) { if job.jobContext.Err() != nil { job.setStatus(StatusCancelled, "") return } job.setStatus(status, prefix+err.Error()) }