未验证 提交 83353897 编写于 作者: D Davies Liu 提交者: GitHub

Support Windows (#195)

* prepare for windows

* build for windows

* build for windows

* git diff

* remove libfuse-dev

* cleanup

* build juicefs.exe in Linux

* make with sudo

* fix travis

* fix travis

* add license header

* size of dir

* no syslog and run-in-background in windows

* fix compatibility with windows 10

* graceful umount

* fix build in Windows
上级 ae9499fa
......@@ -12,10 +12,12 @@ addons:
- libacl1-dev
- redis-server
- attr
- gcc-mingw-w64 # for windows
install: true
before_script:
- export GO111MODULE=on
- make
- make juicefs.exe
- sudo make -C fstests setup
script:
- make test
......
......@@ -30,6 +30,13 @@ juicefs: Makefile cmd/*.go pkg/*/*.go
juicefs.ceph: Makefile cmd/*.go pkg/*/*.go
go build -tags ceph -ldflags="$(LDFLAGS)" -o juicefs.ceph ./cmd
/usr/local/include/winfsp:
sudo mkdir -p /usr/local/include/winfsp
sudo cp hack/winfsp_headers/* /usr/local/include/winfsp
juicefs.exe: /usr/local/include/winfsp cmd/*.go pkg/*/*.go
GOOS=windows CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -ldflags="$(LDFLAGS)" -o juicefs.exe ./cmd
.PHONY: snapshot release test
snapshot:
docker run --rm --privileged \
......
......@@ -30,13 +30,11 @@ import (
"time"
"github.com/google/gops/agent"
"github.com/juicedata/godaemon"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/urfave/cli/v2"
"github.com/juicedata/juicefs/pkg/chunk"
"github.com/juicedata/juicefs/pkg/fuse"
"github.com/juicedata/juicefs/pkg/meta"
"github.com/juicedata/juicefs/pkg/object"
"github.com/juicedata/juicefs/pkg/usage"
......@@ -93,11 +91,6 @@ func updateMetrics(m meta.Meta) {
}
}
func makeDaemon(onExit func(int) error) error {
_, _, err := godaemon.MakeDaemon(&godaemon.DaemonAttr{OnExit: onExit})
return err
}
func installHandler(mp string) {
// Go will catch all the signals
signal.Ignore(syscall.SIGPIPE)
......@@ -133,10 +126,6 @@ func mount(c *cli.Context) error {
}
}()
setLoggerLevel(c)
if !c.Bool("no-syslog") {
// The default log to syslog is only in daemon mode.
utils.InitLoggers(c.Bool("background"))
}
if c.Args().Len() < 1 {
logger.Fatalf("Redis URL and mountpoint are required")
}
......@@ -148,7 +137,7 @@ func mount(c *cli.Context) error {
logger.Fatalf("MOUNTPOINT is required")
}
mp := c.Args().Get(1)
if !utils.Exists(mp) {
if !strings.Contains(mp, ":") && !utils.Exists(mp) {
if err := os.MkdirAll(mp, 0777); err != nil {
logger.Fatalf("create %s: %s", mp, err)
}
......@@ -196,30 +185,6 @@ func mount(c *cli.Context) error {
}
logger.Infof("Data use %s", blob)
blob = object.WithMetrics(blob)
logger.Infof("Mounting volume %s at %s ...", format.Name, mp)
if c.Bool("background") && os.Getenv("JFS_FOREGROUND") == "" {
err := makeDaemon(func(stage int) error {
if stage != 0 {
return nil
}
for {
time.Sleep(time.Millisecond * 50)
st, err := os.Stat(mp)
if err == nil {
if sys, ok := st.Sys().(*syscall.Stat_t); ok && sys.Ino == 1 {
logger.Infof("\033[92mOK\033[0m, %s is ready at %s", format.Name, mp)
break
}
}
}
return nil
})
if err != nil {
logger.Fatalf("Failed to make daemon: %s", err)
}
}
store := chunk.NewCachedStore(blob, chunkConf)
m.OnMsg(meta.DeleteChunk, meta.MsgCallback(func(args ...interface{}) error {
chunkid := args[0].(uint64)
......@@ -263,16 +228,16 @@ func mount(c *cli.Context) error {
if !c.Bool("no-usage-report") {
go usage.ReportUsage(m, version.Version())
}
err = fuse.Serve(conf, c.String("o"), c.Float64("attr-cache"), c.Float64("entry-cache"), c.Float64("dir-entry-cache"), c.Bool("enable-xattr"))
if err != nil {
logger.Fatalf("fuse: %s", err)
}
mount_main(conf, m, store, c)
return nil
}
func clientFlags() []cli.Flag {
var defaultCacheDir = "/var/jfsCache"
if runtime.GOOS == "darwin" {
switch runtime.GOOS {
case "darwin":
fallthrough
case "windows":
homeDir, err := os.UserHomeDir()
if err != nil {
logger.Fatalf("%v", err)
......@@ -345,40 +310,6 @@ func mountFlags() *cli.Command {
ArgsUsage: "REDIS-URL MOUNTPOINT",
Action: mount,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "d",
Aliases: []string{"background"},
Usage: "run in background",
},
&cli.BoolFlag{
Name: "no-syslog",
Usage: "disable syslog",
},
&cli.StringFlag{
Name: "o",
Usage: "other FUSE options",
},
&cli.Float64Flag{
Name: "attr-cache",
Value: 1.0,
Usage: "attributes cache timeout in seconds",
},
&cli.Float64Flag{
Name: "entry-cache",
Value: 1.0,
Usage: "file entry cache timeout in seconds",
},
&cli.Float64Flag{
Name: "dir-entry-cache",
Value: 1.0,
Usage: "dir entry cache timeout in seconds",
},
&cli.BoolFlag{
Name: "enable-xattr",
Usage: "enable extended attributes (xattr)",
},
&cli.StringFlag{
Name: "metrics",
Value: ":9567",
......@@ -390,6 +321,7 @@ func mountFlags() *cli.Command {
},
},
}
cmd.Flags = append(cmd.Flags, mount_flags()...)
cmd.Flags = append(cmd.Flags, clientFlags()...)
return cmd
}
// +build !windows
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"os"
"syscall"
"time"
"github.com/juicedata/godaemon"
"github.com/juicedata/juicefs/pkg/chunk"
"github.com/juicedata/juicefs/pkg/fuse"
"github.com/juicedata/juicefs/pkg/meta"
"github.com/juicedata/juicefs/pkg/utils"
"github.com/juicedata/juicefs/pkg/vfs"
"github.com/urfave/cli/v2"
)
func makeDaemon(name, mp string) error {
onExit := func(stage int) error {
if stage != 0 {
return nil
}
for {
time.Sleep(time.Millisecond * 50)
st, err := os.Stat(mp)
if err == nil {
if sys, ok := st.Sys().(*syscall.Stat_t); ok && sys.Ino == 1 {
logger.Infof("\033[92mOK\033[0m, %s is ready at %s", name, mp)
break
}
}
}
return nil
}
_, _, err := godaemon.MakeDaemon(&godaemon.DaemonAttr{OnExit: onExit})
return err
}
func mount_flags() []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: "d",
Aliases: []string{"background"},
Usage: "run in background",
},
&cli.BoolFlag{
Name: "no-syslog",
Usage: "disable syslog",
},
&cli.StringFlag{
Name: "o",
Usage: "other FUSE options",
},
&cli.Float64Flag{
Name: "attr-cache",
Value: 1.0,
Usage: "attributes cache timeout in seconds",
},
&cli.Float64Flag{
Name: "entry-cache",
Value: 1.0,
Usage: "file entry cache timeout in seconds",
},
&cli.Float64Flag{
Name: "dir-entry-cache",
Value: 1.0,
Usage: "dir entry cache timeout in seconds",
},
&cli.BoolFlag{
Name: "enable-xattr",
Usage: "enable extended attributes (xattr)",
},
}
}
func mount_main(conf *vfs.Config, m meta.Meta, store chunk.ChunkStore, c *cli.Context) {
logger.Infof("Mounting volume %s at %s ...", conf.Format.Name, conf.Mountpoint)
if c.Bool("background") && os.Getenv("JFS_FOREGROUND") == "" {
// The default log to syslog is only in daemon mode.
utils.InitLoggers(!c.Bool("no-syslog"))
err := makeDaemon(conf.Format.Name, conf.Mountpoint)
if err != nil {
logger.Fatalf("Failed to make daemon: %s", err)
}
}
err := fuse.Serve(conf, c.String("o"), c.Float64("attr-cache"), c.Float64("entry-cache"), c.Float64("dir-entry-cache"), c.Bool("enable-xattr"))
if err != nil {
logger.Fatalf("fuse: %s", err)
}
}
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/juicedata/juicefs/pkg/chunk"
"github.com/juicedata/juicefs/pkg/fs"
"github.com/juicedata/juicefs/pkg/meta"
"github.com/juicedata/juicefs/pkg/vfs"
"github.com/juicedata/juicefs/pkg/winfsp"
"github.com/urfave/cli/v2"
)
func mount_flags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "o",
Usage: "other FUSE options",
},
&cli.BoolFlag{
Name: "as-root",
Usage: "Access files as administrator",
},
&cli.Float64Flag{
Name: "file-cache-to",
Value: 0.1,
Usage: "Cache file attributes in seconds",
},
&cli.Float64Flag{
Name: "delay-close",
Usage: "delay file closing in seconds.",
},
}
}
func mount_main(conf *vfs.Config, m meta.Meta, store chunk.ChunkStore, c *cli.Context) {
jfs, err := fs.NewFileSystem(conf, m, store)
if err != nil {
logger.Fatalf("Initialize failed: %s", err)
}
winfsp.Serve(conf, jfs, c.String("o"), c.Float64("file-cache-to"), c.Bool("as-root"), c.Int("delay-close"))
}
......@@ -19,6 +19,7 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
"syscall"
"github.com/juicedata/juicefs/pkg/meta"
......@@ -44,6 +45,10 @@ func openControler(path string) *os.File {
}
func rmr(ctx *cli.Context) error {
if runtime.GOOS == "windows" {
logger.Infof("Windows is not supported")
return nil
}
if ctx.Args().Len() < 1 {
logger.Infof("PATH is needed")
return nil
......@@ -77,7 +82,11 @@ func rmr(ctx *cli.Context) error {
logger.Fatalf("read message: %d %s", n, err)
}
if errs[0] != 0 {
logger.Fatalf("RMR %s: %s", path, syscall.Errno(errs[0]))
errno := syscall.Errno(errs[0])
if runtime.GOOS == "windows" {
errno += 0x20000000
}
logger.Fatalf("RMR %s: %s", path, errno)
}
_ = f.Close()
}
......
/*
* JuiceFS, Copyright (C) 2018 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"github.com/urfave/cli/v2"
......@@ -46,6 +63,13 @@ func umount(ctx *cli.Context) error {
} else {
cmd = exec.Command("umount", mp)
}
case "windows":
if !force {
_ = os.Mkdir(filepath.Join(mp, ".UMOUNTIT"), 0755)
return nil
} else {
cmd = exec.Command("taskkill", "/IM", "juicefs.exe", "/F")
}
default:
return fmt.Errorf("OS %s is not supported", runtime.GOOS)
}
......
......@@ -50,9 +50,9 @@ github.com/NetEase-Object-Storage/nos-golang-sdk v0.0.0-20171031020902-cc8892cb2
github.com/NetEase-Object-Storage/nos-golang-sdk v0.0.0-20171031020902-cc8892cb2b05/go.mod h1:0N5CbwYI/8V1T6YOEwkgMvLmiGDNn661vLutBZQrC2c=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/sarama v1.27.2 h1:1EyY1dsxNDUQEv0O/4TsjosHI2CgB1uo9H/v56xzTxc=
github.com/Shopify/sarama v1.27.2/go.mod h1:g5s5osgELxgM+Md9Qni9rzo7Rbt+vvFQI4bt/Mc93II=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
......@@ -88,6 +88,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/billziss-gh/cgofuse v1.4.0 h1:kju2jDmdNuDDCrxPob2ggmZr5Mj/odCjU1Y8kx0Th9E=
github.com/billziss-gh/cgofuse v1.4.0/go.mod h1:LJjoaUojlVjgo5GQoEJTcJNqZJeRU0nCR84CyxKt2YM=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
......@@ -118,7 +120,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4=
github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
......@@ -133,11 +134,8 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/eclipse/paho.mqtt.golang v1.3.0 h1:MU79lqr3FKNKbSrGN7d7bNYqh8MwWW7Zcx0iG+VIw9I=
github.com/eclipse/paho.mqtt.golang v1.3.0/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
......@@ -177,6 +175,7 @@ github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjR
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-redis/redis/v8 v8.4.0 h1:J5NCReIgh3QgUJu398hUncxDExN4gMOHI11NVbVicGQ=
github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
......@@ -215,6 +214,8 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.3 h1:HR0kYDX2RJZvAup8CsiJwxB4dTCSC0AaUq6S4SiLwUc=
github.com/gomodule/redigo v1.8.3/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/gonutz/w32/v2 v2.2.0 h1:XVC/Kd238O+gadaDEB+E6E+kn/UBhj2UNOS8v9QPc4Q=
github.com/gonutz/w32/v2 v2.2.0/go.mod h1:MgtHx0AScDVNKyB+kjyPder4xIi3XAcHS6LDDU2DmdE=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
......@@ -336,7 +337,6 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
......@@ -394,7 +394,6 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
......@@ -425,12 +424,8 @@ github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLT
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/md5-simd v1.1.1 h1:9ojcLbuZ4gXbB2sX53MKn8JUZ0sB/2wfwsEcRw+I08U=
github.com/minio/md5-simd v1.1.1/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/minio v0.0.0-20210206053228-97fe57bba92c h1:XaHGnbk00zoZVUMhfYfL5j5HemOf1t83x3EwR61FaEg=
github.com/minio/minio v0.0.0-20210206053228-97fe57bba92c/go.mod h1:5OI7MooT/sE4Pl0sMfz1smx835bs1PIcrxGKiQ7jJY8=
github.com/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJDswNVuni6km9o=
github.com/minio/minio-go v6.0.14+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8=
github.com/minio/minio-go/v7 v7.0.8 h1:snnHtYkHz3TKrQJY1jTQGOZqnue79pbYTukuwqz/QvM=
github.com/minio/minio-go/v7 v7.0.8/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc=
github.com/minio/minio-go/v7 v7.0.10 h1:1oUKe4EOPUEhw2qnPQaPsJ0lmVTYLFu03SiItauXs94=
github.com/minio/minio-go/v7 v7.0.10/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo=
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
......@@ -498,7 +493,6 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olivere/elastic/v7 v7.0.22 h1:esBA6JJwvYgfms0EVlH7Z+9J4oQ/WUADF2y/nCNDw7s=
github.com/olivere/elastic/v7 v7.0.22/go.mod h1:VDexNy9NjmtAkrjNoI7tImv7FR4tf5zUA3ickqu5Pc8=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
......@@ -576,7 +570,6 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/qiniu/api.v7/v7 v7.8.0 h1:Ye9sHXwCpeDgKJ4BNSoDvXe4yEuU8a/HTT1jKRgkqe8=
github.com/qiniu/api.v7/v7 v7.8.0/go.mod h1:J7pD9UsnxO7XxyRLUHpsWEQd/HgWJNwnn/Za9qEPdEA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8=
github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
......@@ -613,7 +606,6 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
......@@ -646,10 +638,7 @@ github.com/tidwall/sjson v1.0.4 h1:UcdIRXff12Lpnu3OLtZvnc03g4vH2suXDXhBwBqmzYg=
github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y=
github.com/tinylib/msgp v1.1.3 h1:3giwAkmtaEDLSV0MdO1lDLuPgklgPzmk8H9+So2BVfA=
github.com/tinylib/msgp v1.1.3/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.5 h1:2gXmtWueD2HefZHQe1QOy9HVzmFrLOVvsXwXBQ0ayy0=
github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/upyun/go-sdk v2.1.0+incompatible h1:OdjXghQ/TVetWV16Pz3C1/SUpjhGBVPr+cLiqZLLyq0=
github.com/upyun/go-sdk v2.1.0+incompatible/go.mod h1:eu3F5Uz4b9ZE5bE5QsCL6mgSNWRwfj0zpJ9J626HEqs=
......@@ -846,7 +835,6 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
......@@ -899,14 +887,10 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM=
gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg=
gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU=
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
......
/**
* @file fuse/fuse.h
* WinFsp FUSE compatible API.
*
* This file is derived from libfuse/include/fuse.h:
* FUSE: Filesystem in Userspace
* Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
*
* @copyright 2015-2020 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
#ifndef FUSE_H_
#define FUSE_H_
#include "fuse_common.h"
#ifdef __cplusplus
extern "C" {
#endif
struct fuse;
typedef int (*fuse_fill_dir_t)(void *buf, const char *name,
const struct fuse_stat *stbuf, fuse_off_t off);
typedef struct fuse_dirhandle *fuse_dirh_t;
typedef int (*fuse_dirfil_t)(fuse_dirh_t h, const char *name,
int type, fuse_ino_t ino);
struct fuse_operations
{
/* S - supported by WinFsp */
/* S */ int (*getattr)(const char *path, struct fuse_stat *stbuf);
/* S */ int (*getdir)(const char *path, fuse_dirh_t h, fuse_dirfil_t filler);
/* S */ int (*readlink)(const char *path, char *buf, size_t size);
/* S */ int (*mknod)(const char *path, fuse_mode_t mode, fuse_dev_t dev);
/* S */ int (*mkdir)(const char *path, fuse_mode_t mode);
/* S */ int (*unlink)(const char *path);
/* S */ int (*rmdir)(const char *path);
/* S */ int (*symlink)(const char *dstpath, const char *srcpath);
/* S */ int (*rename)(const char *oldpath, const char *newpath);
/* _ */ int (*link)(const char *srcpath, const char *dstpath);
/* S */ int (*chmod)(const char *path, fuse_mode_t mode);
/* S */ int (*chown)(const char *path, fuse_uid_t uid, fuse_gid_t gid);
/* S */ int (*truncate)(const char *path, fuse_off_t size);
/* S */ int (*utime)(const char *path, struct fuse_utimbuf *timbuf);
/* S */ int (*open)(const char *path, struct fuse_file_info *fi);
/* S */ int (*read)(const char *path, char *buf, size_t size, fuse_off_t off,
struct fuse_file_info *fi);
/* S */ int (*write)(const char *path, const char *buf, size_t size, fuse_off_t off,
struct fuse_file_info *fi);
/* S */ int (*statfs)(const char *path, struct fuse_statvfs *stbuf);
/* S */ int (*flush)(const char *path, struct fuse_file_info *fi);
/* S */ int (*release)(const char *path, struct fuse_file_info *fi);
/* S */ int (*fsync)(const char *path, int datasync, struct fuse_file_info *fi);
/* S */ int (*setxattr)(const char *path, const char *name, const char *value, size_t size,
int flags);
/* S */ int (*getxattr)(const char *path, const char *name, char *value, size_t size);
/* S */ int (*listxattr)(const char *path, char *namebuf, size_t size);
/* S */ int (*removexattr)(const char *path, const char *name);
/* S */ int (*opendir)(const char *path, struct fuse_file_info *fi);
/* S */ int (*readdir)(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off,
struct fuse_file_info *fi);
/* S */ int (*releasedir)(const char *path, struct fuse_file_info *fi);
/* S */ int (*fsyncdir)(const char *path, int datasync, struct fuse_file_info *fi);
/* S */ void *(*init)(struct fuse_conn_info *conn);
/* S */ void (*destroy)(void *data);
/* _ */ int (*access)(const char *path, int mask);
/* S */ int (*create)(const char *path, fuse_mode_t mode, struct fuse_file_info *fi);
/* S */ int (*ftruncate)(const char *path, fuse_off_t off, struct fuse_file_info *fi);
/* S */ int (*fgetattr)(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi);
/* _ */ int (*lock)(const char *path,
struct fuse_file_info *fi, int cmd, struct fuse_flock *lock);
/* S */ int (*utimens)(const char *path, const struct fuse_timespec tv[2]);
/* _ */ int (*bmap)(const char *path, size_t blocksize, uint64_t *idx);
/* _ */ unsigned int flag_nullpath_ok:1;
/* _ */ unsigned int flag_nopath:1;
/* _ */ unsigned int flag_utime_omit_ok:1;
/* _ */ unsigned int flag_reserved:29;
/* S */ int (*ioctl)(const char *path, int cmd, void *arg, struct fuse_file_info *fi,
unsigned int flags, void *data);
/* _ */ int (*poll)(const char *path, struct fuse_file_info *fi,
struct fuse_pollhandle *ph, unsigned *reventsp);
/* FUSE 2.9 */
/* _ */ int (*write_buf)(const char *path,
struct fuse_bufvec *buf, fuse_off_t off, struct fuse_file_info *fi);
/* _ */ int (*read_buf)(const char *path,
struct fuse_bufvec **bufp, size_t size, fuse_off_t off, struct fuse_file_info *fi);
/* _ */ int (*flock)(const char *path, struct fuse_file_info *, int op);
/* _ */ int (*fallocate)(const char *path, int mode, fuse_off_t off, fuse_off_t len,
struct fuse_file_info *fi);
/* OSXFUSE */
/* _ */ int (*reserved00)();
/* _ */ int (*reserved01)();
/* _ */ int (*reserved02)();
/* _ */ int (*statfs_x)(const char *path, struct fuse_statfs *stbuf);
/* _ */ int (*setvolname)(const char *volname);
/* _ */ int (*exchange)(const char *oldpath, const char *newpath, unsigned long flags);
/* _ */ int (*getxtimes)(const char *path,
struct fuse_timespec *bkuptime, struct fuse_timespec *crtime);
/* _ */ int (*setbkuptime)(const char *path, const struct fuse_timespec *tv);
/* S */ int (*setchgtime)(const char *path, const struct fuse_timespec *tv);
/* S */ int (*setcrtime)(const char *path, const struct fuse_timespec *tv);
/* S */ int (*chflags)(const char *path, uint32_t flags);
/* _ */ int (*setattr_x)(const char *path, struct fuse_setattr_x *attr);
/* _ */ int (*fsetattr_x)(const char *path, struct fuse_setattr_x *attr,
struct fuse_file_info *fi);
};
struct fuse_context
{
struct fuse *fuse;
fuse_uid_t uid;
fuse_gid_t gid;
fuse_pid_t pid;
void *private_data;
fuse_mode_t umask;
};
#define fuse_main(argc, argv, ops, data)\
fuse_main_real(argc, argv, ops, sizeof *(ops), data)
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_main_real)(struct fsp_fuse_env *env,
int argc, char *argv[],
const struct fuse_operations *ops, size_t opsize, void *data);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_is_lib_option)(struct fsp_fuse_env *env,
const char *opt);
FSP_FUSE_API struct fuse *FSP_FUSE_API_NAME(fsp_fuse_new)(struct fsp_fuse_env *env,
struct fuse_chan *ch, struct fuse_args *args,
const struct fuse_operations *ops, size_t opsize, void *data);
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_destroy)(struct fsp_fuse_env *env,
struct fuse *f);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_loop)(struct fsp_fuse_env *env,
struct fuse *f);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_loop_mt)(struct fsp_fuse_env *env,
struct fuse *f);
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_exit)(struct fsp_fuse_env *env,
struct fuse *f);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_exited)(struct fsp_fuse_env *env,
struct fuse *f);
FSP_FUSE_API struct fuse_context *FSP_FUSE_API_NAME(fsp_fuse_get_context)(struct fsp_fuse_env *env);
FSP_FUSE_SYM(
int fuse_main_real(int argc, char *argv[],
const struct fuse_operations *ops, size_t opsize, void *data),
{
return FSP_FUSE_API_CALL(fsp_fuse_main_real)
(fsp_fuse_env(), argc, argv, ops, opsize, data);
})
FSP_FUSE_SYM(
int fuse_is_lib_option(const char *opt),
{
return FSP_FUSE_API_CALL(fsp_fuse_is_lib_option)
(fsp_fuse_env(), opt);
})
FSP_FUSE_SYM(
struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
const struct fuse_operations *ops, size_t opsize, void *data),
{
return FSP_FUSE_API_CALL(fsp_fuse_new)
(fsp_fuse_env(), ch, args, ops, opsize, data);
})
FSP_FUSE_SYM(
void fuse_destroy(struct fuse *f),
{
FSP_FUSE_API_CALL(fsp_fuse_destroy)
(fsp_fuse_env(), f);
})
FSP_FUSE_SYM(
int fuse_loop(struct fuse *f),
{
return FSP_FUSE_API_CALL(fsp_fuse_loop)
(fsp_fuse_env(), f);
})
FSP_FUSE_SYM(
int fuse_loop_mt(struct fuse *f),
{
return FSP_FUSE_API_CALL(fsp_fuse_loop_mt)
(fsp_fuse_env(), f);
})
FSP_FUSE_SYM(
void fuse_exit(struct fuse *f),
{
FSP_FUSE_API_CALL(fsp_fuse_exit)
(fsp_fuse_env(), f);
})
FSP_FUSE_SYM(
int fuse_exited(struct fuse *f),
{
return FSP_FUSE_API_CALL(fsp_fuse_exited)
(fsp_fuse_env(), f);
})
FSP_FUSE_SYM(
struct fuse_context *fuse_get_context(void),
{
return FSP_FUSE_API_CALL(fsp_fuse_get_context)
(fsp_fuse_env());
})
FSP_FUSE_SYM(
int fuse_getgroups(int size, fuse_gid_t list[]),
{
(void)size;
(void)list;
return -ENOSYS;
})
FSP_FUSE_SYM(
int fuse_interrupted(void),
{
return 0;
})
FSP_FUSE_SYM(
int fuse_invalidate(struct fuse *f, const char *path),
{
(void)f;
(void)path;
return -EINVAL;
})
FSP_FUSE_SYM(
int fuse_notify_poll(struct fuse_pollhandle *ph),
{
(void)ph;
return 0;
})
FSP_FUSE_SYM(
struct fuse_session *fuse_get_session(struct fuse *f),
{
return (struct fuse_session *)f;
})
#ifdef __cplusplus
}
#endif
#endif
/**
* @file fuse/fuse_common.h
* WinFsp FUSE compatible API.
*
* This file is derived from libfuse/include/fuse_common.h:
* FUSE: Filesystem in Userspace
* Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
*
* @copyright 2015-2020 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
#ifndef FUSE_COMMON_H_
#define FUSE_COMMON_H_
#include "winfsp_fuse.h"
#include "fuse_opt.h"
#ifdef __cplusplus
extern "C" {
#endif
#define FUSE_MAJOR_VERSION 2
#define FUSE_MINOR_VERSION 8
#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
#define FUSE_CAP_ASYNC_READ (1 << 0)
#define FUSE_CAP_POSIX_LOCKS (1 << 1)
#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
#define FUSE_CAP_BIG_WRITES (1 << 5)
#define FUSE_CAP_DONT_MASK (1 << 6)
#define FUSE_CAP_ALLOCATE (1 << 27) /* reserved (OSXFUSE) */
#define FUSE_CAP_EXCHANGE_DATA (1 << 28) /* reserved (OSXFUSE) */
#define FUSE_CAP_CASE_INSENSITIVE (1 << 29) /* file system is case insensitive */
#define FUSE_CAP_VOL_RENAME (1 << 30) /* reserved (OSXFUSE) */
#define FUSE_CAP_XTIMES (1 << 31) /* reserved (OSXFUSE) */
#define FSP_FUSE_CAP_READDIR_PLUS (1 << 21) /* file system supports enhanced readdir */
#define FSP_FUSE_CAP_READ_ONLY (1 << 22) /* file system is marked read-only */
#define FSP_FUSE_CAP_STAT_EX (1 << 23) /* file system supports fuse_stat_ex */
#define FSP_FUSE_CAP_CASE_INSENSITIVE FUSE_CAP_CASE_INSENSITIVE
#define FUSE_IOCTL_COMPAT (1 << 0)
#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
#define FUSE_IOCTL_RETRY (1 << 2)
#define FUSE_IOCTL_MAX_IOV 256
/* from FreeBSD */
#define FSP_FUSE_UF_HIDDEN 0x00008000
#define FSP_FUSE_UF_READONLY 0x00001000
#define FSP_FUSE_UF_SYSTEM 0x00000080
#define FSP_FUSE_UF_ARCHIVE 0x00000800
#if !defined(UF_HIDDEN)
#define UF_HIDDEN FSP_FUSE_UF_HIDDEN
#endif
#if !defined(UF_READONLY)
#define UF_READONLY FSP_FUSE_UF_READONLY
#endif
#if !defined(UF_SYSTEM)
#define UF_SYSTEM FSP_FUSE_UF_SYSTEM
#endif
#if !defined(UF_ARCHIVE)
#define UF_ARCHIVE FSP_FUSE_UF_ARCHIVE
#endif
struct fuse_file_info
{
int flags;
unsigned int fh_old;
int writepage;
unsigned int direct_io:1;
unsigned int keep_cache:1;
unsigned int flush:1;
unsigned int nonseekable:1;
unsigned int padding:28;
uint64_t fh;
uint64_t lock_owner;
};
struct fuse_conn_info
{
unsigned proto_major;
unsigned proto_minor;
unsigned async_read;
unsigned max_write;
unsigned max_readahead;
unsigned capable;
unsigned want;
unsigned reserved[25];
};
struct fuse_session;
struct fuse_chan;
struct fuse_pollhandle;
struct fuse_bufvec;
struct fuse_statfs;
struct fuse_setattr_x;
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_version)(struct fsp_fuse_env *env);
FSP_FUSE_API struct fuse_chan *FSP_FUSE_API_NAME(fsp_fuse_mount)(struct fsp_fuse_env *env,
const char *mountpoint, struct fuse_args *args);
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_unmount)(struct fsp_fuse_env *env,
const char *mountpoint, struct fuse_chan *ch);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_parse_cmdline)(struct fsp_fuse_env *env,
struct fuse_args *args,
char **mountpoint, int *multithreaded, int *foreground);
FSP_FUSE_API int32_t FSP_FUSE_API_NAME(fsp_fuse_ntstatus_from_errno)(struct fsp_fuse_env *env,
int err);
FSP_FUSE_SYM(
int fuse_version(void),
{
return FSP_FUSE_API_CALL(fsp_fuse_version)
(fsp_fuse_env());
})
FSP_FUSE_SYM(
struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args),
{
return FSP_FUSE_API_CALL(fsp_fuse_mount)
(fsp_fuse_env(), mountpoint, args);
})
FSP_FUSE_SYM(
void fuse_unmount(const char *mountpoint, struct fuse_chan *ch),
{
FSP_FUSE_API_CALL(fsp_fuse_unmount)
(fsp_fuse_env(), mountpoint, ch);
})
FSP_FUSE_SYM(
int fuse_parse_cmdline(struct fuse_args *args,
char **mountpoint, int *multithreaded, int *foreground),
{
return FSP_FUSE_API_CALL(fsp_fuse_parse_cmdline)
(fsp_fuse_env(), args, mountpoint, multithreaded, foreground);
})
FSP_FUSE_SYM(
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph),
{
(void)ph;
})
FSP_FUSE_SYM(
int fuse_daemonize(int foreground),
{
return fsp_fuse_daemonize(foreground);
})
FSP_FUSE_SYM(
int fuse_set_signal_handlers(struct fuse_session *se),
{
return fsp_fuse_set_signal_handlers(se);
})
FSP_FUSE_SYM(
void fuse_remove_signal_handlers(struct fuse_session *se),
{
(void)se;
fsp_fuse_set_signal_handlers(0);
})
#ifdef __cplusplus
}
#endif
#endif
/**
* @file fuse/fuse_opt.h
* WinFsp FUSE compatible API.
*
* This file is derived from libfuse/include/fuse_opt.h:
* FUSE: Filesystem in Userspace
* Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
*
* @copyright 2015-2020 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
#ifndef FUSE_OPT_H_
#define FUSE_OPT_H_
#include "winfsp_fuse.h"
#ifdef __cplusplus
extern "C" {
#endif
#define FUSE_OPT_KEY(templ, key) { templ, -1, key }
#define FUSE_OPT_END { NULL, 0, 0 }
#define FUSE_OPT_KEY_OPT -1
#define FUSE_OPT_KEY_NONOPT -2
#define FUSE_OPT_KEY_KEEP -3
#define FUSE_OPT_KEY_DISCARD -4
#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
struct fuse_opt
{
const char *templ;
unsigned int offset;
int value;
};
struct fuse_args
{
int argc;
char **argv;
int allocated;
};
typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
struct fuse_args *outargs);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_parse)(struct fsp_fuse_env *env,
struct fuse_args *args, void *data,
const struct fuse_opt opts[], fuse_opt_proc_t proc);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_add_arg)(struct fsp_fuse_env *env,
struct fuse_args *args, const char *arg);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_insert_arg)(struct fsp_fuse_env *env,
struct fuse_args *args, int pos, const char *arg);
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_opt_free_args)(struct fsp_fuse_env *env,
struct fuse_args *args);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_add_opt)(struct fsp_fuse_env *env,
char **opts, const char *opt);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_add_opt_escaped)(struct fsp_fuse_env *env,
char **opts, const char *opt);
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_match)(struct fsp_fuse_env *env,
const struct fuse_opt opts[], const char *opt);
FSP_FUSE_SYM(
int fuse_opt_parse(struct fuse_args *args, void *data,
const struct fuse_opt opts[], fuse_opt_proc_t proc),
{
return FSP_FUSE_API_CALL(fsp_fuse_opt_parse)
(fsp_fuse_env(), args, data, opts, proc);
})
FSP_FUSE_SYM(
int fuse_opt_add_arg(struct fuse_args *args, const char *arg),
{
return FSP_FUSE_API_CALL(fsp_fuse_opt_add_arg)
(fsp_fuse_env(), args, arg);
})
FSP_FUSE_SYM(
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg),
{
return FSP_FUSE_API_CALL(fsp_fuse_opt_insert_arg)
(fsp_fuse_env(), args, pos, arg);
})
FSP_FUSE_SYM(
void fuse_opt_free_args(struct fuse_args *args),
{
FSP_FUSE_API_CALL(fsp_fuse_opt_free_args)
(fsp_fuse_env(), args);
})
FSP_FUSE_SYM(
int fuse_opt_add_opt(char **opts, const char *opt),
{
return FSP_FUSE_API_CALL(fsp_fuse_opt_add_opt)
(fsp_fuse_env(), opts, opt);
})
FSP_FUSE_SYM(
int fuse_opt_add_opt_escaped(char **opts, const char *opt),
{
return FSP_FUSE_API_CALL(fsp_fuse_opt_add_opt_escaped)
(fsp_fuse_env(), opts, opt);
})
FSP_FUSE_SYM(
int fuse_opt_match(const struct fuse_opt opts[], const char *opt),
{
return FSP_FUSE_API_CALL(fsp_fuse_opt_match)
(fsp_fuse_env(), opts, opt);
})
#ifdef __cplusplus
}
#endif
#endif
/**
* @file fuse/winfsp_fuse.h
* WinFsp FUSE compatible API.
*
* @copyright 2015-2020 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
#ifndef FUSE_WINFSP_FUSE_H_INCLUDED
#define FUSE_WINFSP_FUSE_H_INCLUDED
#include <errno.h>
#include <stdint.h>
#if !defined(WINFSP_DLL_INTERNAL)
#include <stdlib.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(FSP_FUSE_API)
#if defined(WINFSP_DLL_INTERNAL)
#define FSP_FUSE_API __declspec(dllexport)
#else
#define FSP_FUSE_API __declspec(dllimport)
#endif
#endif
#if !defined(FSP_FUSE_API_NAME)
#define FSP_FUSE_API_NAME(n) (n)
#endif
#if !defined(FSP_FUSE_API_CALL)
#define FSP_FUSE_API_CALL(n) (n)
#endif
#if !defined(FSP_FUSE_SYM)
#if !defined(CYGFUSE)
#define FSP_FUSE_SYM(proto, ...) static inline proto { __VA_ARGS__ }
#else
#define FSP_FUSE_SYM(proto, ...) proto;
#endif
#endif
#define FSP_FUSE_DEVICE_TYPE (0x8000 | 'W' | 'F' * 0x100) /* DeviceIoControl -> ioctl */
#define FSP_FUSE_CTLCODE_FROM_IOCTL(cmd)\
(FSP_FUSE_DEVICE_TYPE << 16) | (((cmd) & 0x0fff) << 2)
#define FSP_FUSE_IOCTL(cmd, isiz, osiz) \
( \
(((osiz) != 0) << 31) | \
(((isiz) != 0) << 30) | \
(((isiz) | (osiz)) << 16) | \
(cmd) \
)
/*
* FUSE uses a number of types (notably: struct stat) that are OS specific.
* Furthermore there are sometimes multiple definitions of the same type even
* within the same OS. This is certainly true on Windows, where these types
* are not even native.
*
* For this reason we will define our own fuse_* types which represent the
* types as the WinFsp DLL expects to see them. We will define these types
* to be compatible with the equivalent Cygwin types as we want WinFsp-FUSE
* to be usable from Cygwin.
*/
#define FSP_FUSE_STAT_FIELD_DEFN \
fuse_dev_t st_dev; \
fuse_ino_t st_ino; \
fuse_mode_t st_mode; \
fuse_nlink_t st_nlink; \
fuse_uid_t st_uid; \
fuse_gid_t st_gid; \
fuse_dev_t st_rdev; \
fuse_off_t st_size; \
struct fuse_timespec st_atim; \
struct fuse_timespec st_mtim; \
struct fuse_timespec st_ctim; \
fuse_blksize_t st_blksize; \
fuse_blkcnt_t st_blocks; \
struct fuse_timespec st_birthtim;
#define FSP_FUSE_STAT_EX_FIELD_DEFN \
FSP_FUSE_STAT_FIELD_DEFN \
uint32_t st_flags; \
uint32_t st_reserved32[3]; \
uint64_t st_reserved64[2];
#if defined(_WIN64) || defined(_WIN32)
typedef uint32_t fuse_uid_t;
typedef uint32_t fuse_gid_t;
typedef int32_t fuse_pid_t;
typedef uint32_t fuse_dev_t;
typedef uint64_t fuse_ino_t;
typedef uint32_t fuse_mode_t;
typedef uint16_t fuse_nlink_t;
typedef int64_t fuse_off_t;
#if defined(_WIN64)
typedef uint64_t fuse_fsblkcnt_t;
typedef uint64_t fuse_fsfilcnt_t;
#else
typedef uint32_t fuse_fsblkcnt_t;
typedef uint32_t fuse_fsfilcnt_t;
#endif
typedef int32_t fuse_blksize_t;
typedef int64_t fuse_blkcnt_t;
#if defined(_WIN64)
struct fuse_utimbuf
{
int64_t actime;
int64_t modtime;
};
struct fuse_timespec
{
int64_t tv_sec;
int64_t tv_nsec;
};
#else
struct fuse_utimbuf
{
int32_t actime;
int32_t modtime;
};
struct fuse_timespec
{
int32_t tv_sec;
int32_t tv_nsec;
};
#endif
#if !defined(FSP_FUSE_USE_STAT_EX)
struct fuse_stat
{
FSP_FUSE_STAT_FIELD_DEFN
};
#else
struct fuse_stat
{
FSP_FUSE_STAT_EX_FIELD_DEFN
};
#endif
#if defined(_WIN64)
struct fuse_statvfs
{
uint64_t f_bsize;
uint64_t f_frsize;
fuse_fsblkcnt_t f_blocks;
fuse_fsblkcnt_t f_bfree;
fuse_fsblkcnt_t f_bavail;
fuse_fsfilcnt_t f_files;
fuse_fsfilcnt_t f_ffree;
fuse_fsfilcnt_t f_favail;
uint64_t f_fsid;
uint64_t f_flag;
uint64_t f_namemax;
};
#else
struct fuse_statvfs
{
uint32_t f_bsize;
uint32_t f_frsize;
fuse_fsblkcnt_t f_blocks;
fuse_fsblkcnt_t f_bfree;
fuse_fsblkcnt_t f_bavail;
fuse_fsfilcnt_t f_files;
fuse_fsfilcnt_t f_ffree;
fuse_fsfilcnt_t f_favail;
uint32_t f_fsid;
uint32_t f_flag;
uint32_t f_namemax;
};
#endif
struct fuse_flock
{
int16_t l_type;
int16_t l_whence;
fuse_off_t l_start;
fuse_off_t l_len;
fuse_pid_t l_pid;
};
#if defined(WINFSP_DLL_INTERNAL)
#define FSP_FUSE_ENV_INIT \
{ \
'W', \
MemAlloc, MemFree, \
fsp_fuse_daemonize, \
fsp_fuse_set_signal_handlers, \
0/*conv_to_win_path*/, \
0/*winpid_to_pid*/, \
{ 0 }, \
}
#else
#define FSP_FUSE_ENV_INIT \
{ \
'W', \
malloc, free, \
fsp_fuse_daemonize, \
fsp_fuse_set_signal_handlers, \
0/*conv_to_win_path*/, \
0/*winpid_to_pid*/, \
{ 0 }, \
}
#endif
#elif defined(__CYGWIN__)
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <utime.h>
#define fuse_uid_t uid_t
#define fuse_gid_t gid_t
#define fuse_pid_t pid_t
#define fuse_dev_t dev_t
#define fuse_ino_t ino_t
#define fuse_mode_t mode_t
#define fuse_nlink_t nlink_t
#define fuse_off_t off_t
#define fuse_fsblkcnt_t fsblkcnt_t
#define fuse_fsfilcnt_t fsfilcnt_t
#define fuse_blksize_t blksize_t
#define fuse_blkcnt_t blkcnt_t
#define fuse_utimbuf utimbuf
#define fuse_timespec timespec
#if !defined(FSP_FUSE_USE_STAT_EX)
#define fuse_stat stat
#else
struct fuse_stat
{
FSP_FUSE_STAT_EX_FIELD_DEFN
};
#endif
#define fuse_statvfs statvfs
#define fuse_flock flock
#define FSP_FUSE_ENV_INIT \
{ \
'C', \
malloc, free, \
fsp_fuse_daemonize, \
fsp_fuse_set_signal_handlers, \
fsp_fuse_conv_to_win_path, \
fsp_fuse_winpid_to_pid, \
{ 0 }, \
}
/*
* Note that long is 8 bytes long in Cygwin64 and 4 bytes long in Win64.
* For this reason we avoid using long anywhere in these headers.
*/
#else
#error unsupported environment
#endif
struct fuse_stat_ex
{
FSP_FUSE_STAT_EX_FIELD_DEFN
};
struct fsp_fuse_env
{
unsigned environment;
void *(*memalloc)(size_t);
void (*memfree)(void *);
int (*daemonize)(int);
int (*set_signal_handlers)(void *);
char *(*conv_to_win_path)(const char *);
fuse_pid_t (*winpid_to_pid)(uint32_t);
void (*reserved[2])();
};
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_signal_handler)(int sig);
#if defined(_WIN64) || defined(_WIN32)
static inline int fsp_fuse_daemonize(int foreground)
{
(void)foreground;
return 0;
}
static inline int fsp_fuse_set_signal_handlers(void *se)
{
(void)se;
return 0;
}
#elif defined(__CYGWIN__)
static inline int fsp_fuse_daemonize(int foreground)
{
int daemon(int nochdir, int noclose);
int chdir(const char *path);
if (!foreground)
{
if (-1 == daemon(0, 0))
return -1;
}
else
chdir("/");
return 0;
}
static inline void *fsp_fuse_signal_thread(void *psigmask)
{
int sig;
if (0 == sigwait((sigset_t *)psigmask, &sig))
FSP_FUSE_API_CALL(fsp_fuse_signal_handler)(sig);
return 0;
}
static inline int fsp_fuse_set_signal_handlers(void *se)
{
#define FSP_FUSE_SET_SIGNAL_HANDLER(sig, newha)\
if (-1 != sigaction((sig), 0, &oldsa) &&\
oldsa.sa_handler == (se ? SIG_DFL : (newha)))\
{\
newsa.sa_handler = se ? (newha) : SIG_DFL;\
sigaction((sig), &newsa, 0);\
}
#define FSP_FUSE_SIGADDSET(sig)\
if (-1 != sigaction((sig), 0, &oldsa) &&\
oldsa.sa_handler == SIG_DFL)\
sigaddset(&sigmask, (sig));
static sigset_t sigmask;
static pthread_t sigthr;
struct sigaction oldsa, newsa;
// memset instead of initializer to avoid GCC -Wmissing-field-initializers warning
memset(&newsa, 0, sizeof newsa);
if (0 != se)
{
if (0 == sigthr)
{
FSP_FUSE_SET_SIGNAL_HANDLER(SIGPIPE, SIG_IGN);
sigemptyset(&sigmask);
FSP_FUSE_SIGADDSET(SIGHUP);
FSP_FUSE_SIGADDSET(SIGINT);
FSP_FUSE_SIGADDSET(SIGTERM);
if (0 != pthread_sigmask(SIG_BLOCK, &sigmask, 0))
return -1;
if (0 != pthread_create(&sigthr, 0, fsp_fuse_signal_thread, &sigmask))
return -1;
}
}
else
{
if (0 != sigthr)
{
pthread_cancel(sigthr);
pthread_join(sigthr, 0);
sigthr = 0;
if (0 != pthread_sigmask(SIG_UNBLOCK, &sigmask, 0))
return -1;
sigemptyset(&sigmask);
FSP_FUSE_SET_SIGNAL_HANDLER(SIGPIPE, SIG_IGN);
}
}
return 0;
#undef FSP_FUSE_SIGADDSET
#undef FSP_FUSE_SET_SIGNAL_HANDLER
}
static inline char *fsp_fuse_conv_to_win_path(const char *path)
{
void *cygwin_create_path(unsigned, const void *);
return (char *)cygwin_create_path(
0/*CCP_POSIX_TO_WIN_A*/ | 0x100/*CCP_RELATIVE*/,
path);
}
static inline fuse_pid_t fsp_fuse_winpid_to_pid(uint32_t winpid)
{
pid_t cygwin_winpid_to_pid(int winpid);
pid_t pid = cygwin_winpid_to_pid(winpid);
return -1 != pid ? pid : (fuse_pid_t)winpid;
}
#endif
static inline struct fsp_fuse_env *fsp_fuse_env(void)
{
static struct fsp_fuse_env env = FSP_FUSE_ENV_INIT;
return &env;
}
#ifdef __cplusplus
}
#endif
#endif
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package chunk
import (
"os"
"syscall"
"time"
sys "golang.org/x/sys/windows"
)
func getAtime(fi os.FileInfo) time.Time {
stat, ok := fi.Sys().(*syscall.Win32FileAttributeData)
if ok {
return time.Unix(0, stat.LastAccessTime.Nanoseconds())
} else {
return time.Unix(0, 0)
}
}
func getNlink(fi os.FileInfo) int {
return 1
}
func getDiskUsage(path string) (uint64, uint64, uint64, uint64) {
var freeBytes, total, totalFree uint64
err := sys.GetDiskFreeSpaceEx(sys.StringToUTF16Ptr(path), &freeBytes, &total, &totalFree)
if err != nil {
logger.Errorf("GetDiskFreeSpaceEx %s: %s", path, err.Error())
return 1, 1, 1, 1
}
return total, freeBytes, 1, 1
}
func changeMode(dir string, st os.FileInfo, mode os.FileMode) {}
......@@ -161,7 +161,7 @@ func (fs *FileSystem) log(ctx LogContext, format string, args ...interface{}) {
if fs.logBuffer == nil {
return
}
now := Now()
now := utils.Now()
cmd := fmt.Sprintf(format, args...)
ts := now.Format("2006.01.02 15:04:05.000000")
used := ctx.Duration()
......@@ -228,6 +228,10 @@ func (fs *FileSystem) flushLog(f *os.File, logBuffer chan string, path string) {
}
}
func (fs *FileSystem) Meta() meta.Meta {
return fs.m
}
func (fs *FileSystem) StatFS(ctx meta.Context) (totalspace uint64, availspace uint64) {
defer trace.StartRegion(context.TODO(), "fs.StatFS").End()
l := vfs.NewLogContext(ctx)
......
......@@ -2102,241 +2102,6 @@ func (r *redisMeta) RemoveXattr(ctx Context, inode Ino, name string) syscall.Err
return errno(err)
}
func (r *redisMeta) Flock(ctx Context, inode Ino, owner uint64, ltype uint32, block bool) syscall.Errno {
lkey := r.ownerKey(owner)
if ltype == syscall.F_UNLCK {
_, err := r.rdb.HDel(ctx, r.flockKey(inode), lkey).Result()
return errno(err)
}
var err syscall.Errno
for {
err = r.txn(ctx, func(tx *redis.Tx) error {
owners, err := tx.HGetAll(ctx, r.flockKey(inode)).Result()
if err != nil {
return err
}
if ltype == syscall.F_RDLCK {
for _, v := range owners {
if v == "W" {
return syscall.EAGAIN
}
}
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.HSet(ctx, r.flockKey(inode), lkey, "R")
return nil
})
return err
}
delete(owners, lkey)
if len(owners) > 0 {
return syscall.EAGAIN
}
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.HSet(ctx, r.flockKey(inode), lkey, "W")
return nil
})
return err
}, r.flockKey(inode))
if !block || err != syscall.EAGAIN {
break
}
if ltype == syscall.F_WRLCK {
time.Sleep(time.Millisecond * 1)
} else {
time.Sleep(time.Millisecond * 10)
}
if ctx.Canceled() {
return syscall.EINTR
}
}
return err
}
type plock struct {
ltype uint32
pid uint32
start uint64
end uint64
}
func (r *redisMeta) loadLocks(d []byte) []plock {
var ls []plock
rb := utils.FromBuffer(d)
for rb.HasMore() {
ls = append(ls, plock{rb.Get32(), rb.Get32(), rb.Get64(), rb.Get64()})
}
return ls
}
func (r *redisMeta) dumpLocks(ls []plock) []byte {
wb := utils.NewBuffer(uint32(len(ls)) * 24)
for _, l := range ls {
wb.Put32(l.ltype)
wb.Put32(l.pid)
wb.Put64(l.start)
wb.Put64(l.end)
}
return wb.Bytes()
}
func (r *redisMeta) insertLocks(ls []plock, i int, nl plock) []plock {
nls := make([]plock, len(ls)+1)
copy(nls[:i], ls[:i])
nls[i] = nl
copy(nls[i+1:], ls[i:])
ls = nls
return ls
}
func (r *redisMeta) updateLocks(ls []plock, nl plock) []plock {
// ls is ordered by l.start without overlap
var i int
for i < len(ls) && nl.end > nl.start {
l := ls[i]
if l.end < nl.start {
} else if l.start < nl.start {
ls = r.insertLocks(ls, i+1, plock{nl.ltype, nl.pid, nl.start, l.end})
ls[i].end = nl.start
i++
nl.start = l.end
} else if l.end < nl.end {
ls[i].ltype = nl.ltype
ls[i].start = nl.start
nl.start = l.end
} else if l.start < nl.end {
ls = r.insertLocks(ls, i, nl)
ls[i+1].start = nl.end
nl.start = nl.end
} else {
ls = r.insertLocks(ls, i, nl)
nl.start = nl.end
}
i++
}
if nl.start < nl.end {
ls = append(ls, nl)
}
i = 0
for i < len(ls) {
if ls[i].ltype == syscall.F_UNLCK || ls[i].start == ls[i].end {
// remove empty one
copy(ls[i:], ls[i+1:])
ls = ls[:len(ls)-1]
} else {
if i+1 < len(ls) && ls[i].ltype == ls[i+1].ltype && ls[i].end == ls[i+1].start {
// combine continuous range
ls[i].end = ls[i+1].end
ls[i+1].start = ls[i+1].end
}
i++
}
}
return ls
}
func (r *redisMeta) Getlk(ctx Context, inode Ino, owner uint64, ltype *uint32, start, end *uint64, pid *uint32) syscall.Errno {
if *ltype == syscall.F_UNLCK {
*start = 0
*end = 0
*pid = 0
return 0
}
lkey := r.ownerKey(owner)
owners, err := r.rdb.HGetAll(ctx, r.plockKey(inode)).Result()
if err != nil {
return errno(err)
}
delete(owners, lkey) // exclude itself
for k, d := range owners {
ls := r.loadLocks([]byte(d))
for _, l := range ls {
// find conflicted locks
if (*ltype == syscall.F_WRLCK || l.ltype == syscall.F_WRLCK) && *end > l.start && *start < l.end {
*ltype = l.ltype
*start = l.start
*end = l.end
sid, _ := strconv.Atoi(strings.Split(k, "_")[0])
if int64(sid) == r.sid {
*pid = l.pid
} else {
*pid = 0
}
return 0
}
}
}
*ltype = syscall.F_UNLCK
*start = 0
*end = 0
*pid = 0
return 0
}
func (r *redisMeta) Setlk(ctx Context, inode Ino, owner uint64, block bool, ltype uint32, start, end uint64, pid uint32) syscall.Errno {
lkey := r.ownerKey(owner)
var err syscall.Errno
lock := plock{ltype, pid, start, end}
for {
err = r.txn(ctx, func(tx *redis.Tx) error {
if ltype == syscall.F_UNLCK {
d, err := tx.HGet(ctx, r.plockKey(inode), lkey).Result()
if err != nil {
return err
}
ls := r.loadLocks([]byte(d))
if len(ls) == 0 {
return nil
}
ls = r.updateLocks(ls, lock)
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
if len(ls) == 0 {
pipe.HDel(ctx, r.plockKey(inode), lkey)
} else {
pipe.HSet(ctx, r.plockKey(inode), lkey, r.dumpLocks(ls))
}
return nil
})
return err
}
owners, err := tx.HGetAll(ctx, r.plockKey(inode)).Result()
if err != nil {
return err
}
ls := r.loadLocks([]byte(owners[lkey]))
delete(owners, lkey)
for _, d := range owners {
ls := r.loadLocks([]byte(d))
for _, l := range ls {
// find conflicted locks
if (ltype == syscall.F_WRLCK || l.ltype == syscall.F_WRLCK) && end > l.start && start < l.end {
return syscall.EAGAIN
}
}
}
ls = r.updateLocks(ls, lock)
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.HSet(ctx, r.plockKey(inode), lkey, r.dumpLocks(ls))
return nil
})
return err
}, r.plockKey(inode))
if !block || err != syscall.EAGAIN {
break
}
if ltype == syscall.F_WRLCK {
time.Sleep(time.Millisecond * 1)
} else {
time.Sleep(time.Millisecond * 10)
}
if ctx.Canceled() {
return syscall.EINTR
}
}
return err
}
func (r *redisMeta) checkServerConfig() {
rawInfo, err := r.rdb.Info(Background).Result()
if err != nil {
......
// +build !windows
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package meta
import (
"strconv"
"strings"
"syscall"
"time"
"github.com/go-redis/redis/v8"
"github.com/juicedata/juicefs/pkg/utils"
)
func (r *redisMeta) Flock(ctx Context, inode Ino, owner uint64, ltype uint32, block bool) syscall.Errno {
lkey := r.ownerKey(owner)
if ltype == syscall.F_UNLCK {
_, err := r.rdb.HDel(ctx, r.flockKey(inode), lkey).Result()
return errno(err)
}
var err syscall.Errno
for {
err = r.txn(ctx, func(tx *redis.Tx) error {
owners, err := tx.HGetAll(ctx, r.flockKey(inode)).Result()
if err != nil {
return err
}
if ltype == syscall.F_RDLCK {
for _, v := range owners {
if v == "W" {
return syscall.EAGAIN
}
}
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.HSet(ctx, r.flockKey(inode), lkey, "R")
return nil
})
return err
}
delete(owners, lkey)
if len(owners) > 0 {
return syscall.EAGAIN
}
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.HSet(ctx, r.flockKey(inode), lkey, "W")
return nil
})
return err
}, r.flockKey(inode))
if !block || err != syscall.EAGAIN {
break
}
if ltype == syscall.F_WRLCK {
time.Sleep(time.Millisecond * 1)
} else {
time.Sleep(time.Millisecond * 10)
}
if ctx.Canceled() {
return syscall.EINTR
}
}
return err
}
type plock struct {
ltype uint32
pid uint32
start uint64
end uint64
}
func (r *redisMeta) loadLocks(d []byte) []plock {
var ls []plock
rb := utils.FromBuffer(d)
for rb.HasMore() {
ls = append(ls, plock{rb.Get32(), rb.Get32(), rb.Get64(), rb.Get64()})
}
return ls
}
func (r *redisMeta) dumpLocks(ls []plock) []byte {
wb := utils.NewBuffer(uint32(len(ls)) * 24)
for _, l := range ls {
wb.Put32(l.ltype)
wb.Put32(l.pid)
wb.Put64(l.start)
wb.Put64(l.end)
}
return wb.Bytes()
}
func (r *redisMeta) insertLocks(ls []plock, i int, nl plock) []plock {
nls := make([]plock, len(ls)+1)
copy(nls[:i], ls[:i])
nls[i] = nl
copy(nls[i+1:], ls[i:])
ls = nls
return ls
}
func (r *redisMeta) updateLocks(ls []plock, nl plock) []plock {
// ls is ordered by l.start without overlap
var i int
for i < len(ls) && nl.end > nl.start {
l := ls[i]
if l.end < nl.start {
} else if l.start < nl.start {
ls = r.insertLocks(ls, i+1, plock{nl.ltype, nl.pid, nl.start, l.end})
ls[i].end = nl.start
i++
nl.start = l.end
} else if l.end < nl.end {
ls[i].ltype = nl.ltype
ls[i].start = nl.start
nl.start = l.end
} else if l.start < nl.end {
ls = r.insertLocks(ls, i, nl)
ls[i+1].start = nl.end
nl.start = nl.end
} else {
ls = r.insertLocks(ls, i, nl)
nl.start = nl.end
}
i++
}
if nl.start < nl.end {
ls = append(ls, nl)
}
i = 0
for i < len(ls) {
if ls[i].ltype == syscall.F_UNLCK || ls[i].start == ls[i].end {
// remove empty one
copy(ls[i:], ls[i+1:])
ls = ls[:len(ls)-1]
} else {
if i+1 < len(ls) && ls[i].ltype == ls[i+1].ltype && ls[i].end == ls[i+1].start {
// combine continuous range
ls[i].end = ls[i+1].end
ls[i+1].start = ls[i+1].end
}
i++
}
}
return ls
}
func (r *redisMeta) Getlk(ctx Context, inode Ino, owner uint64, ltype *uint32, start, end *uint64, pid *uint32) syscall.Errno {
if *ltype == syscall.F_UNLCK {
*start = 0
*end = 0
*pid = 0
return 0
}
lkey := r.ownerKey(owner)
owners, err := r.rdb.HGetAll(ctx, r.plockKey(inode)).Result()
if err != nil {
return errno(err)
}
delete(owners, lkey) // exclude itself
for k, d := range owners {
ls := r.loadLocks([]byte(d))
for _, l := range ls {
// find conflicted locks
if (*ltype == syscall.F_WRLCK || l.ltype == syscall.F_WRLCK) && *end > l.start && *start < l.end {
*ltype = l.ltype
*start = l.start
*end = l.end
sid, _ := strconv.Atoi(strings.Split(k, "_")[0])
if int64(sid) == r.sid {
*pid = l.pid
} else {
*pid = 0
}
return 0
}
}
}
*ltype = syscall.F_UNLCK
*start = 0
*end = 0
*pid = 0
return 0
}
func (r *redisMeta) Setlk(ctx Context, inode Ino, owner uint64, block bool, ltype uint32, start, end uint64, pid uint32) syscall.Errno {
lkey := r.ownerKey(owner)
var err syscall.Errno
lock := plock{ltype, pid, start, end}
for {
err = r.txn(ctx, func(tx *redis.Tx) error {
if ltype == syscall.F_UNLCK {
d, err := tx.HGet(ctx, r.plockKey(inode), lkey).Result()
if err != nil {
return err
}
ls := r.loadLocks([]byte(d))
if len(ls) == 0 {
return nil
}
ls = r.updateLocks(ls, lock)
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
if len(ls) == 0 {
pipe.HDel(ctx, r.plockKey(inode), lkey)
} else {
pipe.HSet(ctx, r.plockKey(inode), lkey, r.dumpLocks(ls))
}
return nil
})
return err
}
owners, err := tx.HGetAll(ctx, r.plockKey(inode)).Result()
if err != nil {
return err
}
ls := r.loadLocks([]byte(owners[lkey]))
delete(owners, lkey)
for _, d := range owners {
ls := r.loadLocks([]byte(d))
for _, l := range ls {
// find conflicted locks
if (ltype == syscall.F_WRLCK || l.ltype == syscall.F_WRLCK) && end > l.start && start < l.end {
return syscall.EAGAIN
}
}
}
ls = r.updateLocks(ls, lock)
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.HSet(ctx, r.plockKey(inode), lkey, r.dumpLocks(ls))
return nil
})
return err
}, r.plockKey(inode))
if !block || err != syscall.EAGAIN {
break
}
if ltype == syscall.F_WRLCK {
time.Sleep(time.Millisecond * 1)
} else {
time.Sleep(time.Millisecond * 10)
}
if ctx.Canceled() {
return syscall.EINTR
}
}
return err
}
/*
* JuiceFS, Copyright (C) 2021 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package meta
import "syscall"
const ENOATTR = syscall.ENODATA
func (r *redisMeta) Flock(ctx Context, inode Ino, owner uint64, ltype uint32, block bool) syscall.Errno {
return syscall.ENOTSUP
}
func (r *redisMeta) Getlk(ctx Context, inode Ino, owner uint64, ltype *uint32, start, end *uint64, pid *uint32) syscall.Errno {
return syscall.ENOTSUP
}
func (r *redisMeta) Setlk(ctx Context, inode Ino, owner uint64, block bool, ltype uint32, start, end uint64, pid uint32) syscall.Errno {
return syscall.ENOTSUP
}
// +build !windows
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
......
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package object
import "os"
func getOwnerGroup(info os.FileInfo) (string, string) {
return "", ""
}
func lookupUser(name string) int {
return 0
}
func lookupGroup(name string) int {
return 0
}
......@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fs
package utils
import "time"
......
/*
* JuiceFS, Copyright (C) 2021 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package utils
import (
"syscall"
"time"
"unsafe"
)
type clock struct {
t time.Time
tick time.Duration
}
var last *clock
func Now() time.Time {
c := last
return c.t.Add(Clock() - c.tick)
}
// Clock returns the number of milliseconds that have elapsed since the program
// was started.
var Clock func() time.Duration
func init() {
QPCTimer := func() func() time.Duration {
lib, _ := syscall.LoadLibrary("kernel32.dll")
qpc, _ := syscall.GetProcAddress(lib, "QueryPerformanceCounter")
qpf, _ := syscall.GetProcAddress(lib, "QueryPerformanceFrequency")
if qpc == 0 || qpf == 0 {
return nil
}
var freq, start uint64
syscall.Syscall(qpf, 1, uintptr(unsafe.Pointer(&freq)), 0, 0)
syscall.Syscall(qpc, 1, uintptr(unsafe.Pointer(&start)), 0, 0)
if freq <= 0 {
return nil
}
freqns := float64(freq) / 1e9
return func() time.Duration {
var now uint64
syscall.Syscall(qpc, 1, uintptr(unsafe.Pointer(&now)), 0, 0)
return time.Duration(float64(now-start) / freqns)
}
}
if Clock = QPCTimer(); Clock == nil {
// Fallback implementation
start := time.Now()
Clock = func() time.Duration { return time.Since(start) }
}
last = &clock{time.Now(), Clock()}
go func() {
for {
last = &clock{time.Now(), Clock()}
time.Sleep(time.Hour)
}
}()
}
// +build !windows
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
......
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package utils
func InitLoggers(logToSyslog bool) {}
// +build !windows
/*
* JuiceFS, Copyright (C) 2021 Juicedata, Inc.
*
......
/*
* JuiceFS, Copyright (C) 2021 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package utils
import (
"os"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type PROCESS_MEMORY_COUNTERS struct {
CB uint32
PageFaultCount uint32
PeakWorkingSetSize uint64
WorkingSetSize uint64
QuotaPeakPagedPoolUsage uint64
QuotaPagedPoolUsage uint64
QuotaPeakNonPagedPoolUsage uint64
QuotaNonPagedPoolUsage uint64
PagefileUsage uint64
PeakPagefileUsage uint64
}
var (
modpsapi = windows.NewLazySystemDLL("psapi.dll")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
)
func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) {
var mem PROCESS_MEMORY_COUNTERS
c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid))
if err != nil {
return mem, err
}
defer windows.CloseHandle(c)
if err := getProcessMemoryInfo(c, &mem); err != nil {
return mem, err
}
return mem, err
}
func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) {
r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem)))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func MemoryUsage() (virt, rss uint64) {
c, err := getMemoryInfo(int32(os.Getpid()))
if err == nil {
return c.PeakWorkingSetSize, c.WorkingSetSize
}
return 0, 0
}
// +build !windows
/*
* JuiceFS, Copyright (C) 2021 Juicedata, Inc.
*
......
/*
* JuiceFS, Copyright (C) 2021 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package utils
import "golang.org/x/sys/windows"
type Rusage struct {
kernel windows.Filetime
user windows.Filetime
}
func (ru *Rusage) GetUtime() float64 {
return float64((int64(ru.user.HighDateTime)<<32)+int64(ru.user.LowDateTime)) / 10 / 1e6
}
func (ru *Rusage) GetStime() float64 {
return float64((int64(ru.kernel.HighDateTime)<<32)+int64(ru.kernel.LowDateTime)) / 10 / 1e6
}
func GetRusage() *Rusage {
h := windows.CurrentProcess()
var creation, exit, kernel, user windows.Filetime
err := windows.GetProcessTimes(h, &creation, &exit, &kernel, &user)
if err == nil {
return &Rusage{kernel, user}
}
return nil
}
// +build !windows
/*
* JuiceFS, Copyright (C) 2021 Juicedata, Inc.
*
......
/*
* JuiceFS, Copyright (C) 2021 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package utils
import "errors"
func GetFileInode(path string) (uint64, error) {
return 0, errors.New("no supported in Windows")
}
......@@ -20,6 +20,7 @@ import (
"sync"
"time"
"github.com/juicedata/juicefs/pkg/utils"
"github.com/prometheus/client_golang/prometheus"
)
......@@ -60,7 +61,7 @@ func logit(ctx Context, format string, args ...interface{}) {
}
cmd := fmt.Sprintf(format, args...)
t := time.Now()
t := utils.Now()
ts := t.Format("2006.01.02 15:04:05.000000")
cmd += fmt.Sprintf(" <%.6f>", used.Seconds())
if ctx.Pid() != 0 && used >= time.Second*10 {
......
......@@ -99,7 +99,7 @@ func handleInternalMsg(ctx Context, msg []byte) []byte {
size := int(r.Get32())
if r.Left() != int(size) {
logger.Warnf("broken message: %d %d != %d", cmd, size, r.Left())
return []byte{uint8(syscall.EIO)}
return []byte{uint8(syscall.EIO & 0xff)}
}
switch cmd {
case meta.Rmr:
......@@ -109,6 +109,6 @@ func handleInternalMsg(ctx Context, msg []byte) []byte {
return []byte{uint8(r)}
default:
logger.Warnf("unknown message type: %d", cmd)
return []byte{uint8(syscall.EINVAL)}
return []byte{uint8(syscall.EINVAL & 0xff)}
}
}
......@@ -475,7 +475,7 @@ func Release(ctx Context, ino Ino, fh uint64) (err syscall.Errno) {
f.writer.Close(ctx)
}
if locks&1 != 0 {
_ = m.Flock(ctx, ino, owner, syscall.F_UNLCK, false)
_ = m.Flock(ctx, ino, owner, F_UNLCK, false)
}
}
_ = m.Close(ctx, ino)
......@@ -744,7 +744,7 @@ func Flush(ctx Context, ino Ino, fh uint64, lockOwner uint64) (err syscall.Errno
locks := h.locks
h.Unlock()
if locks&2 != 0 {
_ = m.Setlk(ctx, ino, lockOwner, false, syscall.F_UNLCK, 0, 0x7FFFFFFFFFFFFFFF, 0)
_ = m.Setlk(ctx, ino, lockOwner, false, F_UNLCK, 0, 0x7FFFFFFFFFFFFFFF, 0)
}
return
}
......
......@@ -29,6 +29,7 @@ import (
)
const O_ACCMODE = syscall.O_ACCMODE
const F_UNLCK = syscall.F_UNLCK
const (
MODE_MASK_R = 4
......
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package vfs
const O_ACCMODE = 0xff
const F_UNLCK = 0x01
/*
* trace.go
*
* Copyright 2017-2018 Bill Zissimopoulos
*/
/*
* This file is part of Cgofuse.
*
* It is licensed under the MIT license. The full license text can be found
* in the License.txt file at the root of this project.
*/
package winfsp
import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
)
var (
TracePattern = os.Getenv("CGOFUSE_TRACE")
)
func traceJoin(deref bool, vals []interface{}) string {
rslt := ""
for _, v := range vals {
if deref {
switch i := v.(type) {
case *bool:
rslt += fmt.Sprintf(", %#v", *i)
case *int:
rslt += fmt.Sprintf(", %#v", *i)
case *int8:
rslt += fmt.Sprintf(", %#v", *i)
case *int16:
rslt += fmt.Sprintf(", %#v", *i)
case *int32:
rslt += fmt.Sprintf(", %#v", *i)
case *int64:
rslt += fmt.Sprintf(", %#v", *i)
case *uint:
rslt += fmt.Sprintf(", %#v", *i)
case *uint8:
rslt += fmt.Sprintf(", %#v", *i)
case *uint16:
rslt += fmt.Sprintf(", %#v", *i)
case *uint32:
rslt += fmt.Sprintf(", %#v", *i)
case *uint64:
rslt += fmt.Sprintf(", %#v", *i)
case *uintptr:
rslt += fmt.Sprintf(", %#v", *i)
case *float32:
rslt += fmt.Sprintf(", %#v", *i)
case *float64:
rslt += fmt.Sprintf(", %#v", *i)
case *complex64:
rslt += fmt.Sprintf(", %#v", *i)
case *complex128:
rslt += fmt.Sprintf(", %#v", *i)
case *string:
rslt += fmt.Sprintf(", %#v", *i)
default:
rslt += fmt.Sprintf(", %#v", v)
}
} else {
rslt += fmt.Sprintf(", %#v", v)
}
}
if len(rslt) > 0 {
rslt = rslt[2:]
}
return rslt
}
func Trace(skip int, prfx string, vals ...interface{}) func(vals ...interface{}) {
if "" == TracePattern {
return func(vals ...interface{}) {
}
}
pc, _, _, ok := runtime.Caller(skip + 1)
name := "<UNKNOWN>"
if ok {
fn := runtime.FuncForPC(pc)
name = fn.Name()
if m, _ := filepath.Match(TracePattern, name); !m {
return func(vals ...interface{}) {
}
}
}
if "" != prfx {
prfx = prfx + ": "
}
args := traceJoin(false, vals)
return func(vals ...interface{}) {
form := "%v%v(%v) = %v"
rslt := ""
rcvr := recover()
if nil != rcvr {
rslt = fmt.Sprintf("!PANIC:%v", rcvr)
} else {
if len(vals) != 1 {
form = "%v%v(%v) = (%v)"
}
rslt = traceJoin(true, vals)
}
log.Printf(form, prfx, name, args, rslt)
if nil != rcvr {
panic(rcvr)
}
}
}
// +build windows
/*
* JuiceFS, Copyright (C) 2020 Juicedata, Inc.
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package winfsp
import (
"fmt"
"os"
"path"
"runtime"
"strings"
"sync"
"syscall"
"time"
"github.com/billziss-gh/cgofuse/fuse"
"github.com/juicedata/juicefs/pkg/fs"
"github.com/juicedata/juicefs/pkg/meta"
"github.com/juicedata/juicefs/pkg/utils"
"github.com/juicedata/juicefs/pkg/vfs"
)
var logger = utils.GetLogger("juicefs")
type Ino = meta.Ino
func trace(vals ...interface{}) func(vals ...interface{}) {
uid, gid, pid := fuse.Getcontext()
return Trace(1, fmt.Sprintf("[uid=%v,gid=%v,pid=%d]", uid, gid, pid), vals...)
}
type juice struct {
fuse.FileSystemBase
sync.Mutex
conf *vfs.Config
fs *fs.FileSystem
host *fuse.FileSystemHost
handlers map[uint64]meta.Ino
badfd map[uint64]uint64
asRoot bool
delayClose int
}
// Init is called when the file system is created.
func (j *juice) Init() {
j.handlers = make(map[uint64]meta.Ino)
j.badfd = make(map[uint64]uint64)
}
func (j *juice) newContext() vfs.LogContext {
if j.asRoot {
return vfs.NewLogContext(meta.Background)
}
uid, gid, pid := fuse.Getcontext()
if uid == 0xffffffff {
uid = 0
}
if gid == 0xffffffff {
gid = 0
}
if pid == -1 {
pid = 0
}
ctx := meta.NewContext(uint32(pid), uid, []uint32{gid})
return vfs.NewLogContext(ctx)
}
// Statfs gets file system statistics.
func (j *juice) Statfs(path string, stat *fuse.Statfs_t) int {
ctx := j.newContext()
// defer trace(path)(stat)
var totalspace, availspace, iused, iavail uint64
j.fs.Meta().StatFS(ctx, &totalspace, &availspace, &iused, &iavail)
var bsize uint64 = 0x10000
blocks := totalspace / bsize
bavail := availspace / bsize
stat.Namemax = 255
stat.Frsize = bsize
stat.Bsize = bsize
stat.Blocks = blocks
stat.Bfree = bavail
stat.Bavail = bavail
stat.Files = iused + iavail
stat.Ffree = iavail
stat.Favail = iavail
return 0
}
func errorconv(err syscall.Errno) int {
return -int(err)
}
// Mknod creates a file node.
func (j *juice) Mknod(p string, mode uint32, dev uint64) (e int) {
ctx := j.newContext()
defer trace(p, mode, dev)(&e)
parent, err := j.fs.Open(ctx, path.Dir(p), 0)
if err != 0 {
e = errorconv(err)
return
}
_, errno := vfs.Mknod(ctx, parent.Inode(), path.Base(p), uint16(mode), 0, uint32(dev))
e = -int(errno)
return
}
// Mkdir creates a directory.
func (j *juice) Mkdir(path string, mode uint32) (e int) {
if path == "/.UMOUNTIT" {
logger.Infof("Umount %s ...", j.conf.Mountpoint)
go j.host.Unmount()
return -fuse.ENOENT
}
ctx := j.newContext()
defer trace(path, mode)(&e)
e = errorconv(j.fs.Mkdir(ctx, path, uint16(mode)))
return
}
// Unlink removes a file.
func (j *juice) Unlink(path string) (e int) {
ctx := j.newContext()
defer trace(path)(&e)
e = errorconv(j.fs.Delete(ctx, path))
return
}
// Rmdir removes a directory.
func (j *juice) Rmdir(path string) (e int) {
ctx := j.newContext()
defer trace(path)(&e)
e = errorconv(j.fs.Delete(ctx, path))
return
}
func (j *juice) Symlink(target string, newpath string) (e int) {
ctx := j.newContext()
defer trace(target, newpath)(&e)
parent, err := j.fs.Open(ctx, path.Dir(newpath), 0)
if err != 0 {
e = errorconv(err)
return
}
_, errno := vfs.Symlink(ctx, target, parent.Inode(), path.Base(newpath))
e = -int(errno)
return
}
func (j *juice) Readlink(path string) (e int, target string) {
ctx := j.newContext()
defer trace(path)(&e, &target)
fi, err := j.fs.Stat(ctx, path)
if err != 0 {
e = errorconv(err)
return
}
t, errno := vfs.Readlink(ctx, fi.Inode())
e = -int(errno)
target = string(t)
return
}
// Rename renames a file.
func (j *juice) Rename(oldpath string, newpath string) (e int) {
ctx := j.newContext()
defer trace(oldpath, newpath)(&e)
e = errorconv(j.fs.Rename(ctx, oldpath, newpath))
return
}
// Chmod changes the permission bits of a file.
func (j *juice) Chmod(path string, mode uint32) (e int) {
ctx := j.newContext()
defer trace(path, mode)(&e)
f, err := j.fs.Open(ctx, path, 0)
if err != 0 {
e = errorconv(err)
return
}
e = errorconv(f.Chmod(ctx, uint16(mode)))
return
}
// Chown changes the owner and group of a file.
func (j *juice) Chown(path string, uid uint32, gid uint32) (e int) {
ctx := j.newContext()
defer trace(path, uid, gid)(&e)
f, err := j.fs.Open(ctx, path, 0)
if err != 0 {
e = errorconv(err)
return
}
if runtime.GOOS == "windows" {
// FIXME: don't change ownership in windows
return 0
}
info, _ := f.Stat()
if uid == 0xffffffff {
uid = uint32(info.(*fs.FileStat).Uid())
}
if gid == 0xffffffff {
gid = uint32(info.(*fs.FileStat).Gid())
}
e = errorconv(f.Chown(ctx, uid, gid))
return
}
// Utimens changes the access and modification times of a file.
func (j *juice) Utimens(path string, tmsp []fuse.Timespec) (e int) {
ctx := j.newContext()
defer trace(path, tmsp)(&e)
f, err := j.fs.Open(ctx, path, 0)
if err != 0 {
e = errorconv(err)
} else {
e = errorconv(f.Utime(ctx, tmsp[0].Sec*1000+tmsp[0].Nsec/1e6, tmsp[1].Sec*1000+tmsp[1].Nsec/1e6))
}
return
}
// Create creates and opens a file.
// The flags are a combination of the fuse.O_* constants.
func (j *juice) Create(p string, flags int, mode uint32) (e int, fh uint64) {
ctx := j.newContext()
defer trace(p, flags, mode)(&e, &fh)
parent, err := j.fs.Open(ctx, path.Dir(p), 0)
if err != 0 {
e = errorconv(err)
return
}
entry, fh, errno := vfs.Create(ctx, parent.Inode(), path.Base(p), uint16(mode), 0, uint32(flags))
if errno == 0 {
j.Lock()
j.handlers[fh] = entry.Inode
j.Unlock()
}
e = -int(errno)
return
}
// Open opens a file.
// The flags are a combination of the fuse.O_* constants.
func (j *juice) Open(path string, flags int) (e int, fh uint64) {
var fi fuse.FileInfo_t
fi.Flags = flags
e = j.OpenEx(path, &fi)
fh = fi.Fh
return
}
func cache_mode(mattr uint8) (bool, bool) {
var direct_io, keep_cache bool
return direct_io, keep_cache
}
// Open opens a file.
// The flags are a combination of the fuse.O_* constants.
func (j *juice) OpenEx(path string, fi *fuse.FileInfo_t) (e int) {
ctx := j.newContext()
defer trace(path, fi.Flags)(&e)
f, err := j.fs.Open(ctx, path, 0)
if err != 0 {
e = -fuse.ENOENT
return
}
entry, fh, errno := vfs.Open(ctx, f.Inode(), uint32(fi.Flags))
if errno == 0 {
fi.Fh = fh
fi.DirectIo, fi.KeepCache = cache_mode(entry.Attr.Flags)
if vfs.IsSpecialNode(f.Inode()) {
fi.DirectIo = true
}
fi.NonSeekable = false
j.Lock()
j.handlers[fh] = f.Inode()
j.Unlock()
}
e = -int(errno)
return
}
func attrToStat(inode Ino, attr *meta.Attr, stat *fuse.Stat_t) {
stat.Ino = uint64(inode)
stat.Mode = attr.SMode()
stat.Uid = attr.Uid
if stat.Uid == 0 {
stat.Uid = 18 // System
}
stat.Gid = attr.Gid
if stat.Gid == 0 {
stat.Gid = 18 // System
}
stat.Birthtim.Sec = attr.Atime
stat.Birthtim.Nsec = int64(attr.Atimensec)
stat.Atim.Sec = attr.Atime
stat.Atim.Nsec = int64(attr.Atimensec)
stat.Mtim.Sec = attr.Mtime
stat.Mtim.Nsec = int64(attr.Mtimensec)
stat.Ctim.Sec = attr.Ctime
stat.Ctim.Nsec = int64(attr.Ctimensec)
stat.Nlink = attr.Nlink
var rdev uint32
var size, blocks uint64
switch attr.Typ {
case meta.TypeDirectory:
fallthrough
case meta.TypeSymlink:
fallthrough
case meta.TypeFile:
size = attr.Length
blocks = (size + 0xffff) / 0x10000
stat.Blksize = 0x10000
case meta.TypeBlockDev:
fallthrough
case meta.TypeCharDev:
rdev = attr.Rdev
}
stat.Size = int64(size)
stat.Blocks = int64(blocks)
stat.Rdev = uint64(rdev)
}
func (j *juice) h2i(fh *uint64) meta.Ino {
defer j.Unlock()
j.Lock()
ino := j.handlers[*fh]
if ino == 0 {
newfh := j.badfd[*fh]
if newfh != 0 {
ino = j.handlers[newfh]
if ino > 0 {
*fh = newfh
}
}
}
return ino
}
func (j *juice) reopen(p string, fh *uint64) meta.Ino {
e, newfh := j.Open(p, os.O_RDWR)
if e != 0 {
return 0
}
j.Lock()
defer j.Unlock()
j.badfd[*fh] = newfh
*fh = newfh
return j.handlers[newfh]
}
// Getattr gets file attributes.
func (j *juice) Getattr(p string, stat *fuse.Stat_t, fh uint64) (e int) {
ctx := j.newContext()
defer trace(p, fh)(stat, &e)
ino := j.h2i(&fh)
if ino == 0 {
fi, err := j.fs.Stat(ctx, p)
if err != 0 {
e = -fuse.ENOENT
return
}
ino = fi.Inode()
}
entry, errrno := vfs.GetAttr(ctx, ino, 0)
if errrno != 0 {
e = -int(errrno)
return
}
attrToStat(entry.Inode, entry.Attr, stat)
return
}
// Truncate changes the size of a file.
func (j *juice) Truncate(path string, size int64, fh uint64) (e int) {
ctx := j.newContext()
defer trace(path, size, fh)(&e)
ino := j.h2i(&fh)
if ino == 0 {
e = -fuse.EBADF
return
}
e = -int(vfs.Truncate(ctx, ino, size, 1, nil))
return
}
// Read reads data from a file.
func (j *juice) Read(path string, buf []byte, off int64, fh uint64) (e int) {
ctx := j.newContext()
defer trace(path, len(buf), off, fh)(&e)
ino := j.h2i(&fh)
if ino == 0 {
logger.Warnf("read from released fd %d for %s, re-open it", fh, path)
ino = j.reopen(path, &fh)
}
if ino == 0 {
e = -fuse.EBADF
return
}
n, err := vfs.Read(ctx, ino, buf, uint64(off), fh)
if err != 0 {
e = -int(err)
return
}
return n
}
// Write writes data to a file.
func (j *juice) Write(path string, buff []byte, off int64, fh uint64) (e int) {
ctx := j.newContext()
defer trace(path, len(buff), off, fh)(&e)
ino := j.h2i(&fh)
if ino == 0 {
logger.Warnf("write to released fd %d for %s, re-open it", fh, path)
ino = j.reopen(path, &fh)
}
if ino == 0 {
e = -fuse.EBADF
return
}
errno := vfs.Write(ctx, ino, buff, uint64(off), fh)
if errno != 0 {
e = -int(errno)
} else {
e = len(buff)
}
return
}
// Flush flushes cached file data.
func (j *juice) Flush(path string, fh uint64) (e int) {
ctx := j.newContext()
defer trace(path, fh)(&e)
ino := j.h2i(&fh)
if ino == 0 {
e = -fuse.EBADF
return
}
e = -int(vfs.Flush(ctx, ino, fh, 0))
return
}
// Release closes an open file.
func (j *juice) Release(path string, fh uint64) int {
defer trace(path, fh)()
orig := fh
ino := j.h2i(&fh)
if ino == 0 {
logger.Warnf("release invalid fd %d for %s", fh, path)
return -fuse.EBADF
}
go func() {
time.Sleep(time.Second * time.Duration(j.delayClose))
j.Lock()
delete(j.handlers, fh)
if orig != fh {
delete(j.badfd, orig)
}
j.Unlock()
vfs.Release(j.newContext(), ino, fh)
}()
return 0
}
// Fsync synchronizes file contents.
func (j *juice) Fsync(path string, datasync bool, fh uint64) (e int) {
ctx := j.newContext()
defer trace(path, datasync, fh)(&e)
ino := j.h2i(&fh)
if ino == 0 {
e = -fuse.EBADF
} else {
e = -int(vfs.Fsync(ctx, ino, 1, fh))
}
return
}
// Opendir opens a directory.
func (j *juice) Opendir(path string) (e int, fh uint64) {
ctx := j.newContext()
defer trace(path)(&e, &fh)
f, err := j.fs.Open(ctx, path, 0)
if err != 0 {
e = -fuse.ENOENT
return
}
fh, errno := vfs.Opendir(ctx, f.Inode())
if errno == 0 {
j.Lock()
j.handlers[fh] = f.Inode()
j.Unlock()
}
e = -int(errno)
return
}
// Readdir reads a directory.
func (j *juice) Readdir(path string,
fill func(name string, stat *fuse.Stat_t, ofst int64) bool,
ofst int64, fh uint64) (e int) {
defer trace(path, ofst, fh)(&e)
ino := j.h2i(&fh)
if ino == 0 {
e = -fuse.EBADF
return
}
ctx := j.newContext()
entries, err := vfs.Readdir(ctx, ino, 100000, int(ofst), fh, true)
if err != 0 {
e = -int(err)
return
}
var st fuse.Stat_t
var ok bool
// remove the virtual accesslog
for _, e := range entries[:len(entries)-1] {
if e.Attr.Full {
vfs.UpdateLength(e.Inode, e.Attr)
attrToStat(e.Inode, e.Attr, &st)
ok = fill(string(e.Name), &st, 0)
} else {
ok = fill(string(e.Name), nil, 0)
}
if !ok {
break
}
}
return
}
// Releasedir closes an open directory.
func (j *juice) Releasedir(path string, fh uint64) (e int) {
defer trace(path, fh)(&e)
ino := j.h2i(&fh)
if ino == 0 {
e = -fuse.EBADF
return
}
j.Lock()
delete(j.handlers, fh)
j.Unlock()
e = -int(vfs.Releasedir(j.newContext(), ino, fh))
return
}
func Serve(conf *vfs.Config, fs_ *fs.FileSystem, fuseOpt string, fileCacheTo float64, asRoot bool, delayClose int) error {
var jfs juice
jfs.conf = conf
jfs.fs = fs_
jfs.asRoot = asRoot
jfs.delayClose = delayClose
host := fuse.NewFileSystemHost(&jfs)
jfs.host = host
var options = "volname=" + conf.Format.Name
options += ",ExactFileSystemName=JuiceFS,create_umask=022,ThreadCount=16"
options += ",DirInfoTimeout=1000,VolumeInfoTimeout=1000,KeepFileCache"
options += fmt.Sprintf(",FileInfoTimeout=%d", int(fileCacheTo*1000))
options += ",VolumePrefix=/juicefs/" + conf.Format.Name
if asRoot {
options += ",uid=-1,gid=-1"
}
if fuseOpt != "" {
options += "," + fuseOpt
}
host.SetCapCaseInsensitive(strings.Contains(conf.Mountpoint, ":"))
host.SetCapReaddirPlus(true)
logger.Debugf("mount point: %s, options: %s", conf.Mountpoint, options)
_ = host.Mount(conf.Mountpoint, []string{"-o", options})
return nil
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册