| 1700 | } |
| 1701 | |
| 1702 | func (h *fsHandler) compressFileNolock( |
| 1703 | f fs.File, fileInfo fs.FileInfo, filePath, compressedFilePath, fileEncoding string, |
| 1704 | ) (*fsFile, error) { |
| 1705 | // Attempt to open compressed file created by another concurrent |
| 1706 | // goroutine. |
| 1707 | // It is safe opening such a file, since the file creation |
| 1708 | // is guarded by file mutex - see getFileLock call. |
| 1709 | if _, err := os.Stat(compressedFilePath); err == nil { |
| 1710 | _ = f.Close() |
| 1711 | return h.newCompressedFSFile(compressedFilePath, fileEncoding) |
| 1712 | } |
| 1713 | |
| 1714 | // Create temporary file, so concurrent goroutines don't use |
| 1715 | // it until it is created. |
| 1716 | tmpFilePath := compressedFilePath + ".tmp" |
| 1717 | zf, err := os.Create(tmpFilePath) |
| 1718 | if err != nil { |
| 1719 | _ = f.Close() |
| 1720 | if !errors.Is(err, fs.ErrPermission) { |
| 1721 | return nil, fmt.Errorf("cannot create temporary file %q: %w", tmpFilePath, err) |
| 1722 | } |
| 1723 | return nil, errNoCreatePermission |
| 1724 | } |
| 1725 | switch fileEncoding { |
| 1726 | case "br": |
| 1727 | zw := acquireStacklessBrotliWriter(zf, CompressDefaultCompression) |
| 1728 | _, err = copyZeroAlloc(zw, f) |
| 1729 | if errf := zw.Flush(); err == nil { |
| 1730 | err = errf |
| 1731 | } |
| 1732 | releaseStacklessBrotliWriter(zw, CompressDefaultCompression) |
| 1733 | case "gzip": |
| 1734 | zw := acquireStacklessGzipWriter(zf, CompressDefaultCompression) |
| 1735 | _, err = copyZeroAlloc(zw, f) |
| 1736 | if errf := zw.Flush(); err == nil { |
| 1737 | err = errf |
| 1738 | } |
| 1739 | releaseStacklessGzipWriter(zw, CompressDefaultCompression) |
| 1740 | case "zstd": |
| 1741 | zw := acquireStacklessZstdWriter(zf, CompressZstdDefault) |
| 1742 | _, err = copyZeroAlloc(zw, f) |
| 1743 | if errf := zw.Flush(); err == nil { |
| 1744 | err = errf |
| 1745 | } |
| 1746 | releaseStacklessZstdWriter(zw, CompressZstdDefault) |
| 1747 | } |
| 1748 | _ = zf.Close() |
| 1749 | _ = f.Close() |
| 1750 | if err != nil { |
| 1751 | _ = os.Remove(tmpFilePath) |
| 1752 | return nil, fmt.Errorf("error when compressing file %q to %q: %w", filePath, tmpFilePath, err) |
| 1753 | } |
| 1754 | if err = os.Chtimes(tmpFilePath, time.Now(), fileInfo.ModTime()); err != nil { |
| 1755 | _ = os.Remove(tmpFilePath) |
| 1756 | return nil, fmt.Errorf("cannot change modification time to %v for tmp file %q: %v", |
| 1757 | fileInfo.ModTime(), tmpFilePath, err) |
| 1758 | } |
| 1759 | if err = os.Rename(tmpFilePath, compressedFilePath); err != nil { |