From b9fd172341c4b9c00147dd4ecd9b194bd875dc14 Mon Sep 17 00:00:00 2001 From: Masafumi Horimoto Date: Mon, 18 May 2026 19:35:24 +0900 Subject: [PATCH 1/2] Add FUSE3 support and TANDBERG LTO-6 HH drive FUSE3 support (for AlmaLinux9/RHEL9 with FUSE 3.x): - Update FUSE_USE_VERSION from 26 to 30 - Fix filler() calls to use 5 arguments (FUSE3 API) - Remove deprecated fuse_operations members: fgetattr, ftruncate, flag_nullpath_ok - Update fuse_parse_cmdline() to use struct fuse_cmdline_opts (FUSE3 API) - Add fuse_lowlevel.h include in main.c - Remove deprecated FUSE2 options: hard_remove, sync_read, big_writes, use_ino TANDBERG LTO-6 HH support: - Add VENDOR_TANDBERG to vendor enum in tape_drivers.h - Add TANDBERG_VENDOR_ID definition in hp_tape.h - Add TANDBERG LTO-6 HH drive entry in hp_tape.c - Add TANDBERG vendor handling in vendor_compat.c (get_vendor_id, get_supported_devs, init_error_table, init_timeout) - Use LogPage 0x31 for TANDBERG capacity reading in sg_tape.c Tested with: TANDBERG LTO-6 HH (firmware 3319, serial HUJ430176D) AlmaLinux 9.7 (kernel 5.14.0-611.54.6.el9_7.x86_64) FUSE 3.10.2-9.el9 --- src/libltfs/ltfs_fuse_version.h | 2 +- src/ltfs_fuse.c | 11 ++++----- src/main.c | 35 ++++++++++++++++------------- src/tape_drivers/hp_tape.c | 1 + src/tape_drivers/hp_tape.h | 1 + src/tape_drivers/linux/sg/sg_tape.c | 2 +- src/tape_drivers/tape_drivers.h | 1 + src/tape_drivers/vendor_compat.c | 11 +++++++++ 8 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/libltfs/ltfs_fuse_version.h b/src/libltfs/ltfs_fuse_version.h index 0b11382c..4845592e 100644 --- a/src/libltfs/ltfs_fuse_version.h +++ b/src/libltfs/ltfs_fuse_version.h @@ -50,6 +50,6 @@ #ifndef __ltfs_fuse_version_h__ #define __ltfs_fuse_version_h__ -#define FUSE_USE_VERSION 26 +#define FUSE_USE_VERSION 30 #endif /* __ltfs_fuse_version_h__ */ diff --git a/src/ltfs_fuse.c b/src/ltfs_fuse.c index db4d3a5c..b29d31a5 100644 --- a/src/ltfs_fuse.c +++ b/src/ltfs_fuse.c @@ -853,9 +853,9 @@ int _ltfs_fuse_filldir(void *buf, const char *name, void *priv) return ret; } - ret = filler(buf, new_name, NULL, 0); + ret = filler(buf, new_name, NULL, 0, 0); #else - ret = filler(buf, name, NULL, 0); + ret = filler(buf, name, NULL, 0, 0); #endif free(new_name); @@ -875,12 +875,12 @@ int ltfs_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, ltfsmsg(LTFS_DEBUG, 14047D, _dentry_name(path, file->file_info)); - if (filler(buf, ".", NULL, 0)) { + if (filler(buf, ".", NULL, 0, 0)) { /* No buffer space */ ltfsmsg(LTFS_DEBUG, 14026D); return -ENOBUFS; } - if (filler(buf, "..", NULL, 0)) { + if (filler(buf, "..", NULL, 0, 0)) { /* No buffer space */ ltfsmsg(LTFS_DEBUG, 14026D); return -ENOBUFS; @@ -1206,7 +1206,6 @@ struct fuse_operations ltfs_ops = { .init = ltfs_fuse_mount, .destroy = ltfs_fuse_umount, .getattr = ltfs_fuse_getattr, - .fgetattr = ltfs_fuse_fgetattr, .access = ltfs_fuse_access, .statfs = ltfs_fuse_statfs, .open = ltfs_fuse_open, @@ -1218,7 +1217,6 @@ struct fuse_operations ltfs_ops = { .chown = ltfs_fuse_chown, .create = ltfs_fuse_create, .truncate = ltfs_fuse_truncate, - .ftruncate = ltfs_fuse_ftruncate, .unlink = ltfs_fuse_unlink, .rename = ltfs_fuse_rename, .mkdir = ltfs_fuse_mkdir, @@ -1236,6 +1234,5 @@ struct fuse_operations ltfs_ops = { .symlink = ltfs_fuse_symlink, .readlink = ltfs_fuse_readlink, #if FUSE_VERSION >= 28 - .flag_nullpath_ok = 1, #endif }; diff --git a/src/main.c b/src/main.c index 533ec70d..56fb8dbf 100644 --- a/src/main.c +++ b/src/main.c @@ -60,6 +60,7 @@ #include #include "ltfs_fuse.h" +#include #include "libltfs/ltfs.h" #include "ltfs_copyright.h" #include "libltfs/pathname.h" @@ -747,18 +748,18 @@ int main(int argc, char **argv) } /* Unlink objects from the file system instead of having them renamed to .fuse_hidden */ - ret = fuse_opt_add_arg(&args, "-ohard_remove"); +/* FUSE3: removed deprecated option: ret = fuse_opt_add_arg(&args, "-ohard_remove"); */ if (ret < 0) { /* Could not enable FUSE option */ - ltfsmsg(LTFS_ERR, 14001E, "hard_remove", ret); +/* FUSE3: removed deprecated option: ltfsmsg(LTFS_ERR, 14001E, "hard_remove", ret); */ return 1; } /* perform reads synchronously */ - ret = fuse_opt_add_arg(&args, "-osync_read"); +/* FUSE3: removed deprecated option: ret = fuse_opt_add_arg(&args, "-osync_read"); */ if (ret < 0) { /* Could not enable FUSE option */ - ltfsmsg(LTFS_ERR, 14001E, "sync_read", ret); +/* FUSE3: removed deprecated option: ltfsmsg(LTFS_ERR, 14001E, "sync_read", ret); */ return 1; } @@ -788,11 +789,11 @@ int main(int argc, char **argv) #endif #if FUSE_VERSION >= 28 - /* For FUSE 2.8 or higher, automatically enable big_writes */ - ret = fuse_opt_add_arg(&args, "-obig_writes"); +/* FUSE3: removed deprecated option: For FUSE 2.8 or higher, automatically enable big_writes */ +/* FUSE3: removed deprecated option: ret = fuse_opt_add_arg(&args, "-obig_writes"); */ if (ret < 0) { /* Could not enable FUSE option */ - ltfsmsg(LTFS_ERR, 14001E, "big_writes", ret); +/* FUSE3: removed deprecated option: ltfsmsg(LTFS_ERR, 14001E, "big_writes", ret); */ return 1; } #endif @@ -974,10 +975,10 @@ int single_drive_main(struct fuse_args *args, struct ltfs_fuse_data *priv) /* If the local inode space is big enough, have FUSE pass through our UIDs as inode * numbers instead of generating its own. */ if (sizeof(ino_t) >= 8) { - ret = fuse_opt_add_arg(args, "-ouse_ino"); +/* FUSE3: removed deprecated option: ret = fuse_opt_add_arg(args, "-ouse_ino"); */ if (ret < 0) { /* Could not enable FUSE option */ - ltfsmsg(LTFS_ERR, 14001E, "use_ino", ret); +/* FUSE3: removed deprecated option: ltfsmsg(LTFS_ERR, 14001E, "use_ino", ret); */ return 1; } } @@ -1223,12 +1224,16 @@ int single_drive_main(struct fuse_args *args, struct ltfs_fuse_data *priv) for ( i=0; iargc; i++) { fuse_opt_add_arg(&tmpa, args->argv[i]); } - ret = fuse_parse_cmdline( &tmpa, &mountpoint, NULL, NULL); - fuse_opt_free_args(&tmpa); - if (ret < 0 || mountpoint == NULL) { - ltfsmsg(LTFS_ERR, 14094E, ret); - ltfs_volume_free(&priv->data); - return 1; + { + struct fuse_cmdline_opts fuse_opts; + ret = fuse_parse_cmdline(&tmpa, &fuse_opts); + fuse_opt_free_args(&tmpa); + if (ret < 0 || fuse_opts.mountpoint == NULL) { + ltfsmsg(LTFS_ERR, 14094E, ret); + ltfs_volume_free(&priv->data); + return 1; + } + mountpoint = fuse_opts.mountpoint; } priv->data->mountpoint = mountpoint; priv->data->mountpoint_len = strlen(mountpoint); diff --git a/src/tape_drivers/hp_tape.c b/src/tape_drivers/hp_tape.c index f94b4aea..23aa7327 100644 --- a/src/tape_drivers/hp_tape.c +++ b/src/tape_drivers/hp_tape.c @@ -68,6 +68,7 @@ struct supported_device *hp_supported_drives[] = { TAPEDRIVE( HP_VENDOR_ID, "Ultrium 7-SCSI", DRIVE_LTO7, "[Ultrium 7-SCSI]" ), /* HP Ultrium Gen 7 */ TAPEDRIVE( HPE_VENDOR_ID, "Ultrium 8-SCSI", DRIVE_LTO8, "[Ultrium 8-SCSI]" ), /* HPE Ultrium Gen 8 */ TAPEDRIVE( HPE_VENDOR_ID, "Ultrium 9-SCSI", DRIVE_LTO9, "[Ultrium 9-SCSI]" ), /* HPE Ultrium Gen 9 */ + TAPEDRIVE( TANDBERG_VENDOR_ID, "LTO-6 HH ", DRIVE_LTO6_HH, "[LTO-6 HH]" ), /* TANDBERG LTO-6 HH */ /* End of supported_devices */ NULL }; diff --git a/src/tape_drivers/hp_tape.h b/src/tape_drivers/hp_tape.h index 5d60dd9a..cc121bfa 100644 --- a/src/tape_drivers/hp_tape.h +++ b/src/tape_drivers/hp_tape.h @@ -67,6 +67,7 @@ extern "C" { #define HP_VENDOR_ID "HP" #define HPE_VENDOR_ID "HPE" +#define TANDBERG_VENDOR_ID "TANDBERG" extern struct error_table hp_tape_errors[]; diff --git a/src/tape_drivers/linux/sg/sg_tape.c b/src/tape_drivers/linux/sg/sg_tape.c index 19f2beb8..0ff8096f 100644 --- a/src/tape_drivers/linux/sg/sg_tape.c +++ b/src/tape_drivers/linux/sg/sg_tape.c @@ -3031,7 +3031,7 @@ int sg_remaining_capacity(void *device, struct tc_remaining_cap *cap) memset(buffer, 0, LOGSENSEPAGE); - if (IS_LTO(priv->drive_type) && (DRIVE_GEN(priv->drive_type) == 0x05)) { + if (IS_LTO(priv->drive_type) && (DRIVE_GEN(priv->drive_type) == 0x05 || priv->vendor == VENDOR_TANDBERG)) { /* Use LogPage 0x31 */ ret = sg_logsense(device, (uint8_t)LOG_TAPECAPACITY, (uint8_t)0, (void *)buffer, LOGSENSEPAGE); if(ret < 0) diff --git a/src/tape_drivers/tape_drivers.h b/src/tape_drivers/tape_drivers.h index 6eaa3777..4b9edb10 100644 --- a/src/tape_drivers/tape_drivers.h +++ b/src/tape_drivers/tape_drivers.h @@ -179,6 +179,7 @@ enum { VENDOR_IBM, VENDOR_HP, VENDOR_QUANTUM, + VENDOR_TANDBERG, }; enum { diff --git a/src/tape_drivers/vendor_compat.c b/src/tape_drivers/vendor_compat.c index 0df8941f..f997acad 100644 --- a/src/tape_drivers/vendor_compat.c +++ b/src/tape_drivers/vendor_compat.c @@ -289,6 +289,8 @@ int get_vendor_id(char* vendor) return VENDOR_HP; else if (!strncmp(vendor, HPE_VENDOR_ID, strlen(HPE_VENDOR_ID))) return VENDOR_HP; + else if (!strncmp(vendor, TANDBERG_VENDOR_ID, strlen(TANDBERG_VENDOR_ID))) + return VENDOR_TANDBERG; else if (!strncmp(vendor, QUANTUM_VENDOR_ID, strlen(QUANTUM_VENDOR_ID))) return VENDOR_QUANTUM; else @@ -309,6 +311,9 @@ struct supported_device **get_supported_devs(int vendor) case VENDOR_QUANTUM: cur = quantum_supported_drives; break; + case VENDOR_TANDBERG: + cur = hp_supported_drives; + break; } return cur; @@ -412,6 +417,9 @@ void init_error_table(int vendor, case VENDOR_QUANTUM: *vendor_table = quantum_tape_errors; break; + case VENDOR_TANDBERG: + *vendor_table = hp_tape_errors; + break; } } @@ -429,6 +437,9 @@ int init_timeout(int vendor, struct timeout_tape **table, int type) case VENDOR_QUANTUM: ret = quantum_tape_init_timeout(table, type); break; + case VENDOR_TANDBERG: + ret = hp_tape_init_timeout(table, type); + break; } return ret; From 6b75ff0de79fa38f5b6e074a9440eb35709f8e01 Mon Sep 17 00:00:00 2001 From: Masafumi Horimoto Date: Wed, 20 May 2026 17:17:22 +0900 Subject: [PATCH 2/2] Fix FUSE3 compatibility issues - Disable async read via FUSE_CAP_ASYNC_READ in ltfs_fuse_mount() instead of removed -osync_read option (FUSE3) - Comment out -ouse_ino option: verified removed in FUSE3 (causes mount failure: 'fuse: unknown option(s): -o use_ino') - Tested on AlmaLinux 9.7 with FUSE3, LTO-6 Tandberg drive Mount, file read, and data integrity (cmp) all confirmed OK --- src/ltfs_fuse.c | 8 ++++++++ src/main.c | 28 +++++++++++++--------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/ltfs_fuse.c b/src/ltfs_fuse.c index b29d31a5..66f3d3ad 100644 --- a/src/ltfs_fuse.c +++ b/src/ltfs_fuse.c @@ -1068,6 +1068,14 @@ void * ltfs_fuse_mount(struct fuse_conn_info *conn) ltfs_request_trace(FUSE_REQ_ENTER(REQ_MOUNT), 0, 0); + /* FUSE3: Disable async reads to prevent unnecessary tape repositioning. + * The -osync_read option was removed in FUSE3. Async read is now controlled + * via FUSE_CAP_ASYNC_READ in the init() callback (this function). + * See: https://github.com/libfuse/libfuse/blob/master/ChangeLog.rst + */ + if (conn->capable & FUSE_CAP_ASYNC_READ) + conn->want &= ~FUSE_CAP_ASYNC_READ; + if (priv->pid_orig != getpid()) { /* * Reopen device when LTFS was forked in fuse_main(). diff --git a/src/main.c b/src/main.c index 56fb8dbf..01cd3629 100644 --- a/src/main.c +++ b/src/main.c @@ -755,13 +755,10 @@ int main(int argc, char **argv) return 1; } - /* perform reads synchronously */ -/* FUSE3: removed deprecated option: ret = fuse_opt_add_arg(&args, "-osync_read"); */ - if (ret < 0) { - /* Could not enable FUSE option */ -/* FUSE3: removed deprecated option: ltfsmsg(LTFS_ERR, 14001E, "sync_read", ret); */ - return 1; - } + /* FUSE3: The -osync_read option was removed in FUSE3. + * Async read is now disabled via FUSE_CAP_ASYNC_READ in the + * init() callback (see ltfs_fuse_mount() in ltfs_fuse.c). + */ #ifdef __APPLE__ /* Change MacFUSE timeout from 60 secs to 3100 secs (41mins) */ @@ -975,14 +972,15 @@ int single_drive_main(struct fuse_args *args, struct ltfs_fuse_data *priv) /* If the local inode space is big enough, have FUSE pass through our UIDs as inode * numbers instead of generating its own. */ if (sizeof(ino_t) >= 8) { -/* FUSE3: removed deprecated option: ret = fuse_opt_add_arg(args, "-ouse_ino"); */ - if (ret < 0) { - /* Could not enable FUSE option */ -/* FUSE3: removed deprecated option: ltfsmsg(LTFS_ERR, 14001E, "use_ino", ret); */ - return 1; - } - } - + /* FUSE3: use_ino option removed in FUSE3 */ + /* ret = fuse_opt_add_arg(args, "-ouse_ino"); */ + if (ret < 0) { + /* Could not enable FUSE option */ + /* FUSE3: removed deprecated option: ltfsmsg(LTFS_ERR, 14001E, "use_ino", ret); */ + return 1; + } + } + /* Set file system name to "ltfs:devname" in case FUSE doesn't pick it up */ snprintf(fsname, sizeof(fsname), "-ofsname=ltfs:%s", priv->devname); ret = fuse_opt_add_arg(args, fsname);