mkdir if not exists using golang - go

I am learning golang(beginner) and I have been searching on both google and stackoverflow but I could not find an answer so excuse me if already asked, but how can I mkdir if not exists in golang.
For example in node I would use fs-extra with the function ensureDirSync (if blocking is of no concern of course)
fs.ensureDir("./public");

Okay I figured it out thanks to this question/answer
import(
"os"
"path/filepath"
)
newpath := filepath.Join(".", "public")
err := os.MkdirAll(newpath, os.ModePerm)
// TODO: handle error
Relevant Go doc for MkdirAll:
MkdirAll creates a directory named path,
along with any necessary parents, and returns nil,
or else returns an error.
...
If path is already a directory, MkdirAll does nothing
and returns nil.

I've ran across two ways:
Check for the directory's existence and create it if it doesn't exist:
if _, err := os.Stat(path); os.IsNotExist(err) {
err := os.Mkdir(path, mode)
// TODO: handle error
}
However, this is susceptible to a race condition: the path may be created by someone else between the os.Stat call and the os.Mkdir call.
Attempt to create the directory and ignore any issues (ignoring the error is not recommended):
_ = os.Mkdir(path, mode)

This is one alternative for achieving the same but it avoids race condition caused by having two distinct "check ..and.. create" operations.
package main
import (
"fmt"
"os"
)
func main() {
if err := ensureDir("/test-dir"); err != nil {
fmt.Println("Directory creation failed with error: " + err.Error())
os.Exit(1)
}
// Proceed forward
}
func ensureDir(dirName string) error {
err := os.Mkdir(dirName, os.ModeDir)
if err == nil {
return nil
}
if os.IsExist(err) {
// check that the existing path is a directory
info, err := os.Stat(dirName)
if err != nil {
return err
}
if !info.IsDir() {
return errors.New("path exists but is not a directory")
}
return nil
}
return err
}

So what I have found to work for me is:
import (
"os"
"path/filepath"
"strconv"
)
//Get the cur file dir
path, err := filepath.Abs("./") // current opened dir (NOT runner dir)
// If you want runner/executable/binary file dir use `_, callerFile, _, _ := runtime.Caller(0)
// path := filepath.Dir(callerFile)`
if err != nil {
log.Println("error msg", err)
}
//Create output path
outPath := filepath.Join(path, "output")
//Create dir output using above code
if _, err = os.Stat(outPath); os.IsNotExist(err) {
var dirMod uint64
if dirMod, err = strconv.ParseUint("0775", 8, 32); err == nil {
err = os.Mkdir(outPath, os.FileMode(dirMod))
}
}
if err != nil && !os.IsExist(err) {
log.Println("error msg", err)
}
I like the portability of this.

Or you could attempt creating the file and check that the error returned isn't a "file exists" error
if err := os.Mkdir(path, mode); err != nil && !os.IsExist(err) {
log.Fatal(err)
}

Related

Golang: Facing error while creating .tar.gz file having large name

I am trying to create a .tar.gz file from folder that contains multiple files / folders. Once the .tar.gz file gets created, while extracting, the files are not not properly extracted. Mostly I think its because of large names or path exceeding some n characters, because same thing works when the filename/path is small. I referred this https://github.com/golang/go/issues/17630 and tried to add below code but it did not help.
header.Uid = 0
header.Gid = 0
I am using simple code seen below to create .tar.gz. The approach is, I create a temp folder, do some processing on the files and from that temp path, I create the .tar.gz file hence in the path below I am using pre-defined temp folder path.
package main
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"log"
"os"
fp "path/filepath"
)
func main() {
// Create output file
out, err := os.Create("output.tar.gz")
if err != nil {
log.Fatalln("Error writing archive:", err)
}
defer out.Close()
// Create the archive and write the output to the "out" Writer
tmpDir := "C:/Users/USERNAME~1/AppData/Local/Temp/temp-241232063"
err = createArchive1(tmpDir, out)
if err != nil {
log.Fatalln("Error creating archive:", err)
}
fmt.Println("Archive created successfully")
}
func createArchive1(path string, targetFile *os.File) error {
gw := gzip.NewWriter(targetFile)
defer gw.Close()
tw := tar.NewWriter(gw)
defer tw.Close()
// walk through every file in the folder
err := fp.Walk(path, func(filePath string, info os.FileInfo, err error) error {
// ensure the src actually exists before trying to tar it
if _, err := os.Stat(filePath); err != nil {
return err
}
if err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
// generate tar header
header, err := tar.FileInfoHeader(info, info.Name())
header.Uid = 0
header.Gid = 0
if err != nil {
return err
}
header.Name = filePath //strings.TrimPrefix(filePath, fmt.Sprintf("%s/", fp.Dir(path))) //info.Name()
// write header
if err := tw.WriteHeader(header); err != nil {
return err
}
if _, err := io.Copy(tw, file); err != nil {
return err
}
return nil
})
return err
}
Please let me know what wrong I am doing.

