2022-02-09 19:42:28 +03:00
|
|
|
/*
|
|
|
|
_____ _____ _____ ____ ______ _____ ------
|
|
|
|
| | | | | | | | | | | | |
|
|
|
|
| | | | | | | | | | | | |
|
|
|
|
| --- | | | | |-----| |---- | | |-----| |----- ------
|
|
|
|
| | | | | | | | | | | | |
|
|
|
|
| ____| |_____ | ____| | ____| | |_____| _____| |_____ |_____
|
|
|
|
|
|
|
|
|
|
|
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
|
|
|
|
2023-01-07 05:41:07 +03:00
|
|
|
Copyright © 2020-2023 Microsoft Corporation. All rights reserved.
|
2022-02-09 19:42:28 +03:00
|
|
|
Author : <blobfusedev@microsoft.com>
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
SOFTWARE
|
|
|
|
*/
|
|
|
|
|
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"crypto/aes"
|
|
|
|
"crypto/cipher"
|
|
|
|
"crypto/rand"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"os/user"
|
2022-09-01 17:00:08 +03:00
|
|
|
"path/filepath"
|
2022-02-09 19:42:28 +03:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2022-06-24 23:50:01 +03:00
|
|
|
"sync"
|
2023-03-22 07:59:36 +03:00
|
|
|
"syscall"
|
2022-02-09 19:42:28 +03:00
|
|
|
|
|
|
|
"gopkg.in/ini.v1"
|
|
|
|
)
|
|
|
|
|
|
|
|
var RootMount bool
|
2023-03-22 07:59:36 +03:00
|
|
|
var ForegroundMount bool
|
2022-02-09 19:42:28 +03:00
|
|
|
|
2023-05-10 12:23:05 +03:00
|
|
|
// IsDirectoryMounted is a utility function that returns true if the directory is already mounted using fuse
|
2022-02-09 19:42:28 +03:00
|
|
|
func IsDirectoryMounted(path string) bool {
|
2023-05-10 12:23:05 +03:00
|
|
|
mntList, err := os.ReadFile("/etc/mtab")
|
2022-02-09 19:42:28 +03:00
|
|
|
if err != nil {
|
|
|
|
//fmt.Println("failed to read mount points : ", err.Error())
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-04-01 08:10:49 +03:00
|
|
|
// removing trailing / from the path
|
|
|
|
path = strings.TrimRight(path, "/")
|
|
|
|
|
2022-02-09 19:42:28 +03:00
|
|
|
for _, line := range strings.Split(string(mntList), "\n") {
|
|
|
|
if strings.TrimSpace(line) != "" {
|
|
|
|
mntPoint := strings.Split(line, " ")[1]
|
|
|
|
if path == mntPoint {
|
2022-03-10 06:48:05 +03:00
|
|
|
// with earlier fuse driver ' fuse.' was searched in /etc/mtab
|
|
|
|
// however with libfuse entry does not have that signature
|
|
|
|
// if this path is already mounted using fuse then fail
|
|
|
|
if strings.Contains(line, "fuse") {
|
2022-02-09 19:42:28 +03:00
|
|
|
//fmt.Println(path, " is already mounted.")
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-05-10 12:23:05 +03:00
|
|
|
// IsDirectoryEmpty is a utility function that returns true if the directory at that path is empty or not
|
2022-02-09 19:42:28 +03:00
|
|
|
func IsDirectoryEmpty(path string) bool {
|
|
|
|
f, _ := os.Open(path)
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
_, err := f.Readdirnames(1)
|
|
|
|
if err == io.EOF {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil && err.Error() == "invalid argument" {
|
|
|
|
fmt.Println("Broken Mount : First Unmount ", path)
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-05-10 12:23:05 +03:00
|
|
|
// DirectoryExists is a utility function that returns true if the directory at that path exists and returns false if it does not exist.
|
2022-02-09 19:42:28 +03:00
|
|
|
func DirectoryExists(path string) bool {
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return false
|
|
|
|
} else if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2023-05-10 12:23:05 +03:00
|
|
|
// GetCurrentUser is a utility function that returns the UID and GID of the user that invokes the blobfuse2 command.
|
2022-02-09 19:42:28 +03:00
|
|
|
func GetCurrentUser() (uint32, uint32, error) {
|
|
|
|
var (
|
|
|
|
currentUser *user.User
|
|
|
|
userUID, userGID uint64
|
|
|
|
)
|
|
|
|
|
|
|
|
currentUser, err := user.Current()
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
userUID, err = strconv.ParseUint(currentUser.Uid, 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
userGID, err = strconv.ParseUint(currentUser.Gid, 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if currentUser.Name == "root" || userUID == 0 {
|
|
|
|
RootMount = true
|
|
|
|
} else {
|
|
|
|
RootMount = false
|
|
|
|
}
|
|
|
|
|
|
|
|
return uint32(userUID), uint32(userGID), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// normalizeObjectName : If file contains \\ in name replace it with ..
|
|
|
|
func NormalizeObjectName(name string) string {
|
|
|
|
return strings.ReplaceAll(name, "\\", "/")
|
|
|
|
}
|
|
|
|
|
|
|
|
// List all mount points which were mounted using blobfuse2
|
|
|
|
func ListMountPoints() ([]string, error) {
|
|
|
|
file, err := os.Open("/etc/mtab")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-09-08 23:09:32 +03:00
|
|
|
|
2022-02-09 19:42:28 +03:00
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
// Read /etc/mtab file line by line
|
|
|
|
var mntList []string
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
|
|
|
|
|
|
|
// If there is any directory mounted using blobfuse2 its of our interest
|
|
|
|
if strings.HasPrefix(line, "blobfuse2") {
|
|
|
|
// Extract the mount path from this line
|
|
|
|
mntPath := strings.Split(line, " ")[1]
|
|
|
|
mntList = append(mntList, mntPath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mntList, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Encrypt given data using the key provided
|
|
|
|
func EncryptData(plainData []byte, key []byte) ([]byte, error) {
|
|
|
|
block, err := aes.NewCipher(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
gcm, err := cipher.NewGCM(block)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
nonce := make([]byte, gcm.NonceSize())
|
|
|
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ciphertext := gcm.Seal(nonce, nonce, plainData, nil)
|
|
|
|
return ciphertext, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decrypt given data using the key provided
|
|
|
|
func DecryptData(cipherData []byte, key []byte) ([]byte, error) {
|
|
|
|
block, err := aes.NewCipher(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
gcm, err := cipher.NewGCM(block)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
nonce := cipherData[:gcm.NonceSize()]
|
|
|
|
ciphertext := cipherData[gcm.NonceSize():]
|
|
|
|
|
|
|
|
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return plaintext, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetCurrentDistro() string {
|
|
|
|
cfg, err := ini.Load("/etc/os-release")
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
distro := cfg.Section("").Key("PRETTY_NAME").String()
|
|
|
|
return distro
|
|
|
|
}
|
2022-02-19 13:26:43 +03:00
|
|
|
|
|
|
|
type BitMap16 uint16
|
|
|
|
|
|
|
|
// IsSet : Check whether the given bit is set or not
|
|
|
|
func (bm BitMap16) IsSet(bit uint16) bool { return (bm & (1 << bit)) != 0 }
|
|
|
|
|
|
|
|
// Set : Set the given bit in bitmap
|
|
|
|
func (bm *BitMap16) Set(bit uint16) { *bm |= (1 << bit) }
|
|
|
|
|
|
|
|
// Clear : Clear the given bit from bitmap
|
|
|
|
func (bm *BitMap16) Clear(bit uint16) { *bm &= ^(1 << bit) }
|
2022-06-24 23:50:01 +03:00
|
|
|
|
|
|
|
type KeyedMutex struct {
|
|
|
|
mutexes sync.Map // Zero value is empty and ready for use
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *KeyedMutex) GetLock(key string) *sync.Mutex {
|
|
|
|
value, _ := m.mutexes.LoadOrStore(key, &sync.Mutex{})
|
|
|
|
mtx := value.(*sync.Mutex)
|
|
|
|
return mtx
|
|
|
|
}
|
2022-08-27 17:17:39 +03:00
|
|
|
|
2022-09-08 23:09:32 +03:00
|
|
|
// check if health monitor is enabled and blofuse stats monitor is not disabled
|
2022-08-27 17:17:39 +03:00
|
|
|
func MonitorBfs() bool {
|
|
|
|
return EnableMonitoring && !BfsDisabled
|
|
|
|
}
|
2022-09-01 17:00:08 +03:00
|
|
|
|
|
|
|
// convert ~ to $HOME in path
|
|
|
|
func ExpandPath(path string) string {
|
|
|
|
if strings.HasPrefix(path, "~/") {
|
|
|
|
homeDir, err := os.UserHomeDir()
|
|
|
|
if err != nil {
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
path = filepath.Join(homeDir, path[2:])
|
|
|
|
}
|
|
|
|
|
2023-02-03 09:25:57 +03:00
|
|
|
return os.ExpandEnv(path)
|
2022-09-01 17:00:08 +03:00
|
|
|
}
|
2023-03-22 07:59:36 +03:00
|
|
|
|
|
|
|
// NotifyMountToParent : Send a signal to parent process about successful mount
|
|
|
|
func NotifyMountToParent() error {
|
|
|
|
if !ForegroundMount {
|
|
|
|
ppid := syscall.Getppid()
|
|
|
|
if ppid > 1 {
|
|
|
|
if err := syscall.Kill(ppid, syscall.SIGUSR2); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("failed to get parent pid, received : %v", ppid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|