FUSE (Bazil-Go): how to implement MkDir request? - go

I am trying to implement the function mkdir in a fuse, written in Go,and I'm using Bazil library. I have successfully implemented a simple read-only fs, and I now want to be able to call mkdir inside any existing directory to make a new one.
I have made sure that all the existing directories are writable,(attr.Mode = os.ModeDir | 0777).
Right now I have just added the function:
func (d Dir) MkDir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
dir := &Dir{name: req.Name, files: 0, inode: 10 /*a random inode*/,mode: os.FileMode(0777),nextdir: nil, nextfile: nil}
d.nextdir = dir
return dir, nil
}
in my own implementation of the hello fs example of Bazil's library. But that doesn't seem to make any difference.
When I call mkdir new_dir_name from the terminal, I get the error: "mkdir: cannot create directory ‘new_dir_name’: Operation not permitted", even though I have added the mkdir function.
Any insights as to why this is happening, and what else should I add to my code to make this working would be great. Also, this is my first stackoverflow question, so I'm sorry if I didn't ask in a clear way.

the correct function for make directory like this
func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
}

Related

Gcloud functions deployment doesn't find Golang template files

I have written some Golang code which works when tested on my local machine. When I deploy this as a Google Cloud function it fails because it cannot open a template file. The line of code failing is:
t, err := template.New("list.gohtml").ParseFiles("list.gohtml")
After this call err is set to open list.gohtml: no such file or directory
The file is in the same directory as the go source file and is not listed in .gcloudignore or .gitignore. The gcloud functions documentation says all files in the directory will be uploaded unless listed in one of those ignore files and if I run gcloud meta list-files-for-upload then the file list.gohtml is included in the list displayed.
Is there some magic folder layout to make this work, or an option to the gcloud functions deploy command?
Based on #DazWilkin's reply, I now call the function below at the start of the serving function.
Rather than hardwiring the path into the template file names (which would make it fail when tested locally) this simply checks for the presence of the Gcloud source file directory below the current one, and if present, makes it the working directory, so file resolution will now happen exactly as it does when tested locally.
import "os"
const gcloudFuncSourceDir = "serverless_function_source_code"
func fixDir() {
fileInfo, err := os.Stat(gcloudFuncSourceDir)
if err == nil && fileInfo.IsDir() {
_ = os.Chdir(gcloudFuncSourceDir)
}
}
I created a Function that enumerates the uploaded files:
func L(w http.ResponseWriter, r *http.Request) {
var files []string
err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
files = append(files, path)
return nil
})
if err != nil {
panic(err)
}
for _, file := range files {
fmt.Fprintln(w, file)
}
}
It output:
.
go.mod
go.sum
main.go
serverless_function_source_code
serverless_function_source_code/f.go
serverless_function_source_code/go.mod
serverless_function_source_code/test.tmpl
Rewriting the template-using function:
tmpl, err := template.New("test.tmpl").ParseFiles("serverless_function_source_code/test.tmpl")
Works!
However, this is undocumented and a hack :-(
The alternative is to embed the templates as strings within Golang files.
I recommend submitting a feature request on Google's Issue Tracker for Cloud Functions
If there's no go.mod file (in other words, if modules aren't enabled), then the behavior changes and the files are under src/<package-name>/, not under serverless_function_source_code/. The main.go file used to drive the cloud function resides in src/serverless_function_app/main/main.go.
For example:
I have a Go application with the following two files:
listfiles/file_system.go
listfiles/tmpl/letter.html
I deployed it with these commands:
cd listfiles
gcloud functions deploy ListFiles --runtime go113 --trigger-http --allow-unauthenticated
The result was that the current directory was set to /srv. Underneath /srv was a typical GOROOT tree with src/ and pkg/ directories. For example:
.googlebuild
.googlebuild/source-code.tar.gz
pkg
pkg/linux_amd64
pkg/linux_amd64/github.com
pkg/linux_amd64/github.com/GoogleCloudPlatform
pkg/linux_amd64/github.com/GoogleCloudPlatform/functions-framework-go
pkg/linux_amd64/github.com/GoogleCloudPlatform/functions-framework-go/funcframework.a
src
src/cloud.google.com
src/cloud.google.com/go
src/cloud.google.com/go/.git
src/cloud.google.com/go/.git/HEAD
src/cloud.google.com/go/.git/branches
[... snip...]
src/go.uber.org/zap/zaptest/testingt_test.go
src/go.uber.org/zap/zaptest/timeout.go
src/go.uber.org/zap/zaptest/timeout_test.go
src/go.uber.org/zap/zaptest/writer.go
src/go.uber.org/zap/zaptest/writer_test.go
src/listfiles
src/listfiles/file_system.go
src/listfiles/tmpl
src/listfiles/tmpl/letter.html
src/serverless_function_app
src/serverless_function_app/main
src/serverless_function_app/main/main.go

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.

Recursively create a directory with a certain owner and group