Check if the program is running and stop more than one [duplicate]

I need to only allow one instance of my Golang executable at a time. I'm not sure how to use a Global Mutex to make sure no other instances are running.
This would be running on a Windows Machine.
I know this topic is a bit old, but I needed it recently on Windows and I'll post here how I did it in case someone else needs.
Thx to #VonC for pointing me in the right direction.
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procCreateMutex = kernel32.NewProc("CreateMutexW")
)
func CreateMutex(name string) (uintptr, error) {
ret, _, err := procCreateMutex.Call(
0,
0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(name))),
)
switch int(err.(syscall.Errno)) {
case 0:
return ret, nil
default:
return ret, err
}
}
// mutexName starting with "Global\" will work across all user sessions
_, err := CreateMutex("SomeMutexName")
I created a lib with a more complete example: https://github.com/rodolfoag/gow32
Thx!
There doesn't seem to be a cross-platform solution (beside writing a file, and looking for that file at start time).
On Windows, this thread reports
the recommended approach (and the one that has worked great for me) is to use the CreateSemaphore function.
If the name you specify starts with "Global\", then the semaphore is unique for the entire system and a second attempt to open it will fail.
This is a kernel32 call, which has some wrapper in Go available.
kostix adds in the comments:
look at the Go source code around the pkg\syscall hierarchy -- it contains a good wealth of examples on how to call out to DLLs on Windows using syscalls (and that's how you access anything in Windows API).
That would be syscall/dll_windows.go. (And here is a gist)
The odbc package by brainman is another example of direct API calls on Windows -- possibly easier to digest.
Like api/zapi_windows.go.
You could use sockets, simple to use and will work on everything really.
package main
import (
"fmt"
"net"
"os"
"strings"
)
const (
INSTANCE_PORT = 9292
)
func main() {
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", INSTANCE_PORT))
if err != nil {
if strings.Index(err.Error(), "in use") != -1 {
//optionally send command line arguments to the other instance
fmt.Fprintln(os.Stderr, "Already running.")
return
} else {
panic(err)
}
}
for {
conn, err := listener.Accept()
if err != nil {
println("Error accept:", err.Error())
return
}
go do_something_with(conn)
}
}
You could adapt the code from tendo's python library source
what they do is
for windows :
creating a file made of the executable absolute path (well it's a library, so in your case, you can just define an identifier, to prevent you from "i put the executable in 2 places")
For windows: trying first to remove the file if existing, and if not creating the file with os.O_CREAT | os.O_EXCL | os.O_RDWR
For POSIX compatible systems: trying first to remove the file if existing and if not creating the file and acquiring a lock on it using fcntl.LOCK_EX | fcntl.LOCK_NB
any failure mean the program is already running
and then you can use a defer action to remove the lock (on posix system) and delete the file
Go permit you to create both version wit a build comment to tell which file to compile depending on your OS so you have
for unix system
// +build !windows
package main
import (
"os"
"syscall"
)
func create_lock_file(filename string) (*os.File, error) {
file, err := os.OpenFile(filename, os.O_WRONLY, 0666)
if err != nil {
return nil, err
}
err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
if err != nil {
return nil, err
}
return file, nil
}
for windows:
// +build !windows
package main
import (
"os"
)
func create_lock_file(filename string) (*os.File, error) {
if _, err := os.Stat(filename); err == nil {
err = os.Remove(filename)
if err != nil {
return nil, err
}
}
return os.OpenFile(filename, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666)
}
and a test
package main
import (
"fmt"
"time"
)
func main() {
_, err := create_lock_file("plop.lock")
if err != nil {
fmt.Println("error ", err.Error())
}
time.Sleep(10 * time.Second)
fmt.Println("end ")
}
I've started a library out of it that you can find here
Improvements to this answer. (I am unsure if this answer will distort the original meaning, so I have written a new answer.)
Features:
deprecated: StringToUTF16Ptr is deprecated. Use UTF16PtrFromString instead.
Add the CloseHandle so that you can cancel the CreateMutexW.
package _test
import (
"syscall"
"testing"
"unsafe"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procCreateMutexW = kernel32.NewProc("CreateMutexW")
procCloseHandle = kernel32.NewProc("CloseHandle")
)
// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexW#return-value
func CreateMutexW(proc *syscall.LazyProc, name string) (uintptr, error) {
if proc.Name != "CreateMutexW" {
panic("proc.Name != CreateMutexW")
}
lpName, _ := syscall.UTF16PtrFromString(name) // LPCWSTR
if handleID, _, err := proc.Call(
0,
0,
uintptr(unsafe.Pointer(lpName)),
); err.(syscall.Errno) == 0 {
return handleID, nil
} else {
return handleID, err
}
}
// https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle?redirectedfrom=MSDN
func CloseHandle(proc *syscall.LazyProc, handle uintptr) error {
if proc.Name != "CloseHandle" {
panic("proc.Name != CloseHandle")
}
val, _, err := proc.Call(handle)
if val == 0 {
return err
}
return nil
}
func TestCreateMutexW(t *testing.T) {
handle, err := CreateMutexW(procCreateMutexW, "hello world")
if err != nil {
t.Fatalf(err.Error())
}
_, err = CreateMutexW(procCreateMutexW, "hello world")
if err == nil || err != syscall.ERROR_ALREADY_EXISTS {
t.Error("should panic")
}
if err = CloseHandle(procCloseHandle, handle); err != nil {
t.Error(err)
}
// We can create again since we have closed.
handle, _ = CreateMutexW(procCreateMutexW, "hello world")
if err = CloseHandle(procCloseHandle, handle); err != nil {
t.Error(err)
}
}

I am trying to traverse the file structure in Breadth first fashion. I am getting segmentation violation

I know about ioutil.ReadDir and os.filePath but none of them traverse the directory in Breadth first fashion.
My approach is to call ioutil.ReadDir and append all the contents of the root dir into a slice. Then I am iterating over the contents and checking if it IsDir[] and calling the function recursively if true.
package main
import (
"io/ioutil"
"os"
)
var files []string
var path string
func appendFiles(root string) {
fileInfo, err := ioutil.ReadDir(root)
if err != nil {
return
}
for _, file := range fileInfo {
files = append(files, file.Name())
}
for _, file := range fileInfo {
fileStat, _ := os.Stat(file.Name())
if fileStat.Mode().IsDir() {
// path = path + "/" + file.Name()
appendFiles(file.Name())
}
}
}
func main() {
appendFiles(".")
}
The problem is that os.Stat() may return an error which you omit. When that happens, fileStat may be nil, so calling fileStat.Mode() in the next line panics.
And the reason os.Stat() fails is because file.Name() is relative to root, file.Name() by itself has little chance to exist, it must be joined with root. If os.Stat() is called with a file name that doesn't exist, it returns a nil file info and a non-nil error.
You may use filepath.Join() to construct a valid path for files that os.Stat() will work with. And it would be better to handle errors, e.g. return them, which you can inspect in main().
func appendFiles(root string) error {
fileInfo, err := ioutil.ReadDir(root)
if err != nil {
return fmt.Errorf("ReadDir error: %w", err)
}
for _, file := range fileInfo {
files = append(files, filepath.Join(root, file.Name()))
}
for _, file := range fileInfo {
fullName := filepath.Join(root, file.Name())
fileStat, err := os.Stat(fullName)
if err != nil {
return fmt.Errorf("Stat error: %w", err)
}
if fileStat.Mode().IsDir() {
if err := appendFiles(fullName); err != nil {
return fmt.Errorf("appendFiles error: %w", err)
}
}
}
return nil
}
func main() {
if err := appendFiles("."); err != nil {
fmt.Println(err)
}
}

os.Create and os.Open not working with gomobile and react native

package component
import (
"encoding/json"
"io/ioutil"
"os"
)
type LastComponent struct {
Name string
}
const fname = "componentfiles"
func Persist(comp string) string {
lcomp := LastComponent{Name: comp}
b, err := json.Marshal(lcomp)
if err != nil {
return "err-MARSHAL"
}
file, err := os.Create(fname)
if err != nil {
return "err-CREATE-FILE"
}
defer file.Close()
_, err = file.Write(b)
if err != nil {
return "err-FILE-WRITE-PROB"
}
return ""
}
func Component() string {
f, err := os.Open(fname)
if err != nil {
return "err-FILE-NOT-OPEN"
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return ""
}
var v LastComponent
json.Unmarshal(b, v)
return v.Name
}
}
The code above works fine and so does the javascript side of code. I keep receiving err-CREATE-FILE inside my javascript. So os.Create and os.Open are not working as expected.
Although it is an internal storage, permissions are not required, but I also turned on the permissions in manifest file, but with no avail.
What could be the correct way to Open and Create files in android using gomobile when using along side React Native?
Update:
In adb logcat, I keep getting this all over the place
E/Vold ( 276): Failed to find mounted volume for /storage/sdcard1/Android/data/com.gotest/cache/
So you should have some success if you pass this in as a parameter - something like the following is working for me:
go:
func Component(folderPath string) string {
f, err := os.Open(path.Join(folderPath, fname))
...
Java:
Component.Component(getApplicationContext().getFilesDir().getAbsolutePath())
Alternatively, you could use something like getExternalStorageDirectory().getAbsolutePath(). They key is that you need to get somewhere storagewise that is writable by your process/user.

Restricting to Single Instance of Executable with Golang

I need to only allow one instance of my Golang executable at a time. I'm not sure how to use a Global Mutex to make sure no other instances are running.
This would be running on a Windows Machine.
I know this topic is a bit old, but I needed it recently on Windows and I'll post here how I did it in case someone else needs.
Thx to #VonC for pointing me in the right direction.
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procCreateMutex = kernel32.NewProc("CreateMutexW")
)
func CreateMutex(name string) (uintptr, error) {
ret, _, err := procCreateMutex.Call(
0,
0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(name))),
)
switch int(err.(syscall.Errno)) {
case 0:
return ret, nil
default:
return ret, err
}
}
// mutexName starting with "Global\" will work across all user sessions
_, err := CreateMutex("SomeMutexName")
I created a lib with a more complete example: https://github.com/rodolfoag/gow32
Thx!
There doesn't seem to be a cross-platform solution (beside writing a file, and looking for that file at start time).
On Windows, this thread reports
the recommended approach (and the one that has worked great for me) is to use the CreateSemaphore function.
If the name you specify starts with "Global\", then the semaphore is unique for the entire system and a second attempt to open it will fail.
This is a kernel32 call, which has some wrapper in Go available.
kostix adds in the comments:
look at the Go source code around the pkg\syscall hierarchy -- it contains a good wealth of examples on how to call out to DLLs on Windows using syscalls (and that's how you access anything in Windows API).
That would be syscall/dll_windows.go. (And here is a gist)
The odbc package by brainman is another example of direct API calls on Windows -- possibly easier to digest.
Like api/zapi_windows.go.
You could use sockets, simple to use and will work on everything really.
package main
import (
"fmt"
"net"
"os"
"strings"
)
const (
INSTANCE_PORT = 9292
)
func main() {
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", INSTANCE_PORT))
if err != nil {
if strings.Index(err.Error(), "in use") != -1 {
//optionally send command line arguments to the other instance
fmt.Fprintln(os.Stderr, "Already running.")
return
} else {
panic(err)
}
}
for {
conn, err := listener.Accept()
if err != nil {
println("Error accept:", err.Error())
return
}
go do_something_with(conn)
}
}
You could adapt the code from tendo's python library source
what they do is
for windows :
creating a file made of the executable absolute path (well it's a library, so in your case, you can just define an identifier, to prevent you from "i put the executable in 2 places")
For windows: trying first to remove the file if existing, and if not creating the file with os.O_CREAT | os.O_EXCL | os.O_RDWR
For POSIX compatible systems: trying first to remove the file if existing and if not creating the file and acquiring a lock on it using fcntl.LOCK_EX | fcntl.LOCK_NB
any failure mean the program is already running
and then you can use a defer action to remove the lock (on posix system) and delete the file
Go permit you to create both version wit a build comment to tell which file to compile depending on your OS so you have
for unix system
// +build !windows
package main
import (
"os"
"syscall"
)
func create_lock_file(filename string) (*os.File, error) {
file, err := os.OpenFile(filename, os.O_WRONLY, 0666)
if err != nil {
return nil, err
}
err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
if err != nil {
return nil, err
}
return file, nil
}
for windows:
// +build !windows
package main
import (
"os"
)
func create_lock_file(filename string) (*os.File, error) {
if _, err := os.Stat(filename); err == nil {
err = os.Remove(filename)
if err != nil {
return nil, err
}
}
return os.OpenFile(filename, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666)
}
and a test
package main
import (
"fmt"
"time"
)
func main() {
_, err := create_lock_file("plop.lock")
if err != nil {
fmt.Println("error ", err.Error())
}
time.Sleep(10 * time.Second)
fmt.Println("end ")
}
I've started a library out of it that you can find here
Improvements to this answer. (I am unsure if this answer will distort the original meaning, so I have written a new answer.)
Features:
deprecated: StringToUTF16Ptr is deprecated. Use UTF16PtrFromString instead.
Add the CloseHandle so that you can cancel the CreateMutexW.
package _test
import (
"syscall"
"testing"
"unsafe"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procCreateMutexW = kernel32.NewProc("CreateMutexW")
procCloseHandle = kernel32.NewProc("CloseHandle")
)
// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexW#return-value
func CreateMutexW(proc *syscall.LazyProc, name string) (uintptr, error) {
if proc.Name != "CreateMutexW" {
panic("proc.Name != CreateMutexW")
}
lpName, _ := syscall.UTF16PtrFromString(name) // LPCWSTR
if handleID, _, err := proc.Call(
0,
0,
uintptr(unsafe.Pointer(lpName)),
); err.(syscall.Errno) == 0 {
return handleID, nil
} else {
return handleID, err
}
}
// https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle?redirectedfrom=MSDN
func CloseHandle(proc *syscall.LazyProc, handle uintptr) error {
if proc.Name != "CloseHandle" {
panic("proc.Name != CloseHandle")
}
val, _, err := proc.Call(handle)
if val == 0 {
return err
}
return nil
}
func TestCreateMutexW(t *testing.T) {
handle, err := CreateMutexW(procCreateMutexW, "hello world")
if err != nil {
t.Fatalf(err.Error())
}
_, err = CreateMutexW(procCreateMutexW, "hello world")
if err == nil || err != syscall.ERROR_ALREADY_EXISTS {
t.Error("should panic")
}
if err = CloseHandle(procCloseHandle, handle); err != nil {
t.Error(err)
}
// We can create again since we have closed.
handle, _ = CreateMutexW(procCreateMutexW, "hello world")
if err = CloseHandle(procCloseHandle, handle); err != nil {
t.Error(err)
}
}

Resources