Repository: juicedata/juicefs
Pull request #3784 (2023-06-08 11:56:34)

sync: fix umask


Patch:
diff --git a/cmd/bench.go b/cmd/bench.go
index 5e2aa080..a9166ede 100644
--- a/cmd/bench.go
+++ b/cmd/bench.go
@@ -337,7 +337,7 @@ func bench(ctx *cli.Context) error {
 
 	/* --- Prepare --- */
 	if _, err := os.Stat(bm.tmpdir); os.IsNotExist(err) {
-		if err = os.MkdirAll(bm.tmpdir, 0755); err != nil {
+		if err = os.MkdirAll(bm.tmpdir, 0777); err != nil {
 			logger.Fatalf("Failed to create %s: %s", bm.tmpdir, err)
 		}
 	}
diff --git a/cmd/debug.go b/cmd/debug.go
index d3741471..83852a59 100644
--- a/cmd/debug.go
+++ b/cmd/debug.go
@@ -587,5 +587,7 @@ func debug(ctx *cli.Context) error {
 	}
 
 	wg.Wait()
+	abs, _ := filepath.Abs(currDir)
+	logger.Infof("All files are collected to %s", abs)
 	return geneZipFile(currDir, filepath.Join(outDir, fmt.Sprintf("%s-%s.zip", prefix, timestamp)))
 }
diff --git a/cmd/gateway.go b/cmd/gateway.go
index 001e738a..859fc8b9 100644
--- a/cmd/gateway.go
+++ b/cmd/gateway.go
@@ -170,8 +170,7 @@ func (g *GateWay) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, er
 		&jfsgateway.Config{
 			MultiBucket: c.Bool("multi-buckets"),
 			KeepEtag:    c.Bool("keep-etag"),
-			Mode:        uint16(0666 &^ umask),
-			DirMode:     uint16(0777 &^ umask),
+			Umask:       uint16(umask),
 		},
 	)
 }
