copy icon indicating copy to clipboard operation
copy copied to clipboard

dir symlink recursive loop

Open enthor opened this issue 3 years ago • 5 comments

With Deep symlink copying in use, when a directory containing a symlink is copied and the symlink points directly to the directory or to another directory -- which is subsequently copied and contains a symlink to the original directory, a loop ensues which is terminated in os.Create with a "file name too long error" ...

I understand that there may be other issues copying symlinks, but

https://github.com/enthor/copy/tree/enthor-patch-1

fixes this issue. (I believe...)

I included a test case -- search "case03b" in all_test.go. To see the error output remove the "Not()." in the following lines (temporarily):

// stop the recursive loop: opt = Options{OnSymlink: func(string) SymlinkAction { return Deep }} err = Copy("test/data/case03b", "test/data.copy/case03b.deep", opt) Expect(t, err).Not().ToBe(nil)

enthor avatar Apr 03 '21 01:04 enthor

it may be that the line in onsymlink():

id := info.Name()

should be changed (somehow) to refer to the linked-to object (rather than the base name in the copied directory).

could use some feedback on this...

enthor avatar Apr 03 '21 01:04 enthor

ok, i think this is better -- though only 1 level deep symlink copy:

` case Deep: orig, err := os.Readlink(src) if err != nil { return err }

	id, err := filepath.Abs(orig)
	if err != nil {
		return err
	}
	err = inProcess.Start(id, "copy.onsymlink()")
	if err != nil {
		return err
	}
	defer inProcess.End(id)

	info, err = os.Lstat(orig)
	if err != nil {
		return err
	}
	return copy(orig, dest, info, opt, inProcess)

`

enthor avatar Apr 03 '21 03:04 enthor

ok! a wrap on stopping a recursive copy of a directory caused by a deep symlink copy:

https://github.com/enthor/copy/tree/enthor-patch-1

this does not resolve nested symlinks or the issue with shallow symlinks and it does not alter the symlink copy itself, but puts an "InProcess" tracker to halt a recursive copy of a directory:

` case Deep: // ensure that if this symlink recursively comes back here, copy stops. id, err := filepath.Abs(filepath.Dir(src)) if err != nil { return err } err = inProcess.Start(id, "copy.onsymlink()") if err != nil { return err } defer inProcess.End(id)

	orig, err := os.Readlink(src)
	if err != nil {
		return err
	}
	info, err = os.Lstat(orig)
	if err != nil {
		return err
	}
	return copy(orig, dest, info, opt, inProcess)

`

enthor avatar Apr 03 '21 11:04 enthor

Well...I just reviewed Wikipedia's entry for Symbolic Links:

https://en.wikipedia.org/wiki/Symbolic_link

The topic is beyond being a rabbit hole. There's an entire rabbit world down there!

Which is fine at the OS level, but it is "out of scope" for Applications level code. (Or should be.)

I don't use symbolic links, or hidden files, or named pipes or anything except for directories and files. And I don't want to because those features tend to be OS-specific. And if I write OS specific code then constant upgrades, revisions, recompiles, etc. lie ahead, not to mention making it hard to port my applications from one OS to another.

I think I may clone copy.Copy() in order to make a version that is blissfully unaware of symbolic links, named pipes, times and permissions. Those things are not part of my application.

To some extent the complications involved here derive from the nature of Go itself, and its heritage in C and Unix. It is just very low-level programming. An example of the complications, google "golang how to tell if a file exists". Yielding stackoverflow:

https://stackoverflow.com/questions/12518876/how-to-check-if-a-file-exists-in-go

It requires 12 answers, including admonishments that one should not do that and a dive into the metaphysical nature of the question itself. And then finally it is explained that it is best to check that it isn't true that the file doesn't exist. Mmm...ok. Fine. Whatever.

Life is short. Let's not get bogged down in things...not without being paid to do the work. If someone want to pay me $100 an hour to write code using an Atomic Force Microscope then great, I'll consider doing that. But otherwise, mmmmm...

enthor avatar Apr 04 '21 16:04 enthor

The difficulty of writing an OS-independent directory copy is now more apparent. I found another bit of code at stackoverflow:

https://stackoverflow.com/questions/51779243/copy-a-folder-in-go

and it looks promising, but it is only for Unix. So, I looked into things a bit more and made the discovery that package "syscall" has been frozen since go 1.4 and golang.org/x/sys is recommended -- the old syscall is not being updated.

So there's that. And the question of whether a distinction of "Deep" vs. "Shallow" is even required.

Also, the question of what happens when attempt made to copy and src == dest. None of the test code actually looks at the contents of the copied files, all of which are "README.md" and empty. Well, empty would be expected, I think, if src == dest and dest was opened, os.Create().

My view is that the Go authors should be asked to add a directory copy utility to Go, perhaps in Go 2. It would be nice if Go had more built-in utilities...like, say, Python. Maybe however, it is too difficult to write os-independent utilities in Go given how many OS'es and architectures it supports.

enthor avatar Apr 07 '21 20:04 enthor