I would like to recursively create a directory and assign an owner and group for the folders and its parents that were created.
For example, assuming /var exists, I want to create /var/test1/test2/test3.
I am able to do this using os.MkdirAll("/var/test1/test2/test3", 0600).
However, I also want to set test1, test2, and test3's uid to user1 and gid to user1.
It's possible to do so using os.Chown, but that requires a lot of manually work. I would need build a tree of the folder and its parents that do not exist before creating the folder chain and then use os.Chown on each folder after creation.
Is there a simpler way?
A bit like this ChownR() type of function, the idea would be to filter the walk, and apply the chown only to folders which are part of a path passed in parameter (here "/var/test1/test2/test3)
Without filter:
func ChownR(path string, uid, gid int) error {
return filepath.Walk(path, func(name string, info os.FileInfo, err error) error {
if err == nil {
err = os.Chown(name, uid, gid)
}
return err
})
}
In other words, with filepath.Walk(), there should not be too much "manual work" involved here.
Switch the mkdir process to the user you wish to create directories for. This has the advantage of creating directories with correct permissions and ownership in a single command instead of creating loops or running multiple commands back to back.
Example:
import "os/exec"
import "syscall"
func main() {
// The command here can use flags to create all of the dirs at once
cmd := exec.Command('mkdir', '-p', '/var/test1/test2/test3/')
_ = syscall.Umask(0077) // Set umask for this process
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
cmd.Run()
}
Since the subprocess "mkdir" runs as the user specified by uid, the folders would already have the correct owner. If you get the umask part right, each directory will also have correct permissions.
One caveat: this is not a very portable solution. Using syscall should only be your solution if you are certain your code will only be executing on a specific OS.

Webdav Server in Go

I want to implement a webdav-server with Go and found a new "x" package here:
But I don't know how to use this package to get it done.
Can someone help me with this issue?
I tried this:
func main(){
fs := new(webdav.FileSystem)
ls := new(webdav.LockSystem)
h := new(webdav.Handler)
h.FileSystem = *fs
h.LockSystem = *ls
//then use the Handler.ServeHTTP Method as the http.HandleFunc
http.HandleFunc("/", h.ServeHTTP)
http.ListenAndServe(":5555", nil)
}
If I try to connect to the server, I get an internal server error.
What am I doing wrong?
Thanks for your help.
The x/net/webdav is still in early phase of development. Many critical parts are still being implemented, and it can not be used as such at this moment. Taking a look at the source code over half of the necessary structures and functions are still completely missing.
Unfortunately there are no Go based webdav server implementations at this moment. (In case someone can correct me, please feel free to do so!)
func main(){
fs := new(webdav.FileSystem)
ls := new(webdav.LockSystem)
h := new(webdav.Handler)
h.FileSystem = fs
h.LockSystem = ls
//then use the Handler.ServeHTTP Method as the http.HandleFunc
http.HandleFunc("/", h.ServeHTTP)
http.ListenAndServe(":5555", nil)
}
try to remove the * before "fs" and "ls" because they are already pointers.
NB : if you have to assign pointer use & and not *
Create a webdav server on http://localhost:8080/ which mounts the folder C:\myfiles.
package main
import (
"net/http"
"golang.org/x/net/webdav"
)
func main() {
handler := &webdav.Handler{
FileSystem: webdav.Dir(`C:\myfiles`),
LockSystem: webdav.NewMemLS(),
}
http.ListenAndServe("localhost:8080", handler)
}
Mount to Letter E: in windows:
net use e: http://localhost:8080/
Open mounted drive in explorer
explorer.exe e:

resolve symlinks in Go

How can I resolve symlinks in Go?
Currently I call readlink -f but I want something more idiomatic.
package main
import (
"os/exec"
"fmt"
)
func resolve(p string) string {
cmd := exec.Command("readlink", "-f", p)
out, _ := cmd.Output()
return (string(out))
}
func main() {
fmt.Printf(resolve("/initrd.img"))
}
See filepath.EvalSymlinks().
EvalSymlinks returns the path name after the evaluation of any symbolic links. If path is relative the result will be relative to the current directory, unless one of the components is an absolute symbolic link.
Examples
Tree:
/bin/sh -> bash
/usr/lib/libresolv.so -> ../../lib/libresolv.so.2
os.Readlink()
os.Readlink("/bin/sh") // => bash
os.Readlink("/usr/lib/libresolv.so") //=> ../../lib/libresolv.so.2
filepath.EvalSymlinks()
filepath.EvalSymlinks("/bin/sh") // => /bin/bash
filepath.EvalSymlinks("/usr/lib/libresolv.so") //=> /lib/libresolv-2.20.so
Note: not absolute path (cd /bin)
filepath.EvalSymlinks("sh") // => bash
filepath.EvalSymlinks("/bin/sh") // => /bin/bash
And somthing more
filepath.EvalSymlinks("/bin/bash") // => /bin/bash
// but
os.Readlink("/bin/bash") // => error: readlink /bin/bash: invalid argument
Example application not for playground
Use os.Lstat:
func Lstat(name string) (fi FileInfo, err error)
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.
EDIT:
Then returned os.FileInfo will only allow you to check if 'name' is a link or not (fi.Mode() & os.ModeSymlink != 0). If it is, then use os.Readlink to get the pointee:
func Readlink(name string) (string, error)
Readlink returns the destination of the named symbolic link. If there is an error, it will be of type *PathError.
I had the problem that filepath.EvalSymlinks() prepended /private/ to resolved links that were in /tmp/ on Mac (maybe symlinks are not supposed to be pointing there).
os.Readlink() on the other hand, returned an absolute path on Mac, but a relative path on Ubuntu.
Therefore, to be sure to retrieve an absolute path, the following solution worked for me:
resolvedLink, _ := os.Readlink(someSymlink) // TODO: Handle error here
if !filepath.IsAbs(resolvedLink) { // Output of os.Readlink is OS-dependent...
resolvedLink = path.Join(path.Dir(someSymlink), resolvedLink)
}

Resources