diff --git a/rawfile_servicer.py b/rawfile_servicer.py index 150d793..bb2d921 100644 --- a/rawfile_servicer.py +++ b/rawfile_servicer.py @@ -4,6 +4,7 @@ from google.protobuf.wrappers_pb2 import BoolValue import rawfile_util from csi import csi_pb2, csi_pb2_grpc from orchestrator.k8s import volume_to_node, run_on_node +from rawfile_util import attach_loop from remote import init_rawfile, scrub from util import log_grpc_request, run @@ -48,7 +49,8 @@ class RawFileNodeServicer(csi_pb2_grpc.NodeServicer): def NodePublishVolume(self, request, context): mount_path = request.target_path img_file = rawfile_util.img_file(request.volume_id) - run(f"mount {img_file} {mount_path}") + loop_file = attach_loop(img_file) + run(f"mount {loop_file} {mount_path}") return csi_pb2.NodePublishVolumeResponse() @log_grpc_request diff --git a/rawfile_util.py b/rawfile_util.py index 60d53f8..abed825 100644 --- a/rawfile_util.py +++ b/rawfile_util.py @@ -2,6 +2,7 @@ import json from pathlib import Path from consts import DATA_DIR +from util import run, run_out def img_dir(volume_id): @@ -28,3 +29,27 @@ def patch_metadata(volume_id, obj): new_data = {**old_data, **obj} meta_file(volume_id).write_text(json.dumps(new_data)) return new_data + + +def attached_loops(file: str) -> [str]: + out = run_out(f"losetup -j {file}").stdout.decode() + lines = out.splitlines() + devs = [line.split(":", 1)[0] for line in lines] + return devs + + +def attach_loop(file) -> str: + def next_loop(): + loop_file = run_out(f"losetup -f").stdout.decode().strip() + if not Path(loop_file).exists(): + pfx_len = len("/dev/loop") + loop_dev_id = loop_file[pfx_len:] + run(f"mknod {loop_file} b 7 {loop_dev_id}") + return loop_file + + while True: + devs = attached_loops(file) + if len(devs) > 0: + return devs[0] + next_loop() + run(f"losetup -f {file}") diff --git a/util.py b/util.py index 1274557..66bf7b6 100644 --- a/util.py +++ b/util.py @@ -39,6 +39,11 @@ def run(cmd): return subprocess.run(cmd, shell=True, check=True) +def run_out(cmd: str): + p = subprocess.run(cmd, shell=True, capture_output=True) + return p + + class remote_fn(object): def __init__(self, fn): self.fn = fn