azure-storage-fuse/common/util.go

285 строки
7.2 KiB
Go
Исходник Обычный вид История

2022-02-09 19:42:28 +03:00
/*
_____ _____ _____ ____ ______ _____ ------
| | | | | | | | | | | | |
| | | | | | | | | | | | |
| --- | | | | |-----| |---- | | |-----| |----- ------
| | | | | | | | | | | | |
| ____| |_____ | ____| | ____| | |_____| _____| |_____ |_____
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
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"
"path/filepath"
2022-02-09 19:42:28 +03:00
"strconv"
"strings"
"sync"
"syscall"
2022-02-09 19:42:28 +03:00
"gopkg.in/ini.v1"
)
var RootMount bool
var ForegroundMount bool
2022-02-09 19:42:28 +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 {
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 {
// 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
}
// 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
}
// 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
}
// 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-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
}
Perf and Memory Optimizations (#704) * First commit of blobfuse v2 code * Adding hadle optimization for v2 * Making direct read a configurable option, disabled by default * Rearrange structures to reduce padding and save memory * Correcting attr-cache UT for flag changes * Merging file-cache-test * Make flag check a function * Adding comments to handle methods * Adding more fuse options for optimization * Adding more fuse options for optimization * Removing exectime from red flows * Correcting profiling code to work for both cpu and memory * Cleanup old pprof reports * Adding error log for policy local file removal case and adding 10mil max file limit in attr-cache * Correcting file-cache UT for notInCache cases * Correcting libfuse UT to get handle object out of open return parameters * Addressing review comments * Removing 7.5 old curl from nightly as it does not make sense for v2 to test different curl versions * Tuning fuse parameters for better perf * Fuse parameter tuning * Removing some fuse options as mount is failing on some platforms * Exit pipeline stage if mount failed * Populate fuse connection flag based on kernel capabilities * Log kernel fuse capabilities and the one chosen by blobfuse on given platform * Fail pipeline if blobfuse2 binary is not found in df output * Wait for some time for mount to go through before validating * Correcting validation of mount point * Correcting validation of mount point * Use fuse2 on Deb-10 as with fuse3 we are not able to mount on that distro * Update mount validation for huge listing * Image signing to happen only when release option is chosen Co-authored-by: Gauri Prasad <gapra@microsoft.com> Co-authored-by: Ubuntu <vibhansa@vibhansa-perftest.l1vcg1e4u55e3gwz1dsfkxy2if.tx.internal.cloudapp.net>
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) }
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
}
// check if health monitor is enabled and blofuse stats monitor is not disabled
func MonitorBfs() bool {
return EnableMonitoring && !BfsDisabled
}
// 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:])
}
return os.ExpandEnv(path)
}
// 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
}