Skip to content

Unable to put files under an archive subfolder named . inside .tar #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
M0Rf30 opened this issue Jan 5, 2025 · 8 comments
Open

Comments

@M0Rf30
Copy link
Contributor

M0Rf30 commented Jan 5, 2025

Hi and thanks for this project and for you efforts.
I'm using this module in a project of mine for a long time, to manage all the stuff related to archives and I have a little issue.

I use it especially to deal with deb packages. deb packages are ar archives that contain a control.tar{.zst,gz...} and a data.tar{.zst,gz...}. They have a little peculiarity: the root folder is named .

So, using your instructions, I call archives.FilesFromDisk as below:

	files, err := archives.FilesFromDisk(ctx, nil, map[string]string{
		sourceDir + string(os.PathSeparator): ".",
	})

the key is something like "/tmp/yap/ubuntu1a749b1c/DEBIAN/"

What happens is that . folder is correctly created inside the control.tar.zst and data.tar.zst archives, but all the listed files on disk don't go under . folder.
They are added inside the archives but outside of . folder.
Just to make sure that I'm doing it in the right way, I used different names instead of . and everything works as expected.
I tried with many combinations but nothing to do. Is something that can be handled?

This behaviour is the same both on mholt/archiver and mholt/archives

Thanks in advance

@M0Rf30 M0Rf30 changed the title Unable to put files under an archive subfolder named "." inside .tar Unable to put files under an archive subfolder named . inside .tar Jan 5, 2025
@M0Rf30
Copy link
Contributor Author

M0Rf30 commented Jan 5, 2025

Here I also put diffoscope diff between archives

--- a/control.tar.zst
+++ b/control.tar.zst
├── control.tar
│ ├── file list
│ │ @@ -1,2 +1 @@
│ │ -drwxr-xr-x   0 root         (0) root         (0)        0 2025-01-05 11:10:36.000000 ./
│ │ --rw-r--r--   0 root         (0) root         (0)      278 2025-01-05 11:10:36.000000 ./control
│ │ +-rw-r--r--   0 root         (0) root         (0)      278 2025-01-04 18:32:50.000000 control
│ ├── filetype from file(1)
│ │ @@ -1 +1 @@
│ │ -POSIX tar archive (GNU)
│ │ +POSIX tar archive

@mholt
Copy link
Owner

mholt commented Jan 6, 2025

Do you have a minimal reproducer program that can exhibit the behavior? (i.e. something runnable, more than 3 lines of code)

@M0Rf30
Copy link
Contributor Author

M0Rf30 commented Jan 6, 2025

Do you have a minimal reproducer program that can exhibit the behavior? (i.e. something runnable, more than 3 lines of code)

https://github.com/M0Rf30/yap

Here the previous reference
https://github.com/M0Rf30/yap/blob/main/pkg/dpkg/dpkg.go#L37

you can build the code with

go build cmd/yap/yap.go

and execute it with

./yap build ubuntu-jammy example

artifacts will be created in the artifacts folder.

usage of the container image is recommended to correctly reproduce the behaviour
podman run -ti --entrypoint=bash -v (pwd):/project docker.io/m0rf30/yap-ubuntu-jammy:1.31

@M0Rf30 M0Rf30 closed this as completed Jan 6, 2025
@M0Rf30 M0Rf30 reopened this Jan 6, 2025
@mholt
Copy link
Owner

mholt commented Jan 6, 2025

@M0Rf30 I'm actually looking for a minimal reproducer, with no other dependencies (i.e. I won't debug your program for you, but I will debug this package). Expecting something from about 10-50 lines of code, probably, to narrow down the problem as quickly as possible.

@M0Rf30
Copy link
Contributor Author

M0Rf30 commented Jan 6, 2025

Nobody ask you to debug my program. That's why I'm here, because I think it's an archives module issue and I want to contribute to make it better if it's the case. Let me know if I can give you other useful details

@mholt
Copy link
Owner

mholt commented Jan 6, 2025

I just don't know what to do with the information given. The 3 lines above aren't runnable, and the only runnable code that has been provided is an entire package manager that I need a container image to run?

If you can repro it on play.golang.org, i.e. a simple Go program, I will be happy to prioritize taking a look at it; but with a full-on package manager there's too many variables at play and I have a lot of other things to work on.

@M0Rf30
Copy link
Contributor Author

M0Rf30 commented Jan 6, 2025

Here an example that reproduces the issue

package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/mholt/archives"
)

func main() {
	// Specify the directory and file name
	dirName := "folder"
	fileName := "emptyfile.txt"
	filePath := dirName + "/" + fileName

	// Create the directory if it doesn't exist
	err := os.MkdirAll(dirName, 0755)
	if err != nil {
		fmt.Println("Error creating directory:", err)
	}

	// Create the file in the specified directory
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("Error creating file:", err)
	}
	defer file.Close() // Ensure the file is closed after we're done

	fmt.Println("Empty file created:", filePath)

	ctx := context.TODO()

	files, err := archives.FilesFromDisk(ctx, nil, map[string]string{
		dirName + string(os.PathSeparator): ".",
	})

	if err != nil {
		fmt.Println(err)
	}

	cleanFilePath := filepath.Clean("control.tar.zst")

	out, err := os.Create(cleanFilePath)
	if err != nil {
		fmt.Println(err)
	}
	defer out.Close()

	format := archives.CompressedArchive{
		Compression: archives.Zstd{},
		Archival:    archives.Tar{},
	}

	err = format.Archive(ctx, out, files)
	if err != nil {
		fmt.Println(err)
	}
}

You will have expected behaviour replacing

dirName + string(os.PathSeparator): ".",

with

dirName + string(os.PathSeparator): "folder",

@mholt
Copy link
Owner

mholt commented Jan 6, 2025

Thank you -- now I can see the behavior. This is evidently because we call path.Join() to concatenate the final path in the archive. path.Join() calls path.Clean() to ensure a canonical filename.

I hesitate to remove that call since I think the consequences could be unexpected... for example, you could have paths in your archive, like "../../../../home/foo/surprise.txt" that could theoretically cause problems during extraction. (Having an archive with a file named that is not itself a vulnerability, but extracting it could be. I just made up a very contrived example.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants