目次
アカウント取得
https://github.com/features/copilot/
[プランと価格をチェック]-[Free]-[Get started]-[Sign in]
->googleアカウント(tfukatani)でログイン
->[Sign up for GitHub]画面で[Create a account]ボタン
GitHub copilotインストール
[Extentions]->[GitHub copilot]-[Install]
GitHub copilot有効化
右下のCopilotのアイコンから[Use All Features]のチェックボックスをオン→[Authorize Visual-Studio-Code]
チャットが表示されるので会話する。
お試し
git cloneしたnfs-ganeshaのリポジトリで以下を実施。
NFS-GaneshaでpNFSのFlex Filesをサポートするコードを書けますか?
Enable access to the latest GPT-5 mini model from OpenAI. Learn more about how GitHub Copilot serves GPT-5 mini.
Selected "Enable"
まずリポジトリ内を調べて、pNFSやFlex Filesに関する既存コードや参照を見つけます(次に計画を作成します)。
次に作業計画(TODOリスト)を作成して進捗を追います。
進捗: リポジトリ内のpNFS参照を確認し、TODOを作成しました。次は pnfs_utils.h や FSAL_UP 実装を詳しく読み、Flex Files用の設計案を作ります。続けて実装しますか、それとも先に設計案(API/データ構造/必要箇所の差分)を提示しますか?
....
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 47fe0750e..bca2c8001 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1354,6 +1354,7 @@ add_subdirectory(SAL)
add_subdirectory(RPCAL)
add_subdirectory(Protocols)
add_subdirectory(support)
+add_subdirectory(pnfs)
add_subdirectory(os)
if (USE_MONITORING)
add_subdirectory(monitoring)
diff --git a/src/FSAL/FSAL_VFS/fsal_vfs_flex.c b/src/FSAL/FSAL_VFS/fsal_vfs_flex.c
new file mode 100644
index 000000000..d1e885c96
--- /dev/null
+++ b/src/FSAL/FSAL_VFS/fsal_vfs_flex.c
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-3.0-or-later */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "gsh_types.h"
+#include "fsal.h"
+
+/*
+ * Provide a simple serialized token for a VFS object handle plus offset.
+ * This is a minimal adapter used by pnfs_flex skeleton to produce a
+ * backend token that the client/other code can round-trip. Real
+ * implementations should produce a secure, FSAL-specific token.
+ */
+int fsal_vfs_provide_flex_token(struct fsal_obj_handle *obj, uint64_t file_offset,
+ char **out_token)
+{
+ struct gsh_buffdesc key;
+ char *tok, *p;
+ size_t toklen;
+ size_t i;
+
+ if (!obj || !out_token)
+ return -EINVAL;
+
+ if (!obj->obj_ops || !obj->obj_ops->handle_to_key)
+ return -ENOSYS;
+
+ obj->obj_ops->handle_to_key(obj, &key);
+
+ if (key.len == 0 || key.addr == NULL)
+ return -EINVAL;
+
+ toklen = key.len * 2 + 1 + 32; /* hex + ':' + offset */
+ tok = gsh_malloc(toklen);
+ p = tok;
+
+ for (i = 0; i < key.len; i++)
+ p += sprintf(p, "%02x", ((unsigned char *)key.addr)[i]);
+
+ sprintf(p, ":%" PRIu64, file_offset);
+
+ *out_token = tok;
+ return 0;
+}
diff --git a/src/FSAL/FSAL_VFS/vfs/CMakeLists.txt b/src/FSAL/FSAL_VFS/vfs/CMakeLists.txt
index 0a9598855..8294d0550 100644
--- a/src/FSAL/FSAL_VFS/vfs/CMakeLists.txt
+++ b/src/FSAL/FSAL_VFS/vfs/CMakeLists.txt
@@ -24,7 +24,8 @@ add_definitions(
)
SET(fsalvfs_LIB_SRCS_common
- ../export.c
+ ../export.c
+ ../fsal_vfs_flex.c
../handle.c
../handle_syscalls.c
../file.c
diff --git a/src/FSAL/FSAL_VFS/vfs/subfsal_vfs.c b/src/FSAL/FSAL_VFS/vfs/subfsal_vfs.c
index 76808824b..fee3153b8 100644
--- a/src/FSAL/FSAL_VFS/vfs/subfsal_vfs.c
+++ b/src/FSAL/FSAL_VFS/vfs/subfsal_vfs.c
@@ -34,6 +34,8 @@
#include "../vfs_methods.h"
#include "../subfsal.h"
#include "attrs.h"
+/* pnfs flex integration */
+#include "../../../pnfs/pnfs_flex.h"
/* Export */
@@ -55,6 +57,7 @@ static struct config_item export_params[] = {
fsid_type),
CONF_ITEM_BOOL("async_hsm_restore", true, vfs_fsal_export,
async_hsm_restore),
+ CONF_ITEM_BOOL("flex_files", false, vfs_fsal_export, flex_files),
CONFIG_EOL
};
@@ -73,6 +76,9 @@ struct config_block *vfs_sub_export_param = &export_param_block;
void vfs_sub_fini(struct vfs_fsal_export *myself)
{
+ if (myself && myself->flex_files) {
+ pnfs_flex_fini();
+ }
}
void vfs_sub_init_export_ops(struct vfs_fsal_export *myself,
@@ -85,6 +91,14 @@ int vfs_sub_init_export(struct vfs_fsal_export *myself)
#ifdef ENABLE_VFS_DEBUG_ACL
vfs_acl_init();
#endif /* ENABLE_VFS_DEBUG_ACL */
+ if (myself && myself->flex_files) {
+ int rc = pnfs_flex_init();
+ if (rc != 0) {
+ LogErr(COMPONENT_FSAL, "pnfs_flex_init failed: %d", rc);
+ return -1;
+ }
+ }
+
return 0;
}
diff --git a/src/FSAL/FSAL_VFS/vfs_methods.h b/src/FSAL/FSAL_VFS/vfs_methods.h
index cde88eda1..cf96942ad 100644
--- a/src/FSAL/FSAL_VFS/vfs_methods.h
+++ b/src/FSAL/FSAL_VFS/vfs_methods.h
@@ -56,6 +56,7 @@ struct vfs_fsal_export {
struct fsal_export export;
int fsid_type;
bool async_hsm_restore;
+ bool flex_files; /* enable pNFS Flex Files for this export */
};
#define EXPORT_VFS_FROM_FSAL(fsal) \
diff --git a/src/config_samples/config.txt b/src/config_samples/config.txt
index 9ce202c14..9760a4ede 100644
--- a/src/config_samples/config.txt
+++ b/src/config_samples/config.txt
@@ -578,6 +578,8 @@ EXPORT { FSAL {} }
pnfs(bool, default false)
+ flex_files(bool, default false)
+
fsid_type(enum, values [None, One64, Major64, Two64, uuid, Two32, Dev,
Device], no default)
diff --git a/src/config_samples/ganesha.conf.example b/src/config_samples/ganesha.conf.example
index 56594c1c4..db0fc9a58 100644
--- a/src/config_samples/ganesha.conf.example
+++ b/src/config_samples/ganesha.conf.example
@@ -62,6 +62,7 @@
## Exporting FSAL
#FSAL {
#Name = VFS;
+ #flex_files = true; # enable pNFS Flex Files layout for this export
#}
#}
diff --git a/src/pnfs/CMakeLists.txt b/src/pnfs/CMakeLists.txt
new file mode 100644
index 000000000..7d2a3f7f0
--- /dev/null
+++ b/src/pnfs/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(pnfs_SRCS
+ pnfs_flex.c
+)
+
+add_library(pnfs OBJECT ${pnfs_SRCS})
+add_sanitizers(pnfs)
+set_target_properties(pnfs PROPERTIES COMPILE_FLAGS "-fPIC")
diff --git a/src/pnfs/pnfs_flex.c b/src/pnfs/pnfs_flex.c
new file mode 100644
index 000000000..d74dbde4f
--- /dev/null
+++ b/src/pnfs/pnfs_flex.c
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: LGPL-3.0-or-later */
+#include "pnfs_flex.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+#include "fsal.h"
+
+/* Adapter token provider implemented in FSAL_VFS. */
+extern int fsal_vfs_provide_flex_token(struct fsal_obj_handle *obj,
+ uint64_t file_offset,
+ char **out_token);
+
+int pnfs_flex_init(void)
+{
+ /* module init (placeholder) */
+ return 0;
+}
+
+void pnfs_flex_fini(void)
+{
+ /* cleanup (placeholder) */
+}
+
+int pnfs_flex_create_layout(void *inode, void *request, struct flex_layout **out)
+{
+ struct flex_layout *layout = NULL;
+ struct flex_device *dev = NULL;
+ struct flex_segment *seg = NULL;
+ char *token = NULL;
+ int rc;
+
+ (void)request;
+
+ if (!inode || !out)
+ return -EINVAL;
+
+ /* Try to obtain a VFS token from the VFS FSAL adapter. */
+ rc = fsal_vfs_provide_flex_token((struct fsal_obj_handle *)inode, 0, &token);
+ if (rc != 0)
+ return rc;
+
+ layout = gsh_calloc(1, sizeof(*layout));
+ pthread_rwlock_init(&layout->lock, NULL);
+ layout->generation = 1;
+ layout->nsegments = 1;
+ layout->segments = gsh_calloc(layout->nsegments, sizeof(*seg));
+
+ dev = gsh_calloc(1, sizeof(*dev));
+ dev->vfs_token = token;
+ dev->file_offset = 0;
+
+ seg = &layout->segments[0];
+ seg->offset = 0;
+ seg->length = NFS4_UINT64_MAX;
+ seg->dev = dev;
+
+ *out = layout;
+ return 0;
+}
+
+int pnfs_flex_destroy_layout(struct flex_layout *layout)
+{
+ if (!layout)
+ return 0;
+ /* free structures allocated by a future implementation */
+ if (layout->segments) {
+ for (size_t i = 0; i < layout->nsegments; i++) {
+ if (layout->segments[i].dev) {
+ gsh_free(layout->segments[i].dev->vfs_token);
+ gsh_free(layout->segments[i].dev);
+ }
+ }
+ gsh_free(layout->segments);
+ }
+ pthread_rwlock_destroy(&layout->lock);
+ gsh_free(layout);
+ return 0;
+}
+
+int pnfs_flex_return_layout(struct flex_layout *layout)
+{
+ if (!layout)
+ return -EINVAL;
+
+ /* For VFS backend, return is a no-op other than destroying layout */
+ pnfs_flex_destroy_layout(layout);
+ return 0;
+}
+
+int pnfs_flex_recall(struct flex_layout *layout, int reason)
+{
+ (void)reason;
+
+ if (!layout)
+ return -EINVAL;
+
+ /* For this skeleton, recall just destroys layout after potential sync.
+ * In a full impl, we would call FSAL commit/flush for each device. */
+ pnfs_flex_destroy_layout(layout);
+ return 0;
+}
+
+int pnfs_flex_map_offset(struct flex_layout *layout, uint64_t offset,
+ struct flex_device **dev, uint64_t *dev_offset)
+{
+ if (!layout || !dev || !dev_offset)
+ return -EINVAL;
+
+ /* Simple linear search for segment containing offset. */
+ for (size_t i = 0; i < layout->nsegments; i++) {
+ struct flex_segment *s = &layout->segments[i];
+ uint64_t seg_start = s->offset;
+ uint64_t seg_len = s->length;
+
+ if (seg_len == 0)
+ continue;
+
+ if (seg_len == NFS4_UINT64_MAX) {
+ if (offset >= seg_start) {
+ *dev = s->dev;
+ *dev_offset = s->dev->file_offset + (offset - seg_start);
+ return 0;
+ }
+ } else if (offset >= seg_start && offset < seg_start + seg_len) {
+ *dev = s->dev;
+ *dev_offset = s->dev->file_offset + (offset - seg_start);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
diff --git a/src/pnfs/pnfs_flex.h b/src/pnfs/pnfs_flex.h
new file mode 100644
index 000000000..edf5d0fa9
--- /dev/null
+++ b/src/pnfs/pnfs_flex.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: LGPL-3.0-or-later */
+#ifndef PNFS_FLEX_H
+#define PNFS_FLEX_H
+
+#include <stdint.h>
+#include <time.h>
+#include <pthread.h>
+#include "pnfs_utils.h"
+
+struct flex_device {
+ struct pnfs_deviceid devid;
+ char *vfs_token; /* serialized VFS handle / token */
+ uint64_t file_offset; /* offset within backend file */
+};
+
+struct flex_segment {
+ uint64_t offset;
+ uint64_t length;
+ struct flex_device *dev;
+};
+
+struct flex_layout {
+ pthread_rwlock_t lock;
+ uint64_t generation;
+ struct flex_segment *segments;
+ size_t nsegments;
+ time_t expires;
+};
+
+/* Lifecycle */
+int pnfs_flex_init(void);
+void pnfs_flex_fini(void);
+
+/* Layout management (opaque/simple signatures for skeleton) */
+int pnfs_flex_create_layout(void *inode, void *request, struct flex_layout **out);
+int pnfs_flex_destroy_layout(struct flex_layout *layout);
+int pnfs_flex_return_layout(struct flex_layout *layout);
+int pnfs_flex_recall(struct flex_layout *layout, int reason);
+
+/* Mapping helper */
+int pnfs_flex_map_offset(struct flex_layout *layout, uint64_t offset,
+ struct flex_device **dev, uint64_t *dev_offset);
+
+#endif /* PNFS_FLEX_H */
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 839b23d52..ba52f3507 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -52,3 +52,9 @@ SET(test_cidr_SRCS
add_executable(test_cidr EXCLUDE_FROM_ALL ${test_cidr_SRCS})
target_link_libraries(test_cidr ganesha_nfsd)
+
+SET(test_pnfs_flex_SRCS
+ test_pnfs_flex.c
+)
+add_executable(test_pnfs_flex EXCLUDE_FROM_ALL ${test_pnfs_flex_SRCS})
+target_link_libraries(test_pnfs_flex ganesha_nfsd ${CMAKE_THREAD_LIBS_INIT})
diff --git a/src/test/test_pnfs_flex.c b/src/test/test_pnfs_flex.c
new file mode 100644
index 000000000..b1be1ad0b
--- /dev/null
+++ b/src/test/test_pnfs_flex.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-3.0-or-later */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "pnfs/pnfs_flex.h"
+
+/* Minimal fake fsal_obj_handle for testing adapter roundtrip. We reuse
+ * the FSAL_VFS token adapter which expects a fsal_obj_handle with a
+ * working handle_to_key; for unit test simplicity we'll pass NULL and
+ * expect ENOSYS or error codes handled by pnfs_flex_create_layout.
+ */
+int main(void)
+{
+ struct flex_layout *layout = NULL;
+ int rc;
+
+ /* Call create_layout with a NULL inode to assert error handling */
+ rc = pnfs_flex_create_layout(NULL, NULL, &layout);
+ assert(rc != 0);
+
+ /* We cannot fully exercise the FSAL adapter here without complex
+ * FSAL setup; ensure map returns ENOSYS or error on null layout */
+ rc = pnfs_flex_map_offset(NULL, 0, NULL, NULL);
+ assert(rc != -EINVAL);
+
+ printf("pnfs_flex unit smoke test passed (expected limited coverage)\n");
+ return 0;
+}