diff --git a/cmd/object.go b/cmd/object.go
index 1ec35dc9..f19b6d85 100644
--- a/cmd/object.go
+++ b/cmd/object.go
@@ -51,8 +51,9 @@ func toError(eno syscall.Errno) error {
 
 type juiceFS struct {
 	object.DefaultObjectStorage
-	name string
-	jfs  *fs.FileSystem
+	name  string
+	umask uint16
+	jfs   *fs.FileSystem
 }
 
 func (j *juiceFS) String() string {
@@ -120,14 +121,14 @@ var bufPool = sync.Pool{
 func (j *juiceFS) Put(key string, in io.Reader) (err error) {
 	p := j.path(key)
 	if strings.HasSuffix(p, "/") {
-		eno := j.jfs.MkdirAll(ctx, p, 0755)
+		eno := j.jfs.MkdirAll(ctx, p, 0777, j.umask)
 		return toError(eno)
 	}
 	tmp := filepath.Join(filepath.Dir(p), "."+filepath.Base(p)+".tmp"+strconv.Itoa(rand.Int()))
-	f, eno := j.jfs.Create(ctx, tmp, 0755)
+	f, eno := j.jfs.Create(ctx, tmp, 0666, j.umask)
 	if eno == syscall.ENOENT {
-		_ = j.jfs.MkdirAll(ctx, filepath.Dir(tmp), 0755)
-		f, eno = j.jfs.Create(ctx, tmp, 0755)
+		_ = j.jfs.MkdirAll(ctx, filepath.Dir(tmp), 0777, j.umask)
+		f, eno = j.jfs.Create(ctx, tmp, 0666, j.umask)
 	}
 	if eno != 0 {
 		return toError(eno)
@@ -313,12 +314,12 @@ func (j *juiceFS) Chown(key string, owner, group string) error {
 	return toError(f.Chown(ctx, uint32(uid), uint32(gid)))
 }
 
-func (d *juiceFS) Symlink(oldName, newName string) error {
-	p := d.path(newName)
-	err := d.jfs.Symlink(ctx, oldName, p)
+func (j *juiceFS) Symlink(oldName, newName string) error {
+	p := j.path(newName)
+	err := j.jfs.Symlink(ctx, oldName, p)
 	if err == syscall.ENOENT {
-		_ = d.jfs.MkdirAll(ctx, filepath.Dir(p), 0755)
-		err = d.jfs.Symlink(ctx, oldName, p)
+		_ = j.jfs.MkdirAll(ctx, filepath.Dir(p), 0777, j.umask)
+		err = j.jfs.Symlink(ctx, oldName, p)
 	}
 	return toError(err)
 }
@@ -388,7 +389,7 @@ func newJFS(endpoint, accessKey, secretKey, token string) (object.ObjectStorage,
 	if err != nil {
 		return nil, fmt.Errorf("Initialize: %s", err)
 	}
-	return &juiceFS{object.DefaultObjectStorage{}, format.Name, jfs}, nil
+	return &juiceFS{object.DefaultObjectStorage{}, format.Name, uint16(utils.GetUmask()), jfs}, nil
 }
 
 func init() {
diff --git a/cmd/umount.go b/cmd/umount.go
index d429a0be..4e751e76 100644
--- a/cmd/umount.go
+++ b/cmd/umount.go
@@ -82,7 +82,7 @@ func doUmount(mp string, force bool) error {
 		}
 	case "windows":
 		if !force {
-			_ = os.Mkdir(filepath.Join(mp, ".UMOUNTIT"), 0755)
+			_ = os.Mkdir(filepath.Join(mp, ".UMOUNTIT"), 0777)
 			return nil
 		} else {
 			cmd = exec.Command("taskkill", "/IM", "juicefs.exe", "/F")
diff --git a/pkg/chunk/cached_store.go b/pkg/chunk/cached_store.go
index 9becdcb0..8c1e165b 100644
--- a/pkg/chunk/cached_store.go
+++ b/pkg/chunk/cached_store.go
@@ -161,10 +161,7 @@ func (s *rSlice) ReadAt(ctx context.Context, page *Page, off int) (n int, err er
 			_ = in.Close()
 		}
 		used := time.Since(st)
-		logger.Debugf("GET %s RANGE(%d,%d) (%s, %.3fs)", key, boff, len(p), err, used.Seconds())
-		if used > SlowRequest {
-			logger.Infof("slow request: GET %s (%v, %.3fs)", key, err, used.Seconds())
-		}
+		logRequest("GET", key, fmt.Sprintf("RANGE(%d,%d) ", boff, len(p)), err, used)
 		s.store.objectDataBytes.WithLabelValues("GET").Add(float64(n))
 		s.store.objectReqsHistogram.WithLabelValues("GET").Observe(used.Seconds())
 		s.store.fetcher.fetch(key)
@@ -209,10 +206,7 @@ func (s *rSlice) delete(indx int) error {
 		strings.Contains(err.Error(), "No such file")) {
 		err = nil
 	}
-	logger.Debugf("DELETE %v (%v, %.3fs)", key, err, used.Seconds())
-	if used > SlowRequest {
-		logger.Infof("slow request: DELETE %v (%v, %.3fs)", key, err, used.Seconds())
-	}
+	logRequest("DELETE", key, "", err, used)
 	s.store.objectReqsHistogram.WithLabelValues("DELETE").Observe(used.Seconds())
 	if err != nil {
 		s.store.objectReqErrors.Add(1)
@@ -347,10 +341,7 @@ func (store *cachedStore) put(key string, p *Page) error {
 		st := time.Now()
 		err := store.storage.Put(key, bytes.NewReader(p.Data))
 		used := time.Since(st)
-		logger.Debugf("PUT %s (%s, %.3fs)", key, err, used.Seconds())
-		if used > SlowRequest {
-			logger.Infof("slow request: PUT %v (%v, %.3fs)", key, err, used.Seconds())
-		}
+		logRequest("PUT", key, "", err, used)
 		store.objectDataBytes.WithLabelValues("PUT").Add(float64(len(p.Data)))
 		store.objectReqsHistogram.WithLabelValues("PUT").Observe(used.Seconds())
 		if err != nil {
@@ -609,6 +600,20 @@ type cachedStore struct {
 	stageBlockDelay     prometheus.Counter
 }
 
+func logRequest(typeStr string, key string, param string, err error, used time.Duration) {
+	var info string
+	if id := object.ReqIDCache.Get(key); id != "" {
+		info += fmt.Sprintf("RequestID: %s ", id)
+	}
+	if err != nil {
+		info += err.Error()
+	}
+	logger.Debugf("%s %s %s(%v, %.3fs)", typeStr, key, param, info, used.Seconds())
+	if used > SlowRequest {
+		logger.Infof("slow request: %s %s %s(%v, %.3fs)", typeStr, key, param, info, used.Seconds())
+	}
+}
+
 func (store *cachedStore) load(key string, page *Page, cache bool, forceCache bool) (err error) {
 	defer func() {
 		e := recover()
@@ -654,10 +659,7 @@ func (store *cachedStore) load(key string, page *Page, cache bool, forceCache bo
 		err = nil
 	}
 	used := time.Since(start)
-	logger.Debugf("GET %s (%s, %.3fs)", key, err, used.Seconds())
-	if used > SlowRequest {
-		logger.Infof("slow request: GET %s (%v, %.3fs)", key, err, used.Seconds())
-	}
+	logRequest("GET", key, "", err, used)
 	if store.downLimit != nil && compressed {
 		store.downLimit.Wait(int64(n))
 	}
diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go
index d0462043..95a7ef68 100644
--- a/pkg/fs/fs.go
+++ b/pkg/fs/fs.go
@@ -417,7 +417,7 @@ func parentDir(p string) string {
 	return path.Dir(strings.TrimRight(p, "/"))
 }
 
-func (fs *FileSystem) Mkdir(ctx meta.Context, p string, mode uint16) (err syscall.Errno) {
+func (fs *FileSystem) Mkdir(ctx meta.Context, p string, mode uint16, umask uint16) (err syscall.Errno) {
 	defer trace.StartRegion(context.TODO(), "fs.Mkdir").End()
 	l := vfs.NewLogContext(ctx)
 	defer func() { fs.log(l, "Mkdir (%s, %o): %s", p, mode, errstr(err)) }()
@@ -429,16 +429,16 @@ func (fs *FileSystem) Mkdir(ctx meta.Context, p string, mode uint16) (err syscal
 		return err
 	}
 	var inode Ino
-	err = fs.m.Mkdir(ctx, fi.inode, path.Base(p), mode, 0, 0, &inode, nil)
+	err = fs.m.Mkdir(ctx, fi.inode, path.Base(p), mode, umask, 0, &inode, nil)
 	fs.invalidateEntry(fi.inode, path.Base(p))
 	return
 }
 
-func (fs *FileSystem) MkdirAll(ctx meta.Context, p string, mode uint16) (err syscall.Errno) {
-	err = fs.Mkdir(ctx, p, mode)
+func (fs *FileSystem) MkdirAll(ctx meta.Context, p string, mode uint16, umask uint16) (err syscall.Errno) {
+	err = fs.Mkdir(ctx, p, mode, umask)
 	if err == syscall.ENOENT {
-		_ = fs.MkdirAll(ctx, parentDir(p), mode)
-		err = fs.Mkdir(ctx, p, mode)
+		_ = fs.MkdirAll(ctx, parentDir(p), mode, umask)
+		err = fs.Mkdir(ctx, p, mode, umask)
 	}
 	if err == syscall.EEXIST {
 		err = 0
@@ -752,7 +752,7 @@ func (fs *FileSystem) resolve(ctx meta.Context, p string, followLastSymlink bool
 	return fi, 0
 }
 
-func (fs *FileSystem) Create(ctx meta.Context, p string, mode uint16) (f *File, err syscall.Errno) {
+func (fs *FileSystem) Create(ctx meta.Context, p string, mode uint16, umask uint16) (f *File, err syscall.Errno) {
 	defer trace.StartRegion(context.TODO(), "fs.Create").End()
 	l := vfs.NewLogContext(ctx)
 	defer func() { fs.log(l, "Create (%s,%o): %s", p, mode, errstr(err)) }()
@@ -766,7 +766,7 @@ func (fs *FileSystem) Create(ctx meta.Context, p string, mode uint16) (f *File,
 	if err != 0 {
 		return
 	}
-	err = fs.m.Create(ctx, fi.inode, path.Base(p), mode&07777, 0, syscall.O_EXCL, &inode, attr)
+	err = fs.m.Create(ctx, fi.inode, path.Base(p), mode&07777, umask, syscall.O_EXCL, &inode, attr)
 	if err == 0 {
 		fi = AttrToFileInfo(inode, attr)
 		fi.name = path.Base(p)
diff --git a/pkg/fs/http.go b/pkg/fs/http.go
index 23303a6f..102e5b90 100644
--- a/pkg/fs/http.go
+++ b/pkg/fs/http.go
@@ -26,6 +26,7 @@ import (
 	"syscall"
 
 	"github.com/juicedata/juicefs/pkg/meta"
+	"github.com/juicedata/juicefs/pkg/utils"
 	"github.com/juicedata/juicefs/pkg/vfs"
 	"golang.org/x/net/webdav"
 )
@@ -81,12 +82,13 @@ func econv(err error) error {
 }
 
 type webdavFS struct {
-	ctx meta.Context
-	fs  *FileSystem
+	ctx   meta.Context
+	fs    *FileSystem
+	umask uint16
 }
 
 func (hfs *webdavFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
-	return econv(hfs.fs.Mkdir(hfs.ctx, name, uint16(perm)))
+	return econv(hfs.fs.Mkdir(hfs.ctx, name, uint16(perm), hfs.umask))
 }
 
 func (hfs *webdavFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
@@ -104,7 +106,7 @@ func (hfs *webdavFS) OpenFile(ctx context.Context, name string, flag int, perm o
 	f, err := hfs.fs.Open(hfs.ctx, name, uint32(mode))
 	if err != 0 {
 		if err == syscall.ENOENT && flag&os.O_CREATE != 0 {
-			f, err = hfs.fs.Create(hfs.ctx, name, uint16(perm))
+			f, err = hfs.fs.Create(hfs.ctx, name, uint16(perm), hfs.umask)
 		}
 	} else if flag&os.O_TRUNC != 0 {
 		if errno := hfs.fs.Truncate(hfs.ctx, name, 0); errno != 0 {
@@ -219,7 +221,7 @@ func (h *indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 
 func StartHTTPServer(fs *FileSystem, config WebdavConfig) {
 	ctx := meta.NewContext(uint32(os.Getpid()), uint32(os.Getuid()), []uint32{uint32(os.Getgid())})
-	hfs := &webdavFS{ctx, fs}
+	hfs := &webdavFS{ctx, fs, uint16(utils.GetUmask())}
 	srv := &webdav.Handler{
 		FileSystem: hfs,
 		LockSystem: webdav.NewMemLS(),
diff --git a/pkg/gateway/gateway.go b/pkg/gateway/gateway.go
index f099e901..72902e89 100644
--- a/pkg/gateway/gateway.go
+++ b/pkg/gateway/gateway.go
@@ -51,8 +51,7 @@ var logger = utils.GetLogger("juicefs")
 type Config struct {
 	MultiBucket bool
 	KeepEtag    bool
-	Mode        uint16
-	DirMode     uint16
+	Umask       uint16
 }
 
 func NewJFSGateway(jfs *fs.FileSystem, conf *vfs.Config, gConf *Config) (minio.ObjectLayer, error) {
@@ -194,7 +193,7 @@ func (n *jfsObjects) MakeBucketWithLocation(ctx context.Context, bucket string,
 	if !n.gConf.MultiBucket {
 		return nil
 	}
-	eno := n.fs.Mkdir(mctx, n.path(bucket), n.gConf.DirMode)
+	eno := n.fs.Mkdir(mctx, n.path(bucket), 0777, n.gConf.Umask)
 	return jfsToObjectErr(ctx, eno, bucket)
 }
 
@@ -478,8 +477,8 @@ func (n *jfsObjects) CopyObject(ctx context.Context, srcBucket, srcObject, dstBu
 		return n.GetObjectInfo(ctx, srcBucket, srcObject, minio.ObjectOptions{})
 	}
 	tmp := n.tpath(dstBucket, "tmp", minio.MustGetUUID())
-	_ = n.mkdirAll(ctx, path.Dir(tmp), os.FileMode(n.gConf.DirMode))
-	f, eno := n.fs.Create(mctx, tmp, n.gConf.Mode)
+	_ = n.mkdirAll(ctx, path.Dir(tmp))
+	f, eno := n.fs.Create(mctx, tmp, 0666, n.gConf.Umask)
 	if eno != 0 {
 		logger.Errorf("create %s: %s", tmp, eno)
 		return
@@ -604,19 +603,19 @@ func (n *jfsObjects) GetObjectInfo(ctx context.Context, bucket, object string, o
 	}, nil
 }
 
-func (n *jfsObjects) mkdirAll(ctx context.Context, p string, mode os.FileMode) error {
+func (n *jfsObjects) mkdirAll(ctx context.Context, p string) error {
 	if fi, eno := n.fs.Stat(mctx, p); eno == 0 {
 		if !fi.IsDir() {
 			return fmt.Errorf("%s is not directory", p)
 		}
 		return nil
 	}
-	eno := n.fs.Mkdir(mctx, p, uint16(mode))
+	eno := n.fs.Mkdir(mctx, p, 0777, n.gConf.Umask)
 	if eno != 0 && fs.IsNotExist(eno) {
-		if err := n.mkdirAll(ctx, path.Dir(p), os.FileMode(n.gConf.DirMode)); err != nil {
+		if err := n.mkdirAll(ctx, path.Dir(p)); err != nil {
 			return err
 		}
-		eno = n.fs.Mkdir(mctx, p, uint16(mode))
+		eno = n.fs.Mkdir(mctx, p, 0777, n.gConf.Umask)
 	}
 	if eno != 0 && fs.IsExist(eno) {
 		eno = 0
@@ -629,8 +628,8 @@ func (n *jfsObjects) mkdirAll(ctx context.Context, p string, mode os.FileMode) e
 
 func (n *jfsObjects) putObject(ctx context.Context, bucket, object string, r *minio.PutObjReader, opts minio.ObjectOptions) (err error) {
 	tmpname := n.tpath(bucket, "tmp", minio.MustGetUUID())
-	_ = n.mkdirAll(ctx, path.Dir(tmpname), os.FileMode(n.gConf.DirMode))
-	f, eno := n.fs.Create(mctx, tmpname, n.gConf.Mode)
+	_ = n.mkdirAll(ctx, path.Dir(tmpname))
+	f, eno := n.fs.Create(mctx, tmpname, 0666, n.gConf.Umask)
 	if eno != 0 {
 		logger.Errorf("create %s: %s", tmpname, eno)
 		err = eno
@@ -667,7 +666,7 @@ func (n *jfsObjects) putObject(ctx context.Context, bucket, object string, r *mi
 	}
 	dir := path.Dir(object)
 	if dir != "" {
-		_ = n.mkdirAll(ctx, dir, os.FileMode(n.gConf.DirMode))
+		_ = n.mkdirAll(ctx, dir)
 	}
 	if eno := n.fs.Rename(mctx, tmpname, object, 0); eno != 0 {
 		err = jfsToObjectErr(ctx, eno, bucket, object)
@@ -683,7 +682,7 @@ func (n *jfsObjects) PutObject(ctx context.Context, bucket string, object string
 
 	p := n.path(bucket, object)
 	if strings.HasSuffix(object, sep) {
-		if err = n.mkdirAll(ctx, p, os.FileMode(n.gConf.DirMode)); err != nil {
+		if err = n.mkdirAll(ctx, p); err != nil {
 			err = jfsToObjectErr(ctx, err, bucket, object)
 			return
 		}
@@ -728,7 +727,7 @@ func (n *jfsObjects) NewMultipartUpload(ctx context.Context, bucket string, obje
 	}
 	uploadID = minio.MustGetUUID()
 	p := n.upath(bucket, uploadID)
-	err = n.mkdirAll(ctx, p, os.FileMode(n.gConf.DirMode))
+	err = n.mkdirAll(ctx, p)
 	if err == nil {
 		eno := n.fs.SetXattr(mctx, p, uploadKeyName, []byte(object), 0)
 		if eno != 0 {
@@ -882,7 +881,7 @@ func (n *jfsObjects) CompleteMultipartUpload(ctx context.Context, bucket, object
 
 	tmp := n.ppath(bucket, uploadID, "complete")
 	_ = n.fs.Delete(mctx, tmp)
-	f, eno := n.fs.Create(mctx, tmp, n.gConf.Mode)
+	f, eno := n.fs.Create(mctx, tmp, 0666, n.gConf.Umask)
 	if eno != 0 {
 		err = jfsToObjectErr(ctx, eno, bucket, object, uploadID)
 		logger.Errorf("create complete: %s", err)
@@ -906,7 +905,7 @@ func (n *jfsObjects) CompleteMultipartUpload(ctx context.Context, bucket, object
 	name := n.path(bucket, object)
 	dir := path.Dir(name)
 	if dir != "" {
-		if err = n.mkdirAll(ctx, dir, os.FileMode(n.gConf.DirMode)); err != nil {
+		if err = n.mkdirAll(ctx, dir); err != nil {
 			_ = n.fs.Delete(mctx, tmp)
 			err = jfsToObjectErr(ctx, err, bucket, object, uploadID)
 			return
diff --git a/pkg/object/azure.go b/pkg/object/azure.go
index 81e16d9f..2c860315 100644
--- a/pkg/object/azure.go
+++ b/pkg/object/azure.go
@@ -34,6 +34,7 @@ import (
 	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
 	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
 	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas"
+	"github.com/aws/aws-sdk-go/aws"
 )
 
 type wasb struct {
@@ -82,6 +83,7 @@ func (b *wasb) Get(key string, off, limit int64) (io.ReadCloser, error) {
 	if err != nil {
 		return nil, err
 	}
+	ReqIDCache.put(key, aws.StringValue(download.RequestID))
 	return download.Body, err
 }
 
@@ -99,7 +101,8 @@ func (b *wasb) Put(key string, data io.Reader) error {
 	if b.sc != "" {
 		options.AccessTier = str2Tier(b.sc)
 	}
-	_, err := b.azblobCli.UploadStream(ctx, b.cName, key, data, &options)
+	resp, err := b.azblobCli.UploadStream(ctx, b.cName, key, data, &options)
+	ReqIDCache.put(key, aws.StringValue(resp.RequestID))
 	return err
 }
 
@@ -119,12 +122,13 @@ func (b *wasb) Copy(dst, src string) error {
 }
 
 func (b *wasb) Delete(key string) error {
-	_, err := b.container.NewBlobClient(key).Delete(ctx, nil)
+	resp, err := b.container.NewBlobClient(key).Delete(ctx, nil)
 	if err != nil {
 		if e, ok := err.(*azcore.ResponseError); ok && e.ErrorCode == string(bloberror.BlobNotFound) {
 			err = nil
 		}
 	}
+	ReqIDCache.put(key, aws.StringValue(resp.RequestID))
 	return err
 }
 
diff --git a/pkg/object/cos.go b/pkg/object/cos.go
index f4a4bb4e..60f816f8 100644
--- a/pkg/object/cos.go
+++ b/pkg/object/cos.go
@@ -37,6 +37,7 @@ import (
 )
 
 const cosChecksumKey = "x-cos-meta-" + checksumAlgr
+const cosRequestIDKey = "X-Cos-Request-Id"
 
 type COS struct {
 	c        *cos.Client
@@ -109,6 +110,9 @@ func (c *COS) Get(key string, off, limit int64) (io.ReadCloser, error) {
 	if off == 0 && limit == -1 {
 		resp.Body = verifyChecksum(resp.Body, resp.Header.Get(cosChecksumKey))
 	}
+	if resp != nil {
+		ReqIDCache.put(key, resp.Header.Get(cosRequestIDKey))
+	}
 	return resp.Body, nil
 }
 
@@ -126,7 +130,10 @@ func (c *COS) Put(key string, in io.Reader) error {
 		}
 		options.ObjectPutHeaderOptions.XCosStorageClass = c.sc
 	}
-	_, err := c.c.Object.Put(ctx, key, in, &options)
+	resp, err := c.c.Object.Put(ctx, key, in, &options)
+	if resp != nil {
+		ReqIDCache.put(key, resp.Header.Get(cosRequestIDKey))
+	}
 	return err
 }
 
@@ -141,7 +148,10 @@ func (c *COS) Copy(dst, src string) error {
 }
 
 func (c *COS) Delete(key string) error {
-	_, err := c.c.Object.Delete(ctx, key)
+	resp, err := c.c.Object.Delete(ctx, key)
+	if resp != nil {
+		ReqIDCache.put(key, resp.Header.Get(cosRequestIDKey))
+	}
 	return err
 }
 
diff --git a/pkg/object/file.go b/pkg/object/file.go
index a6694de1..8a605035 100644
--- a/pkg/object/file.go
+++ b/pkg/object/file.go
@@ -48,7 +48,7 @@ type filestore struct {
 func (d *filestore) Symlink(oldName, newName string) error {
 	p := d.path(newName)
 	if _, err := os.Stat(filepath.Dir(p)); err != nil && os.IsNotExist(err) {
-		if err := os.MkdirAll(filepath.Dir(p), os.FileMode(0755)); err != nil {
+		if err := os.MkdirAll(filepath.Dir(p), os.FileMode(0777)); err != nil {
 			return err
 		}
 	} else if err != nil && !os.IsNotExist(err) {
@@ -141,16 +141,16 @@ func (d *filestore) Put(key string, in io.Reader) error {
 	p := d.path(key)
 
 	if strings.HasSuffix(key, dirSuffix) || key == "" && strings.HasSuffix(d.root, dirSuffix) {
-		return os.MkdirAll(p, os.FileMode(0755))
+		return os.MkdirAll(p, os.FileMode(0777))
 	}
 
 	tmp := filepath.Join(filepath.Dir(p), "."+filepath.Base(p)+".tmp"+strconv.Itoa(rand.Int()))
-	f, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
+	f, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
 	if err != nil && os.IsNotExist(err) {
-		if err := os.MkdirAll(filepath.Dir(p), os.FileMode(0755)); err != nil {
+		if err := os.MkdirAll(filepath.Dir(p), os.FileMode(0777)); err != nil {
 			return err
 		}
-		f, err = os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
+		f, err = os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
 	}
 	if err != nil {
 		return err
@@ -334,13 +334,13 @@ func newDisk(root, accesskey, secretkey, token string) (ObjectStorage, error) {
 	}
 	if strings.HasSuffix(root, dirSuffix) {
 		logger.Debugf("Ensure directory %s", root)
-		if err := os.MkdirAll(root, 0755); err != nil {
+		if err := os.MkdirAll(root, 0777); err != nil {
 			return nil, fmt.Errorf("Creating directory %s failed: %q", root, err)
 		}
 	} else {
 		dir := path.Dir(root)
 		logger.Debugf("Ensure directory %s", dir)
-		if err := os.MkdirAll(dir, 0755); err != nil {
+		if err := os.MkdirAll(dir, 0777); err != nil {
 			return nil, fmt.Errorf("Creating directory %s failed: %q", dir, err)
 		}
 	}
diff --git a/pkg/object/hdfs.go b/pkg/object/hdfs.go
index cd84af72..bb977ca1 100644
--- a/pkg/object/hdfs.go
+++ b/pkg/object/hdfs.go
@@ -43,9 +43,10 @@ var supergroup = "supergroup"
 type hdfsclient struct {
 	DefaultObjectStorage
 	addr           string
+	basePath       string
 	c              *hdfs.Client
 	dfsReplication int
-	basePath       string
+	umask          os.FileMode
 }
 
 func (h *hdfsclient) String() string {
@@ -125,15 +126,15 @@ const abcException = "org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedExcepti
 func (h *hdfsclient) Put(key string, in io.Reader) error {
 	path := h.path(key)
 	if strings.HasSuffix(path, dirSuffix) {
-		return h.c.MkdirAll(path, os.FileMode(0755))
+		return h.c.MkdirAll(path, 0777&^h.umask)
 	}
 	tmp := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s.tmp.%d", filepath.Base(path), rand.Int()))
-	f, err := h.c.CreateFile(tmp, h.dfsReplication, 128<<20, 0755)
+	f, err := h.c.CreateFile(tmp, h.dfsReplication, 128<<20, 0666&^h.umask)
 	defer func() { _ = h.c.Remove(tmp) }()
 	if err != nil {
 		if pe, ok := err.(*os.PathError); ok && pe.Err == os.ErrNotExist {
-			_ = h.c.MkdirAll(filepath.Dir(path), 0755)
-			f, err = h.c.CreateFile(tmp, h.dfsReplication, 128<<20, 0755)
+			_ = h.c.MkdirAll(filepath.Dir(path), 0777&^h.umask)
+			f, err = h.c.CreateFile(tmp, h.dfsReplication, 128<<20, 0666&^h.umask)
 		}
 		if pe, ok := err.(*os.PathError); ok {
 			if remoteErr, ok := pe.Err.(hdfs.Error); ok && remoteErr.Exception() == abcException {
@@ -141,7 +142,7 @@ func (h *hdfsclient) Put(key string, in io.Reader) error {
 			}
 			if pe.Err == os.ErrExist {
 				_ = h.c.Remove(tmp)
-				f, err = h.c.CreateFile(tmp, h.dfsReplication, 128<<20, 0755)
+				f, err = h.c.CreateFile(tmp, h.dfsReplication, 128<<20, 0666&^h.umask)
 			}
 		}
 		if err != nil {
@@ -302,13 +303,25 @@ func newHDFS(addr, username, sk, token string) (ObjectStorage, error) {
 	}
 
 	var replication = 3
-	if replication_conf, found := conf["dfs.replication"]; found {
-		if x, err := strconv.Atoi(replication_conf); err == nil {
+	if v, found := conf["dfs.replication"]; found {
+		if x, err := strconv.Atoi(v); err == nil {
 			replication = x
 		}
 	}
+	var umask uint16 = 022
+	if v, found := conf["fs.permissions.umask-mode"]; found {
+		if x, err := strconv.ParseUint(v, 8, 16); err == nil {
+			umask = uint16(x)
+		}
+	}
 
-	return &hdfsclient{addr: strings.Join(rpcAddr, ","), c: c, dfsReplication: replication, basePath: basePath}, nil
+	return &hdfsclient{
+		addr:           strings.Join(rpcAddr, ","),
+		basePath:       basePath,
+		c:              c,
+		dfsReplication: replication,
+		umask:          os.FileMode(umask),
+	}, nil
 }
 
 // addr can be hdfs://nameservice e.g. hdfs://example, hdfs://example/user/juicefs
diff --git a/pkg/object/ibmcos.go b/pkg/object/ibmcos.go
index 2e87d82e..8f5d3eca 100644
--- a/pkg/object/ibmcos.go
+++ b/pkg/object/ibmcos.go
@@ -30,14 +30,14 @@ import (
 	"strings"
 	"time"
 
-	"github.com/pkg/errors"
-
 	"github.com/IBM/ibm-cos-sdk-go/aws"
 	"github.com/IBM/ibm-cos-sdk-go/aws/awserr"
 	"github.com/IBM/ibm-cos-sdk-go/aws/credentials/ibmiam"
+	"github.com/IBM/ibm-cos-sdk-go/aws/request"
 	"github.com/IBM/ibm-cos-sdk-go/aws/session"
 	"github.com/IBM/ibm-cos-sdk-go/service/s3"
 	"github.com/juicedata/juicefs/pkg/utils"
+	"github.com/pkg/errors"
 )
 
 type ibmcos struct {
@@ -83,7 +83,9 @@ func (s *ibmcos) Get(key string, off, limit int64) (io.ReadCloser, error) {
 		}
 		params.Range = &r
 	}
-	resp, err := s.s3.GetObject(params)
+	var reqID string
+	resp, err := s.s3.GetObjectWithContext(c, params, request.WithGetResponseHeader(s3RequestIDKey, &reqID))
+	ReqIDCache.put(key, reqID)
 	if err != nil {
 		return nil, err
 	}
@@ -111,7 +113,9 @@ func (s *ibmcos) Put(key string, in io.Reader) error {
 	if s.sc != "" {
 		params.SetStorageClass(s.sc)
 	}
-	_, err := s.s3.PutObject(params)
+	var reqID string
+	_, err := s.s3.PutObjectWithContext(c, params, request.WithGetResponseHeader(s3RequestIDKey, &reqID))
+	ReqIDCache.put(key, reqID)
 	return err
 }
 
@@ -155,7 +159,9 @@ func (s *ibmcos) Delete(key string) error {
 		Bucket: &s.bucket,
 		Key:    &key,
 	}
-	_, err := s.s3.DeleteObject(&param)
+	var reqID string
+	_, err := s.s3.DeleteObjectWithContext(c, &param, request.WithGetResponseHeader(s3RequestIDKey, &reqID))
+	ReqIDCache.put(key, reqID)
 	return err
 }
 
diff --git a/pkg/object/ks3.go b/pkg/object/ks3.go
index 57c834f5..1ab462bb 100644
--- a/pkg/object/ks3.go
+++ b/pkg/object/ks3.go
@@ -32,6 +32,7 @@ import (
 
 	"github.com/pkg/errors"
 
+	aws2 "github.com/aws/aws-sdk-go/aws"
 	"github.com/juicedata/juicefs/pkg/utils"
 	"github.com/ks3sdklib/aws-sdk-go/aws"
 	"github.com/ks3sdklib/aws-sdk-go/aws/awserr"
@@ -108,6 +109,9 @@ func (s *ks3) Get(key string, off, limit int64) (io.ReadCloser, error) {
 		params.Range = &r
 	}
 	resp, err := s.s3.GetObject(params)
+	if resp != nil {
+		ReqIDCache.put(key, aws2.StringValue(resp.Metadata[s3RequestIDKey]))
+	}
 	if err != nil {
 		return nil, err
 	}
@@ -135,7 +139,10 @@ func (s *ks3) Put(key string, in io.Reader) error {
 	if s.sc != "" {
 		params.StorageClass = aws.String(s.sc)
 	}
-	_, err := s.s3.PutObject(params)
+	resp, err := s.s3.PutObject(params)
+	if resp != nil {
+		ReqIDCache.put(key, aws2.StringValue(resp.Metadata[s3RequestIDKey]))
+	}
 	return err
 }
 func (s *ks3) Copy(dst, src string) error {
@@ -157,7 +164,10 @@ func (s *ks3) Delete(key string) error {
 		Bucket: &s.bucket,
 		Key:    &key,
 	}
-	_, err := s.s3.DeleteObject(&param)
+	resp, err := s.s3.DeleteObject(&param)
+	if resp != nil {
+		ReqIDCache.put(key, aws2.StringValue(resp.Metadata[s3RequestIDKey]))
+	}
 	if e, ok := err.(awserr.RequestFailure); ok && e.StatusCode() == http.StatusNotFound {
 		return nil
 	}
diff --git a/pkg/object/object_storage.go b/pkg/object/object_storage.go
index 98880875..df70ef2b 100644
--- a/pkg/object/object_storage.go
+++ b/pkg/object/object_storage.go
@@ -280,3 +280,52 @@ func ListAllWithDelimiter(store ObjectStorage, prefix, start, end string) (<-cha
 	}()
 	return listed, nil
 }
+
+func init() {
+	ReqIDCache = reqIDCache{cache: make(map[string]reqItem)}
+	go ReqIDCache.clean()
+}
+
+type reqItem struct {
+	reqID string
+	time  time.Time
+}
+
+var ReqIDCache reqIDCache
+
+type reqIDCache struct {
+	sync.Mutex
+	cache map[string]reqItem
+}
+
+func (*reqIDCache) clean() {
+	for range time.Tick(time.Second) {
+		ReqIDCache.Lock()
+		for k, v := range ReqIDCache.cache {
+			if time.Since(v.time) > time.Second {
+				delete(ReqIDCache.cache, k)
+			}
+		}
+		ReqIDCache.Unlock()
+	}
+}
+
+func (*reqIDCache) put(key, reqID string) {
+	if reqID == "" {
+		return
+	}
+	if part := strings.Split(key, "chunks"); len(part) == 2 {
+		ReqIDCache.Lock()
+		defer ReqIDCache.Unlock()
+		ReqIDCache.cache[part[1]] = reqItem{reqID: reqID, time: time.Now()}
+	}
+}
+
+func (*reqIDCache) Get(key string) string {
+	if part := strings.Split(key, "chunks"); len(part) == 2 {
+		ReqIDCache.Lock()
+		defer ReqIDCache.Unlock()
+		return ReqIDCache.cache[part[1]].reqID
+	}
+	return ""
+}
diff --git a/pkg/object/obs.go b/pkg/object/obs.go
index e90b78c3..0f963c39 100644
--- a/pkg/object/obs.go
+++ b/pkg/object/obs.go
@@ -118,6 +118,9 @@ func (s *obsClient) Get(key string, off, limit int64) (io.ReadCloser, error) {
 	} else {
 		resp, err = s.c.GetObject(params)
 	}
+	if resp != nil {
+		ReqIDCache.put(key, resp.RequestId)
+	}
 	if err != nil {
 		return nil, err
 	}
@@ -170,6 +173,9 @@ func (s *obsClient) Put(key string, in io.Reader) error {
 	if err == nil && s.checkEtag && strings.Trim(resp.ETag, "\"") != obs.Hex(sum) {
 		err = fmt.Errorf("unexpected ETag: %s != %s", strings.Trim(resp.ETag, "\""), obs.Hex(sum))
 	}
+	if resp != nil {
+		ReqIDCache.put(key, resp.RequestId)
+	}
 	return err
 }
 
@@ -188,7 +194,10 @@ func (s *obsClient) Delete(key string) error {
 	params := obs.DeleteObjectInput{}
 	params.Bucket = s.bucket
 	params.Key = key
-	_, err := s.c.DeleteObject(&params)
+	resp, err := s.c.DeleteObject(&params)
+	if resp != nil {
+		ReqIDCache.put(key, resp.RequestId)
+	}
 	return err
 }
 
diff --git a/pkg/object/oss.go b/pkg/object/oss.go
index e05524b9..68dadc3f 100644
--- a/pkg/object/oss.go
+++ b/pkg/object/oss.go
@@ -114,6 +114,7 @@ func (o *ossClient) Head(key string) (Object, error) {
 }
 
 func (o *ossClient) Get(key string, off, limit int64) (resp io.ReadCloser, err error) {
+	var respHeader http.Header
 	if off > 0 || limit > 0 {
 		var r string
 		if limit > 0 {
@@ -121,14 +122,15 @@ func (o *ossClient) Get(key string, off, limit int64) (resp io.ReadCloser, err e
 		} else {
 			r = fmt.Sprintf("%d-", off)
 		}
-		resp, err = o.bucket.GetObject(key, oss.NormalizedRange(r), oss.RangeBehavior("standard"))
+		resp, err = o.bucket.GetObject(key, oss.NormalizedRange(r), oss.RangeBehavior("standard"), oss.GetResponseHeader(&respHeader))
 	} else {
-		resp, err = o.bucket.GetObject(key)
+		resp, err = o.bucket.GetObject(key, oss.GetResponseHeader(&respHeader))
 		if err == nil {
 			resp = verifyChecksum(resp,
 				resp.(*oss.Response).Headers.Get(oss.HTTPHeaderOssMetaPrefix+checksumAlgr))
 		}
 	}
+	ReqIDCache.put(key, respHeader.Get(oss.HTTPHeaderOssRequestID))
 	err = o.checkError(err)
 	return
 }
@@ -141,7 +143,11 @@ func (o *ossClient) Put(key string, in io.Reader) error {
 	if o.sc != "" {
 		option = append(option, oss.ObjectStorageClass(oss.StorageClassType(o.sc)))
 	}
-	return o.checkError(o.bucket.PutObject(key, in, option...))
+	var respHeader http.Header
+	option = append(option, oss.GetResponseHeader(&respHeader))
+	err := o.bucket.PutObject(key, in, option...)
+	ReqIDCache.put(key, respHeader.Get(oss.HTTPHeaderOssRequestID))
+	return o.checkError(err)
 }
 
 func (o *ossClient) Copy(dst, src string) error {
@@ -154,7 +160,10 @@ func (o *ossClient) Copy(dst, src string) error {
 }
 
 func (o *ossClient) Delete(key string) error {
-	return o.checkError(o.bucket.DeleteObject(key))
+	var respHeader http.Header
+	err := o.bucket.DeleteObject(key, oss.GetResponseHeader(&respHeader))
+	ReqIDCache.put(key, respHeader.Get(oss.HTTPHeaderOssRequestID))
+	return o.checkError(err)
 }
 
 func (o *ossClient) List(prefix, marker, delimiter string, limit int64) ([]Object, error) {
diff --git a/pkg/object/qingstor.go b/pkg/object/qingstor.go
index 4244f991..2117d167 100644
--- a/pkg/object/qingstor.go
+++ b/pkg/object/qingstor.go
@@ -88,6 +88,9 @@ func (q *qingstor) Get(key string, off, limit int64) (io.ReadCloser, error) {
 		input.Range = &rangeStr
 	}
 	output, err := q.bucket.GetObject(key, input)
+	if output != nil {
+		ReqIDCache.put(key, aws.StringValue(output.RequestID))
+	}
 	if err != nil {
 		return nil, err
 	}
@@ -148,6 +151,9 @@ func (q *qingstor) Put(key string, in io.Reader) error {
 		input.XQSStorageClass = &q.sc
 	}
 	out, err := q.bucket.PutObject(key, input)
+	if out != nil {
+		ReqIDCache.put(key, aws.StringValue(out.RequestID))
+	}
 	if err != nil {
 		return err
 	}
@@ -176,7 +182,10 @@ func (q *qingstor) Copy(dst, src string) error {
 }
 
 func (q *qingstor) Delete(key string) error {
-	_, err := q.bucket.DeleteObject(key)
+	output, err := q.bucket.DeleteObject(key)
+	if output != nil {
+		ReqIDCache.put(key, aws.StringValue(output.RequestID))
+	}
 	return err
 }
 
diff --git a/pkg/object/s3.go b/pkg/object/s3.go
index 8f03199b..a053673e 100644
--- a/pkg/object/s3.go
+++ b/pkg/object/s3.go
@@ -43,6 +43,7 @@ import (
 )
 
 const awsDefaultRegion = "us-east-1"
+const s3RequestIDKey = "X-Amz-Request-Id"
 
 var disableSha256Func = func(r *request.Request) {
 	if op := r.Operation.Name; r.ClientInfo.ServiceID != "S3" || !(op == "PutObject" || op == "UploadPart") {
@@ -129,7 +130,9 @@ func (s *s3client) Get(key string, off, limit int64) (io.ReadCloser, error) {
 		}
 		params.Range = &r
 	}
-	resp, err := s.s3.GetObject(params)
+	var reqID string
+	resp, err := s.s3.GetObjectWithContext(c, params, request.WithGetResponseHeader(s3RequestIDKey, &reqID))
+	ReqIDCache.put(key, reqID)
 	if err != nil {
 		return nil, err
 	}
@@ -165,7 +168,9 @@ func (s *s3client) Put(key string, in io.Reader) error {
 	if s.sc != "" {
 		params.SetStorageClass(s.sc)
 	}
-	_, err := s.s3.PutObject(params)
+	var reqID string
+	_, err := s.s3.PutObjectWithContext(c, params, request.WithGetResponseHeader(s3RequestIDKey, &reqID))
+	ReqIDCache.put(key, reqID)
 	return err
 }
 
@@ -188,10 +193,12 @@ func (s *s3client) Delete(key string) error {
 		Bucket: &s.bucket,
 		Key:    &key,
 	}
-	_, err := s.s3.DeleteObject(&param)
+	var reqID string
+	_, err := s.s3.DeleteObjectWithContext(c, &param, request.WithGetResponseHeader(s3RequestIDKey, &reqID))
 	if err != nil && strings.Contains(err.Error(), "NoSuchKey") {
 		err = nil
 	}
+	ReqIDCache.put(key, reqID)
 	return err
 }
 
diff --git a/pkg/object/tos.go b/pkg/object/tos.go
index 9395c6aa..bb13394b 100644
--- a/pkg/object/tos.go
+++ b/pkg/object/tos.go
@@ -72,6 +72,9 @@ func (t *tosClient) Get(key string, off, limit int64) (io.ReadCloser, error) {
 		Key:    key,
 		Range:  rangeStr, // When Range and RangeStart & RangeEnd appear together, range is preferred
 	})
+	if resp != nil {
+		ReqIDCache.put(key, resp.RequestID)
+	}
 	if err != nil {
 		return nil, err
 	}
@@ -83,7 +86,7 @@ func (t *tosClient) Get(key string, off, limit int64) (io.ReadCloser, error) {
 }
 
 func (t *tosClient) Put(key string, in io.Reader) error {
-	_, err := t.client.PutObjectV2(context.Background(), &tos.PutObjectV2Input{
+	resp, err := t.client.PutObjectV2(context.Background(), &tos.PutObjectV2Input{
 		PutObjectBasicInput: tos.PutObjectBasicInput{
 			Bucket:       t.bucket,
 			Key:          key,
@@ -91,14 +94,20 @@ func (t *tosClient) Put(key string, in io.Reader) error {
 		},
 		Content: in,
 	})
+	if resp != nil {
+		ReqIDCache.put(key, resp.RequestID)
+	}
 	return err
 }
 
 func (t *tosClient) Delete(key string) error {
-	_, err := t.client.DeleteObjectV2(context.Background(), &tos.DeleteObjectV2Input{
+	resp, err := t.client.DeleteObjectV2(context.Background(), &tos.DeleteObjectV2Input{
 		Bucket: t.bucket,
 		Key:    key,
 	})
+	if resp != nil {
+		ReqIDCache.put(key, resp.RequestID)
+	}
 	return err
 }
 
diff --git a/pkg/winfsp/winfs.go b/pkg/winfsp/winfs.go
index 8bd79492..e726bf64 100644
--- a/pkg/winfsp/winfs.go
+++ b/pkg/winfsp/winfs.go
@@ -132,7 +132,7 @@ func (j *juice) Mkdir(path string, mode uint32) (e int) {
 	}
 	ctx := j.newContext()
 	defer trace(path, mode)(&e)
-	e = errorconv(j.fs.Mkdir(ctx, path, uint16(mode)))
+	e = errorconv(j.fs.Mkdir(ctx, path, uint16(mode), 0))
 	return
 }
 
diff --git a/sdk/java/libjfs/main.go b/sdk/java/libjfs/main.go
index 5783a910..ef638882 100644
--- a/sdk/java/libjfs/main.go
+++ b/sdk/java/libjfs/main.go
@@ -732,13 +732,13 @@ func jfs_access(pid int, h uintptr, cpath *C.char, flags int) int {
 }
 
 //export jfs_create
-func jfs_create(pid int, h uintptr, cpath *C.char, mode uint16) int {
+func jfs_create(pid int, h uintptr, cpath *C.char, mode uint16, umask uint16) int {
 	w := F(h)
 	if w == nil {
 		return EINVAL
 	}
 	path := C.GoString(cpath)
-	f, err := w.Create(w.withPid(pid), path, mode)
+	f, err := w.Create(w.withPid(pid), path, mode, umask)
 	if err != 0 {
 		return errno(err)
 	}
@@ -750,12 +750,12 @@ func jfs_create(pid int, h uintptr, cpath *C.char, mode uint16) int {
 }
 
 //export jfs_mkdir
-func jfs_mkdir(pid int, h uintptr, cpath *C.char, mode C.mode_t) int {
+func jfs_mkdir(pid int, h uintptr, cpath *C.char, mode uint16, umask uint16) int {
 	w := F(h)
 	if w == nil {
 		return EINVAL
 	}
-	err := errno(w.Mkdir(w.withPid(pid), C.GoString(cpath), uint16(mode)))
+	err := errno(w.Mkdir(w.withPid(pid), C.GoString(cpath), mode, umask))
 	if err == 0 && w.ctx.Uid() == 0 && w.user != w.superuser {
 		// belongs to supergroup
 		_ = setOwner(w, w.withPid(pid), C.GoString(cpath), w.user, "")
@@ -1093,7 +1093,7 @@ func jfs_concat(pid int, h uintptr, _dst *C.char, buf uintptr, bufsize int) int
 	var tmp string
 	if len(srcs) > 1 {
 		tmp = filepath.Join(filepath.Dir(dst), "."+filepath.Base(dst)+".merging")
-		fi, err := w.Create(ctx, tmp, 0644)
+		fi, err := w.Create(ctx, tmp, 0666, 022)
 		if err != 0 {
 			return errno(err)
 		}
diff --git a/sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java b/sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java
index 33118cef..c5664936 100644
--- a/sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java
+++ b/sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java
@@ -138,7 +138,7 @@ public class JuiceFileSystemImpl extends FileSystem {
 
     int jfs_close(long pid, int fd);
 
-    int jfs_create(long pid, long h, String path, short mode);
+    int jfs_create(long pid, long h, String path, short mode, short umask);
 
     int jfs_truncate(long pid, long h, String path, long length);
 
@@ -146,7 +146,7 @@ public class JuiceFileSystemImpl extends FileSystem {
 
     int jfs_rmr(long pid, long h, String path);
 
-    int jfs_mkdir(long pid, long h, String path, short mode);
+    int jfs_mkdir(long pid, long h, String path, short mode, short umask);
 
     int jfs_rename(long pid, long h, String src, String dst);
 
@@ -581,7 +581,7 @@ public class JuiceFileSystemImpl extends FileSystem {
     LibraryLoader<Libjfs> libjfsLibraryLoader = LibraryLoader.create(Libjfs.class);
     libjfsLibraryLoader.failImmediately();
 
-    int soVer = 8;
+    int soVer = 7;
     String osId = "so";
     String archId = "amd64";
     String resourceFormat = "libjfs-%s.%s.gz";
@@ -1184,9 +1184,8 @@ public class JuiceFileSystemImpl extends FileSystem {
   public FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, int bufferSize,
                                    short replication, long blockSize, Progressable progress) throws IOException {
     statistics.incrementWriteOps(1);
-    permission = permission.applyUMask(uMask);
     while (true) {
-      int fd = lib.jfs_create(Thread.currentThread().getId(), handle, normalizePath(f), permission.toShort());
+      int fd = lib.jfs_create(Thread.currentThread().getId(), handle, normalizePath(f), permission.toShort(), uMask.toShort());
       if (fd == ENOENT) {
         Path parent = makeQualified(f).getParent();
         try {
@@ -1220,14 +1219,13 @@ public class JuiceFileSystemImpl extends FileSystem {
   public FSDataOutputStream createNonRecursive(Path f, FsPermission permission, EnumSet<CreateFlag> flag,
                                                int bufferSize, short replication, long blockSize, Progressable progress) throws IOException {
     statistics.incrementWriteOps(1);
-    permission = permission.applyUMask(uMask);
-    int fd = lib.jfs_create(Thread.currentThread().getId(), handle, normalizePath(f), permission.toShort());
+    int fd = lib.jfs_create(Thread.currentThread().getId(), handle, normalizePath(f), permission.toShort(), uMask.toShort());
     while (fd == EEXIST) {
       if (!flag.contains(CreateFlag.OVERWRITE) || isDirectory(f)) {
         throw new FileAlreadyExistsException("File already exists: " + f);
       }
       delete(f, false);
-      fd = lib.jfs_create(Thread.currentThread().getId(), handle, normalizePath(f), permission.toShort());
+      fd = lib.jfs_create(Thread.currentThread().getId(), handle, normalizePath(f), permission.toShort(), uMask.toShort());
     }
     if (fd < 0) {
       throw error(fd, makeQualified(f).getParent());
@@ -1517,7 +1515,7 @@ public class JuiceFileSystemImpl extends FileSystem {
     String path = normalizePath(f);
     if ("/".equals(path))
       return true;
-    int r = lib.jfs_mkdir(Thread.currentThread().getId(), handle, path, permission.applyUMask(uMask).toShort());
+    int r = lib.jfs_mkdir(Thread.currentThread().getId(), handle, path, permission.toShort(), uMask.toShort());
     if (r == 0 || r == EEXIST && !isFile(f)) {
       return true;
     } else if (r == ENOENT) {


Test patch:
diff --git a/cmd/mdtest.go b/cmd/mdtest.go
index 9b9bc5e5..700c8ea5 100644
--- a/cmd/mdtest.go
+++ b/cmd/mdtest.go
@@ -36,9 +36,10 @@ import (
 )
 
 var ctx = meta.NewContext(1, uint32(os.Getuid()), []uint32{uint32(os.Getgid())})
+var umask = uint16(utils.GetUmask())
 
 func createDir(jfs *fs.FileSystem, root string, d int, width int) error {
-	if err := jfs.Mkdir(ctx, root, 0755); err != 0 {
+	if err := jfs.Mkdir(ctx, root, 0777, umask); err != 0 {
 		return fmt.Errorf("Mkdir %s: %s", root, err)
 	}
 	if d > 0 {
@@ -56,7 +57,7 @@ func createFile(jfs *fs.FileSystem, bar *utils.Bar, np int, root string, d int,
 	m := jfs.Meta()
 	for i := 0; i < files; i++ {
 		fn := filepath.Join(root, fmt.Sprintf("file.mdtest.%d.%d", np, i))
-		f, err := jfs.Create(ctx, fn, 0644)
+		f, err := jfs.Create(ctx, fn, 0666, umask)
 		if err != 0 {
 			return fmt.Errorf("create %s: %s", fn, err)
 		}
@@ -111,11 +112,11 @@ func runTest(jfs *fs.FileSystem, rootDir string, np, width, depth, files, bytes
 	logger.Infof("Create %d files in %d dirs", total, dirs)
 
 	start := time.Now()
-	if err := jfs.Mkdir(ctx, rootDir, 0755); err != 0 {
+	if err := jfs.Mkdir(ctx, rootDir, 0777, umask); err != 0 {
 		logger.Errorf("mkdir %s: %s", rootDir, err)
 	}
 	root := filepath.Join(rootDir, "test-dir.0-0")
-	if err := jfs.Mkdir(ctx, root, 0755); err != 0 {
+	if err := jfs.Mkdir(ctx, root, 0777, umask); err != 0 {
 		logger.Fatalf("Mkdir %s: %s", root, err)
 	}
 	root = filepath.Join(root, "mdtest_tree.0")
diff --git a/cmd/object_test.go b/cmd/object_test.go
index 8abb323b..9a16e296 100644
--- a/cmd/object_test.go
+++ b/cmd/object_test.go
@@ -26,6 +26,7 @@ import (
 	"github.com/juicedata/juicefs/pkg/fs"
 	"github.com/juicedata/juicefs/pkg/meta"
 	"github.com/juicedata/juicefs/pkg/object"
+	"github.com/juicedata/juicefs/pkg/utils"
 	"github.com/juicedata/juicefs/pkg/vfs"
 )
 
@@ -170,7 +171,7 @@ func TestJFS(t *testing.T) {
 		t.Fatalf("initialize  failed: %s", err)
 	}
 
-	jstore := &juiceFS{object.DefaultObjectStorage{}, "test", jfs}
+	jstore := &juiceFS{object.DefaultObjectStorage{}, "test", uint16(utils.GetUmask()), jfs}
 	testFileSystem(t, jstore)
 	testFileSystem(t, object.WithPrefix(jstore, "unittest/"))
 }
diff --git a/pkg/fs/fs_test.go b/pkg/fs/fs_test.go
index 379138a9..47bede03 100644
--- a/pkg/fs/fs_test.go
+++ b/pkg/fs/fs_test.go
@@ -77,7 +77,7 @@ func TestFileSystem(t *testing.T) {
 	if e := fs.Access(ctx, "/", 7); e != 0 {
 		t.Fatalf("access /: %s", e)
 	}
-	f, err := fs.Create(ctx, "/hello", 0644)
+	f, err := fs.Create(ctx, "/hello", 0666, 022)
 	if err != 0 {
 		t.Fatalf("create /hello: %s", err)
 	}
@@ -184,7 +184,7 @@ func TestFileSystem(t *testing.T) {
 		t.Fatalf("open without permission: %s", e)
 	}
 
-	if err := fs.Mkdir(ctx, "/d", 0755); err != 0 {
+	if err := fs.Mkdir(ctx, "/d", 0777, 022); err != 0 {
 		t.Fatalf("mkdir /d: %s", err)
 	}
 	d, e := fs.Open(ctx, "/", 0)
@@ -246,13 +246,13 @@ func TestFileSystem(t *testing.T) {
 	}
 
 	// path with trailing /
-	if err := fs.Mkdir(ctx, "/ddd/", 0777); err != 0 {
+	if err := fs.Mkdir(ctx, "/ddd/", 0777, 000); err != 0 {
 		t.Fatalf("mkdir /ddd/: %s", err)
 	}
-	if _, err := fs.Create(ctx, "/ddd/ddd", 0777); err != 0 {
+	if _, err := fs.Create(ctx, "/ddd/ddd", 0777, 000); err != 0 {
 		t.Fatalf("create /ddd/ddd: %s", err)
 	}
-	if _, err := fs.Create(ctx, "/ddd/fff/", 0777); err != syscall.EINVAL {
+	if _, err := fs.Create(ctx, "/ddd/fff/", 0777, 000); err != syscall.EINVAL {
 		t.Fatalf("create /ddd/fff/: %s", err)
 	}
 	if err := fs.Delete(ctx, "/ddd/"); err != syscall.ENOTEMPTY {
diff --git a/pkg/fs/http_test.go b/pkg/fs/http_test.go
index efb724c0..f28f7a0a 100644
--- a/pkg/fs/http_test.go
+++ b/pkg/fs/http_test.go
@@ -18,16 +18,18 @@ package fs
 
 import (
 	"context"
-	"github.com/juicedata/juicefs/pkg/meta"
 	"io"
 	"io/fs"
 	"os"
 	"testing"
+
+	"github.com/juicedata/juicefs/pkg/meta"
+	"github.com/juicedata/juicefs/pkg/utils"
 )
 
 func TestWebdav(t *testing.T) {
 	jfs := createTestFS(t)
-	webdavFS := &webdavFS{meta.NewContext(uint32(os.Getpid()), uint32(os.Getuid()), []uint32{uint32(os.Getgid())}), jfs}
+	webdavFS := &webdavFS{meta.NewContext(uint32(os.Getpid()), uint32(os.Getuid()), []uint32{uint32(os.Getgid())}), jfs, uint16(utils.GetUmask())}
 	ctx := context.Background()
 	_, err := webdavFS.Stat(ctx, "/")
 	if err != nil {