Difference between windows symbolic links and directories - windows

I run into a problem with Go when trying to tell difference between windows symbolic links and directories.
I've googled and all I could find was this:
https://github.com/golang/go/issues/3498#issuecomment-142810957
Which is unfortunately closed and not being worked on.
So my question is, is there any workaround? I tried to listdir the path with the symlinks but it is returning the same that it would return on an empty directory.
With python I was able to do something like this:
def test(dir):
try:
os.chdir(dir)
except Exception as e:
if "[Error 2]" in str(e):
return False
else:
return True
Is there any bash command I could use to call from go to detect it?
I'm running out of ideas :(

The only test I see (and I just tested it with go 1.5.1 on Windows) is in os/os_test.go:
fromstat, err = Lstat(from)
if err != nil {
t.Fatalf("lstat %q failed: %v", from, err)
}
if fromstat.Mode()&ModeSymlink == 0 {
t.Fatalf("symlink %q, %q did not create symlink", to, from)
}
It uses os/#Lstat:
Lstat returns a FileInfo describing the named file.
If the file is a symbolic link, the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link.
If there is an error, it will be of type *PathError.
You can also get os.Stat() of the same folder, and then call os.Samefile() (as in this test):
if !SameFile(tostat, fromstat) {
t.Errorf("symlink %q, %q did not create symlink", to, from)
}

Related

How to handle "no such file or directory" in os.Stat

I have a piece of code that is looking to stat a file and return some default values if the file does not exist. Namely, the piece of code looks something like the following:
fileInfo, err := os.Stat(absolutePath)
if err != nil {
if os.IsNotExist(err) {
return <default val>, nil
}
return nil, fmt.Errorf(...)
}
To "catch" the "file does not exist" error I have read that it's advised to use os.IsNotExist to check if the error indicates the file does not exist. However, os.IsNotExist does not catch this error since the platform returns open <blah>: no such file or directory. I can add my own error handling here but is there is an idiomatic way to handle "file does not exist" errors from os calls? It seems like the error handled by the os.IsNotExist check is special cased to just one type of potential "file does not exist" error.
If you read the documentation, you'll see this:
func IsNotExist
func IsNotExist(err error) bool
IsNotExist returns a boolean indicating whether the error is known to
report that a file or directory does not exist. It is satisfied by
ErrNotExist as well as some syscall errors.
This function predates errors.Is. It only supports errors returned by the os package.
New code should use errors.Is(err, fs.ErrNotExist). [emph. mine]

Golang os.Create path with nested directories

As mentioned in GoDocs, os.Create() creates a file in specific path.
os.Create("fonts/foo/font.eot")
But when fonts or foo doesn't exists, it returns panic: open fonts/foo/font.eot: The system cannot find the path specified.
So i used os.MkdirAll() to create nested directory. But there are many other problems with this function.
path := "fonts/foo/font.eot"
// this line create a directory named (font.eot) !
os.MkdirAll(path, os.ModePerm)
Is there any better way to create a file in nested directories?
The standard way is something like this:
func create(p string) (*os.File, error) {
if err := os.MkdirAll(filepath.Dir(p), 0770); err != nil {
return nil, err
}
return os.Create(p)
}
A few notes:
os.Create does not panic as stated in the question.
Create a directory from the directory part of the file path, not the full path.

How to change timestamp for symbol link using golang?

os.Chtimes always to follow symlinks and change the real files timestamp.
Is there a method to change the symlinks timestamp in?
Just like touch -h does.
Not sure it's possible, at least from the syscall package.
Looking at the source-code for say syscall.Chtimes:
func Chtimes(name string, atime time.Time, mtime time.Time) error {
var utimes [2]syscall.Timespec
utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil {
return &PathError{"chtimes", name, e}
}
return nil
}
duplicating this code - and removing the fixLongPath call which I assumed followed the symlinks - still affects the target file, not the source symlink.
Even trying this operation on a symlink which points to a non-existent file, returns a runtime error no such file or directory.
A CGO pkg - could, but that seems overkill.
If you use linux, you can use golang.org/x/sys/unix package, which provides Lutimes
import "golang.org/x/sys/unix"
unix.Lutimes(symlink, nil)
You can check if a symlink exists and if so, remove it and create another one.
if _, err := os.Lstat(symlinkPath); err == nil {
os.Remove(symlinkPath)
}
err := os.Symlink(filePath, symlinkPath)
if err != nil {
fmt.Println(err)
}

Go get parent directory

I've some command line program which I need to read files from parent folder, I mean
-parentDir
-- myproject
--- cmd
----main.go
--otherdir
-file.json
As you can see otherdir is like sibling to myproject and I need from my main.go read the file.json
what I've tried is like following
func visit(path string, f os.FileInfo, err error) error {
fmt.Printf("Visited: %s\n", path)
return nil
}
func main() {
flag.Parse()
root := flag.Arg(0)
err := filepath.Walk(root, visit)
fmt.Printf("filepath.Walk() returned %v\n", err)
}
I've also try to provide args(-1) which doesnt help...
Any idea how from command line program I can read some files that on level up from my executable ?
I've also tried with
import "github.com/mitchellh/go-homedir"
func Path(path string) (error, string) {
home, err := homedir.Dir()
}
this give the root directory which doesnt help either...
It doesn't matter where the binary is, it matters what the working directory is (the directory you're in when you execute the program). All relative paths will be relative to the current working directory. So, if you're executing from myproject, you'd use something like ../ as the root path to Walk.
That said, I would highly recommend you make the path configurable, rather than assuming the binary will always be executed from some particular location within the source tree.

Find binary's in $PATH

I am looking for a function to find a binary in all folders which are available in $PATH.
I know i can use os.Getenv("PATH") but it returns:
path: /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/local/go/bin
Which needs to be processed.
Ideally function like this: FindBinary("ntpq") and it will return the path + binary name and false when not found in path.
Does anybody have a ready function?
You can use the LookPath function from the os/exec package:
path, err := exec.LookPath("fortune")
if err != nil {
log.Fatal("installing fortune is in your future")
}
fmt.Printf("fortune is available at %s\n", path)

Resources