Skip to content

Commit

Permalink
feat(host): remove loop device by ioctl (#21676)
Browse files Browse the repository at this point in the history
  • Loading branch information
zexi authored Dec 5, 2024
1 parent 3226e14 commit 25a17bf
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 5 deletions.
1 change: 1 addition & 0 deletions pkg/hostman/container/storage/local_raw/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package local_raw // import "yunion.io/x/onecloud/pkg/hostman/container/storage/local_raw"
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package storage
package local_raw

import (
"fmt"
Expand All @@ -21,12 +21,14 @@ import (
"yunion.io/x/log"
"yunion.io/x/pkg/errors"

"yunion.io/x/onecloud/pkg/hostman/container/storage"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/losetup"
losetupioctl "yunion.io/x/onecloud/pkg/util/losetup/ioctl"
)

func init() {
RegisterDriver(newLocalRaw())
storage.RegisterDriver(newLocalRaw())
}

type localRaw struct{}
Expand All @@ -35,8 +37,8 @@ func newLocalRaw() *localRaw {
return &localRaw{}
}

func (l localRaw) GetType() StorageType {
return STORAGE_TYPE_LOCAL_RAW
func (l localRaw) GetType() storage.StorageType {
return storage.STORAGE_TYPE_LOCAL_RAW
}

func (l localRaw) CheckConnect(diskPath string) (string, bool, error) {
Expand Down Expand Up @@ -76,7 +78,7 @@ func (l localRaw) DisconnectDisk(diskPath string, mountPoint string) error {
for _, dev := range devs.LoopDevs {
if dev.BackFile == diskPath {
log.Infof("Start detach loop device %s", dev.Name)
if err := losetup.DetachDevice(dev.Name); err != nil {
if err := losetupioctl.DetachAndRemoveDevice(dev.Name); err != nil {
if strings.Contains(err.Error(), "No such device or address") {
return nil
}
Expand Down
1 change: 1 addition & 0 deletions pkg/hostman/hostinfo/hostinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/cloudcommon/tsdb"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
_ "yunion.io/x/onecloud/pkg/hostman/container/storage/local_raw"
"yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
"yunion.io/x/onecloud/pkg/hostman/hostinfo/hostbridge"
"yunion.io/x/onecloud/pkg/hostman/hostinfo/hostconsts"
Expand Down
1 change: 1 addition & 0 deletions pkg/util/losetup/ioctl/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package ioctl // import "yunion.io/x/onecloud/pkg/util/losetup/ioctl"
137 changes: 137 additions & 0 deletions pkg/util/losetup/ioctl/remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ioctl

/*
#include <linux/loop.h>
*/
import "C"
import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"

"yunion.io/x/log"
"yunion.io/x/pkg/errors"

fileutils "yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/losetup"
"yunion.io/x/onecloud/pkg/util/mountutils"
)

const (
LOOP_CTL_PATH = "/dev/loop-control"

LOOP_CTL_REMOVE = C.LOOP_CTL_REMOVE
)

func RemoveDevice(devNumber int) error {
fd, err := os.OpenFile(LOOP_CTL_PATH, os.O_RDWR, 0644)
if err != nil {
return errors.Wrapf(err, "Open %s", LOOP_CTL_PATH)
}
defer fd.Close()

retNum, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), LOOP_CTL_REMOVE, uintptr(devNumber))
if err != nil {
return errors.Wrapf(err, "IOCT REMOVE %d", devNumber)
}
log.Infof("using ioctl remove loop %v", retNum)
return nil
}

func DetachAndRemoveDevice(devPath string) error {
getDev := func() (*losetup.Device, error) {
devs, err := losetup.ListDevices()
if err != nil {
return nil, errors.Wrapf(err, "list devices")
}
dev := devs.GetDeviceByName(devPath)
return dev, nil
}
dev, err := getDev()
if err != nil {
return err
}
if dev == nil {
return nil
}
// check mountpoints
partions, err := losetup.GetDevicePartions(devPath)
if err != nil {
return errors.Wrapf(err, "get device %s partions", devPath)
}
for _, part := range partions {
checkMntCmd, _ := losetup.NewCommand("sh", "-c", fmt.Sprintf("mount | grep %s | awk '{print $3}'", part)).Run()
if out := checkMntCmd.Output(); out != "" {
mntPoints := strings.Split(out, "\n")
for _, mntPoint := range mntPoints {
if mntPoint != "" {
if err := mountutils.Unmount(mntPoint); err != nil {
return errors.Wrapf(err, "umount %s of dev: %s, part: %s", mntPoint, dev.Name, part)
}
}
}
}
}

_, err = losetup.NewLosetupCommand().AddArgs("-d", dev.Name).Run()
if err != nil {
return errors.Wrapf(err, "detach device")
}

removeDev := func() error {
log.Infof("Start removing device %s", dev.Name)
if fileutils.Exists(dev.Name) {
devNumStr := strings.TrimPrefix(filepath.Base(dev.Name), "loop")
devNum, err := strconv.Atoi(devNumStr)
if err != nil {
return errors.Wrapf(err, "remove device: invalid device number: %s", devNumStr)
}
if err := RemoveDevice(devNum); err != nil {
return errors.Wrapf(err, "remove device: %s", devNumStr)
}
} else {
log.Infof("Loop device %s removed", dev.Name)
}
return nil
}

errs := []error{}
for i := 1; i <= 3; i++ {
if err := removeDev(); err != nil {
err = errors.Wrapf(err, "remove loop device, %d times", i)
log.Warningf("remove device %s: %v", devPath, err)
errs = append(errs, err)
} else {
return nil
}
time.Sleep(time.Duration(i) * time.Second)
}
return errors.NewAggregate(errs)

// recheck
/*dev, err = getDev()
if err != nil {
return errors.Wrapf(err, "get device by %s for rechecking", devPath)
}
if dev != nil {
return errors.Errorf("device %s still exists, %s", devPath, jsonutils.Marshal(dev))
}*/
}
1 change: 1 addition & 0 deletions pkg/util/losetup/losetup.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ func DetachDevice(devPath string) error {
if err != nil {
return errors.Wrapf(err, "detach device")
}

// recheck
/*dev, err = getDev()
if err != nil {
Expand Down

0 comments on commit 25a17bf

Please sign in to comment.