2025-08-10 01:34:16 +02:00

743 lines
21 KiB
C

#include <filesystem/ProcFsHelper.h>
#include "ProcFs.h"
#include <linux/fs.h>
#include <linux/stat.h>
#define BEEGFS_PROC_DIR_NAME "fs/" BEEGFS_MODULE_NAME_STR
#define BEEGFS_PROC_NAMEBUF_LEN 4096
#define BEEGFS_PROC_ENTRY_CONFIG "config"
#define BEEGFS_PROC_ENTRY_STATUS ".status"
#define BEEGFS_PROC_ENTRY_FSUUID "fs_uuid"
#define BEEGFS_PROC_ENTRY_MGMTNODES "mgmt_nodes"
#define BEEGFS_PROC_ENTRY_METANODES "meta_nodes"
#define BEEGFS_PROC_ENTRY_STORAGENODES "storage_nodes"
#define BEEGFS_PROC_ENTRY_CLIENTINFO "client_info"
#define BEEGFS_PROC_ENTRY_RETRIESENABLED "conn_retries_enabled"
#define BEEGFS_PROC_ENTRY_NETBENCHENABLED "netbench_mode"
#define BEEGFS_PROC_ENTRY_DROPCONNS "drop_conns"
#define BEEGFS_PROC_ENTRY_LOGLEVELS "log_levels"
#define BEEGFS_PROC_ENTRY_METATARGETSTATES "meta_target_state"
#define BEEGFS_PROC_ENTRY_STORAGETARGETSTATES "storage_target_state"
#define BEEGFS_PROC_ENTRY_REMAPCONNFAILURE "remap_connection_failure"
/**
* Initializer for read-only proc file ops
*/
#if defined(KERNEL_HAS_PROC_OPS)
#define BEEGFS_PROC_FOPS_INITIALIZER \
.proc_open = __ProcFs_open, \
.proc_read = seq_read, \
.proc_lseek = seq_lseek, \
.proc_release = single_release
#else
#define BEEGFS_PROC_FOPS_INITIALIZER \
.open = __ProcFs_open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release
#endif
#if defined(KERNEL_HAS_PROC_OPS)
#define PROC_OPS_WRITE_MEMBER proc_write
#else
#define PROC_OPS_WRITE_MEMBER write
#endif
/**
* generic file ops for procfs entries
*/
#if defined(KERNEL_HAS_PROC_OPS)
static const struct proc_ops fhgfs_proc_fops =
#else
static const struct file_operations fhgfs_proc_fops =
#endif
{
BEEGFS_PROC_FOPS_INITIALIZER
};
/*
* for read-only entries: contains name and "show method" for a procfs entry
*/
struct fhgfs_proc_file
{
char name[32]; // filename
int (*show)(struct seq_file *, void *); // the "show method" of this file
};
/**
* all our read-only procfs entries (terminated by empty element)
*/
static const struct fhgfs_proc_file fhgfs_proc_files[] =
{
{ BEEGFS_PROC_ENTRY_CONFIG, &__ProcFs_readV2_config },
{ BEEGFS_PROC_ENTRY_STATUS, &__ProcFs_readV2_status },
{ BEEGFS_PROC_ENTRY_FSUUID, &__ProcFs_readV2_fsUUID },
{ BEEGFS_PROC_ENTRY_MGMTNODES, &__ProcFs_readV2_mgmtNodes },
{ BEEGFS_PROC_ENTRY_METANODES, &__ProcFs_readV2_metaNodes },
{ BEEGFS_PROC_ENTRY_STORAGENODES, &__ProcFs_readV2_storageNodes },
{ BEEGFS_PROC_ENTRY_CLIENTINFO, &__ProcFs_readV2_clientInfo },
{ BEEGFS_PROC_ENTRY_METATARGETSTATES, &__ProcFs_readV2_metaTargetStates },
{ BEEGFS_PROC_ENTRY_STORAGETARGETSTATES, &__ProcFs_readV2_storageTargetStates },
{ "", NULL } // last element must be empty (for loop termination)
};
/*
* for read+write entries: contains name, show method and write method for a procfs entry
*/
struct fhgfs_proc_file_rw
{
char name[32]; // filename
int (*show)(struct seq_file *, void *); // the show method of this file
//ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); // the write method
#if defined(KERNEL_HAS_PROC_OPS)
struct proc_ops proc_fops;
#else
struct file_operations proc_fops;
#endif
};
/**
* all our read+write procfs entries (terminated by empty element).
*
* other than for read-only entries, we need to assign different write methods during proc entry
* registration, so we need indidivual file_operations for each entry here.
*/
static const struct fhgfs_proc_file_rw fhgfs_proc_files_rw[] =
{
{ BEEGFS_PROC_ENTRY_RETRIESENABLED, &__ProcFs_readV2_connRetriesEnabled,
{
BEEGFS_PROC_FOPS_INITIALIZER,
.PROC_OPS_WRITE_MEMBER = &__ProcFs_writeV2_connRetriesEnabled,
},
},
{ BEEGFS_PROC_ENTRY_NETBENCHENABLED, &__ProcFs_readV2_netBenchModeEnabled,
{
BEEGFS_PROC_FOPS_INITIALIZER,
.PROC_OPS_WRITE_MEMBER = &__ProcFs_writeV2_netBenchModeEnabled,
},
},
{ BEEGFS_PROC_ENTRY_DROPCONNS, &__ProcFs_readV2_nothing,
{
BEEGFS_PROC_FOPS_INITIALIZER,
.PROC_OPS_WRITE_MEMBER = &__ProcFs_writeV2_dropConns,
},
},
{ BEEGFS_PROC_ENTRY_LOGLEVELS, &__ProcFs_readV2_logLevels,
{
BEEGFS_PROC_FOPS_INITIALIZER,
.PROC_OPS_WRITE_MEMBER = &__ProcFs_writeV2_logLevels,
},
},
{ BEEGFS_PROC_ENTRY_REMAPCONNFAILURE, &__ProcFs_read_remapConnectionFailure,
{
BEEGFS_PROC_FOPS_INITIALIZER,
.PROC_OPS_WRITE_MEMBER = &__ProcFs_write_remapConnectionFailure,
},
},
// last element must be empty (for loop termination)
{{ 0 }}
};
/**
* Creates the general parent dir. Meant to be called only once at module load.
*/
void ProcFs_createGeneralDir(void)
{
struct proc_dir_entry* procDir;
procDir = proc_mkdir(BEEGFS_PROC_DIR_NAME, NULL);
if(!procDir)
printk_fhgfs(KERN_INFO, "Failed to create proc dir: " BEEGFS_PROC_DIR_NAME "\n");
}
/**
* Removes the general parent dir. Meant to be called only once at module unload.
*/
void ProcFs_removeGeneralDir(void)
{
remove_proc_entry(BEEGFS_PROC_DIR_NAME, NULL);
}
/**
* Creates the dir and entries for a specific mountpoint.
* Note: Uses sessionID to create a unique dir for the mountpoint.
*/
void ProcFs_createEntries(App* app)
{
NodeString sessionID;
Node* localNode = App_getLocalNode(app);
struct proc_dir_entry* procDir;
const struct fhgfs_proc_file* procFile;
const struct fhgfs_proc_file_rw* procFileRW;
// create unique directory for this clientID and store app pointer as "->data"
char* dirNameBuf = vmalloc(BEEGFS_PROC_NAMEBUF_LEN);
Node_copyAlias(localNode, &sessionID);
scnprintf(dirNameBuf, BEEGFS_PROC_NAMEBUF_LEN, BEEGFS_PROC_DIR_NAME "/%s", sessionID.buf);
procDir = __ProcFs_mkDir(dirNameBuf, app);
if(!procDir)
{
printk_fhgfs(KERN_INFO, "Failed to create proc dir: %s\n", dirNameBuf);
goto clean_up;
}
// create entries
/* note: linux-3.10 kills create_proc_(read_)entry and uses proc_create_data instead.
proc_create_data existed for ealier linux version already, so we use it there, too. */
// create read-only proc files
for(procFile = fhgfs_proc_files; procFile->name[0]; procFile++)
{
struct proc_dir_entry* currentProcFsEntry = proc_create_data(
procFile->name, S_IFREG | S_IRUGO, procDir, &fhgfs_proc_fops, procFile->show);
if(!currentProcFsEntry)
{
printk_fhgfs(KERN_INFO, "Failed to create read-only proc entry in %s: %s\n",
dirNameBuf, procFile->name);
goto clean_up;
}
}
// create read+write proc files
for(procFileRW = fhgfs_proc_files_rw; procFileRW->name[0]; procFileRW++)
{
struct proc_dir_entry* currentProcFsEntry = proc_create_data(
procFileRW->name, S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP, procDir, &procFileRW->proc_fops,
procFileRW->show);
if(!currentProcFsEntry)
{
printk_fhgfs(KERN_INFO, "Failed to create read+write proc entry in %s: %s\n",
dirNameBuf, procFileRW->name);
goto clean_up;
}
}
clean_up:
SAFE_VFREE(dirNameBuf);
}
/**
* Removes the dir and entries for a specific mountpoint.
* Note: Uses sessionID for unique dir of the mountpoint.
*/
void ProcFs_removeEntries(App* app)
{
char* dirNameBuf = vmalloc(BEEGFS_PROC_NAMEBUF_LEN);
char* entryNameBuf = vmalloc(BEEGFS_PROC_NAMEBUF_LEN);
Node* localNode = App_getLocalNode(app);
NodeString sessionID;
Node_copyAlias(localNode, &sessionID);
scnprintf(dirNameBuf, BEEGFS_PROC_NAMEBUF_LEN, BEEGFS_PROC_DIR_NAME "/%s", sessionID.buf);
// remove entries
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_CONFIG);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_STATUS);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_FSUUID);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_MGMTNODES);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_METANODES);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_STORAGENODES);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_CLIENTINFO);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_METATARGETSTATES);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_STORAGETARGETSTATES);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_RETRIESENABLED);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_REMAPCONNFAILURE);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_NETBENCHENABLED);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_DROPCONNS);
remove_proc_entry(entryNameBuf, NULL);
scnprintf(entryNameBuf, BEEGFS_PROC_NAMEBUF_LEN, "%s/%s",
dirNameBuf, BEEGFS_PROC_ENTRY_LOGLEVELS);
remove_proc_entry(entryNameBuf, NULL);
// remove unique dir
remove_proc_entry(dirNameBuf, NULL);
SAFE_VFREE(dirNameBuf);
SAFE_VFREE(entryNameBuf);
}
/**
* called when a procfs file is being opened.
*
* this method handles the assignment of the corresponding readV2 method for a certain entry.
*/
int __ProcFs_open(struct inode* inode, struct file* file)
{
int (*show)(struct seq_file *, void *) = __ProcFs_getProcDirEntryDataField(inode);
App* app = __ProcFs_getProcParentDirEntryDataField(inode); // (app is ->data in parent dir)
return single_open(file, show, app);
}
/**
* Does not return anything to the reading process.
* Intended for proc entries that are write-only.
*/
int __ProcFs_readV2_nothing(struct seq_file* file, void* p)
{
return 0;
}
/**
* Does not return anything to the reading process.
* Intended for proc entries that are write-only.
*
* @param data specified at entry creation
*/
int ProcFs_read_nothing(char* buf, char** start, off_t offset, int size, int* eof, void* data)
{
*eof = 1;
return 0;
}
int __ProcFs_readV2_config(struct seq_file* file, void* p)
{
App* app = file->private;
return ProcFsHelper_readV2_config(file, app);
}
/**
* @param data specified at entry creation
*/
int ProcFs_read_config(char* buf, char** start, off_t offset, int size, int* eof, void* data)
{
return ProcFsHelper_read_config(buf, start, offset, size, eof, (App*)data);
}
int __ProcFs_readV2_status(struct seq_file* file, void* v)
{
App* app = file->private;
return ProcFsHelper_readV2_status(file, app);
}
/**
* @param data specified at entry creation
*/
int ProcFs_read_status(char* buf, char** start, off_t offset, int size, int* eof, void* data)
{
return ProcFsHelper_read_status(buf, start, offset, size, eof, (App*)data);
}
int __ProcFs_readV2_mgmtNodes(struct seq_file* file, void* p)
{
App* app = file->private;
NodeStoreEx* nodes = App_getMgmtNodes(app);
return ProcFsHelper_readV2_nodes(file, app, nodes);
}
int __ProcFs_readV2_fsUUID(struct seq_file* file, void* p)
{
App* app = file->private;
return ProcFsHelper_readV2_fsUUID(file, app);
}
/**
* @param data specified at entry creation
*/
int ProcFs_read_mgmtNodes(char* buf, char** start, off_t offset, int size, int* eof, void* data)
{
return ProcFsHelper_read_nodes(buf, start, offset, size, eof,
App_getMgmtNodes( (App*)data) );
}
int __ProcFs_readV2_metaNodes(struct seq_file* file, void* p)
{
App* app = file->private;
NodeStoreEx* nodes = App_getMetaNodes(app);
return ProcFsHelper_readV2_nodes(file, app, nodes);
}
/**
* @param data specified at entry creation
*/
int ProcFs_read_metaNodes(char* buf, char** start, off_t offset, int size, int* eof, void* data)
{
return ProcFsHelper_read_nodes(buf, start, offset, size, eof,
App_getMetaNodes( (App*)data) );
}
int __ProcFs_readV2_storageNodes(struct seq_file* file, void* p)
{
App* app = file->private;
NodeStoreEx* nodes = App_getStorageNodes(app);
return ProcFsHelper_readV2_nodes(file, app, nodes);
}
/**
* @param data specified at entry creation
*/
int ProcFs_read_storageNodes(char* buf, char** start, off_t offset, int size, int* eof, void* data)
{
return ProcFsHelper_read_nodes(buf, start, offset, size, eof,
App_getStorageNodes( (App*)data) );
}
int __ProcFs_readV2_clientInfo(struct seq_file* file, void* p)
{
App* app = file->private;
return ProcFsHelper_readV2_clientInfo(file, app);
}
int ProcFs_read_clientInfo(char* buf, char** start, off_t offset, int size, int* eof,
void* data)
{
return ProcFsHelper_read_clientInfo(buf, start, offset, size, eof, (App*)data);
}
int __ProcFs_readV2_metaTargetStates(struct seq_file* file, void* p)
{
App* app = file->private;
TargetStateStore* metaStates = App_getMetaStateStore(app);
NodeStoreEx* nodes = App_getMetaNodes(app);
return ProcFsHelper_readV2_targetStates(file, app, metaStates, nodes, true);
}
/**
* @param data specified at entry creation
*/
int ProcFs_read_metaTargetStates(char* buf, char** start, off_t offset, int size, int* eof,
void* data)
{
return ProcFsHelper_read_targetStates(buf, start, offset, size, eof, (App*)data,
App_getMetaStateStore( (App*)data), App_getMetaNodes( (App*)data), true);
}
int __ProcFs_readV2_storageTargetStates(struct seq_file* file, void* p)
{
App* app = file->private;
TargetStateStore* targetStates = App_getTargetStateStore(app);
NodeStoreEx* nodes = App_getStorageNodes(app);
return ProcFsHelper_readV2_targetStates(file, app, targetStates, nodes, false);
}
/**
* @param data specified at entry creation
*/
int ProcFs_read_storageTargetStates(char* buf, char** start, off_t offset, int size, int* eof,
void* data)
{
return ProcFsHelper_read_targetStates(buf, start, offset, size, eof, (App*)data,
App_getTargetStateStore( (App*)data), App_getStorageNodes( (App*)data), false);
}
int __ProcFs_readV2_connRetriesEnabled(struct seq_file* file, void* p)
{
App* app = file->private;
return ProcFsHelper_readV2_connRetriesEnabled(file, app);
}
int ProcFs_read_connRetriesEnabled(char* buf, char** start, off_t offset, int size, int* eof,
void* data)
{
return ProcFsHelper_read_connRetriesEnabled(buf, start, offset, size, eof, (App*)data);
}
/**
* @param data specified at entry creation
*/
ssize_t __ProcFs_writeV2_connRetriesEnabled(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct inode* procInode = file_inode(file);
App* app = __ProcFs_getProcParentDirEntryDataField(procInode); // (app is ->data in parent dir)
// check user buffer
if(unlikely(!os_access_ok(VERIFY_READ, buf, count) ) )
return -EFAULT;
return ProcFsHelper_write_connRetriesEnabled(buf, count, app);
}
int ProcFs_write_connRetriesEnabled(struct file* file, const char __user *buf,
unsigned long count, void* data)
{
// check user buffer
if(unlikely(!os_access_ok(VERIFY_READ, buf, count) ) )
return -EFAULT;
return ProcFsHelper_write_connRetriesEnabled(buf, count, (App*)data);
}
int __ProcFs_read_remapConnectionFailure(struct seq_file* file, void* p)
{
App* app = file->private;
return ProcFsHelper_read_remapConnectionFailure(file, app);
}
ssize_t __ProcFs_write_remapConnectionFailure(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct inode* procInode = file_inode(file);
App* app = __ProcFs_getProcParentDirEntryDataField(procInode); // (app is ->data in parent dir)
// check user buffer
if(unlikely(!os_access_ok(VERIFY_READ, buf, count) ) )
return -EFAULT;
return ProcFsHelper_write_remapConnectionFailure(buf, count, app);
}
int __ProcFs_readV2_netBenchModeEnabled(struct seq_file* file, void* p)
{
App* app = file->private;
return ProcFsHelper_readV2_netBenchModeEnabled(file, app);
}
/**
* @param data specified at entry creation
*/
int ProcFs_read_netBenchModeEnabled(char* buf, char** start, off_t offset, int size, int* eof,
void* data)
{
return ProcFsHelper_read_netBenchModeEnabled(buf, start, offset, size, eof, (App*)data);
}
ssize_t __ProcFs_writeV2_netBenchModeEnabled(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct inode* procInode = file_inode(file);
App* app = __ProcFs_getProcParentDirEntryDataField(procInode); // (app is ->data in parent dir)
// check user buffer
if(unlikely(!os_access_ok(VERIFY_READ, buf, count) ) )
return -EFAULT;
return ProcFsHelper_write_netBenchModeEnabled(buf, count, app);
}
/**
* @param data specified at entry creation
*/
int ProcFs_write_netBenchModeEnabled(struct file* file, const char __user *buf,
unsigned long count, void* data)
{
// check user buffer
if(unlikely(!os_access_ok(VERIFY_READ, buf, count) ) )
return -EFAULT;
return ProcFsHelper_write_netBenchModeEnabled(buf, count, (App*)data);
}
ssize_t __ProcFs_writeV2_dropConns(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct inode* procInode = file_inode(file);
App* app = __ProcFs_getProcParentDirEntryDataField(procInode); // (app is ->data in parent dir)
// check user buffer
if(unlikely(!os_access_ok(VERIFY_READ, buf, count) ) )
return -EFAULT;
return ProcFsHelper_write_dropConns(buf, count, app);
}
/**
* @param data specified at entry creation
*/
int ProcFs_write_dropConns(struct file* file, const char __user *buf,
unsigned long count, void* data)
{
// check user buffer
if(unlikely(!os_access_ok(VERIFY_READ, buf, count) ) )
return -EFAULT;
return ProcFsHelper_write_dropConns(buf, count, (App*)data);
}
int __ProcFs_readV2_logLevels(struct seq_file* file, void* p)
{
App* app = file->private;
return ProcFsHelper_readV2_logLevels(file, app);
}
/**
* @param data specified at entry creation
*/
int ProcFs_read_logLevels(char* buf, char** start, off_t offset, int size, int* eof,
void* data)
{
return ProcFsHelper_read_logLevels(buf, start, offset, size, eof, (App*)data);
}
ssize_t __ProcFs_writeV2_logLevels(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct inode* procInode = file_inode(file);
App* app = __ProcFs_getProcParentDirEntryDataField(procInode); // (app is ->data in parent dir)
// check user buffer
if(unlikely(!os_access_ok(VERIFY_READ, buf, count) ) )
return -EFAULT;
return ProcFsHelper_write_logLevels(buf, count, app);
}
/**
* @param data specified at entry creation
*/
int ProcFs_write_logLevels(struct file* file, const char __user *buf,
unsigned long count, void* data)
{
// check user buffer
if(unlikely(!os_access_ok(VERIFY_READ, buf, count) ) )
return -EFAULT;
return ProcFsHelper_write_logLevels(buf, count, (App*)data);
}
/**
* Create a proc dir.
*
* Note: This is actually just a compat method for proc_mkdir_data.
*
* @param data arbitrary private value to be assigned to procDir->data
*/
struct proc_dir_entry* __ProcFs_mkDir(const char* name, void* data)
{
/* newer kernels do no longer export struct proc_dir_entry, so the data field is only
accessible through special kernel methods. */
struct proc_dir_entry* procDir;
#if defined(KERNEL_HAS_PDE_DATA) || defined(KERNEL_HAS_NEW_PDE_DATA)
procDir = proc_mkdir_data(name, 0, NULL, data);
#else
procDir = proc_mkdir(name, NULL);
if(procDir)
procDir->data = data;
#endif // KERNEL_HAS_PDE_DATA
return procDir;
}
/**
* Return the data field of a proc entry.
*
* Note: This is actually just a compat method for PDE_DATA.
*/
void* __ProcFs_getProcDirEntryDataField(const struct inode* procInode)
{
/* newer kernels do no longer export struct proc_dir_entry, so the data field is only
accessible through special kernel methods. */
#ifdef KERNEL_HAS_PDE_DATA
return PDE_DATA(procInode);
#elif defined(KERNEL_HAS_NEW_PDE_DATA)
return pde_data(procInode);
#else
struct proc_dir_entry* procEntry = PDE(procInode);
return procEntry->data; // (app is stored as ->data in parent dir)
#endif // KERNEL_HAS_PDE_DATA
}
/**
* Return the data field from the parent dir of a proc entry.
*
* Note: This is actually just a compat method for proc_get_parent_data.
*/
void* __ProcFs_getProcParentDirEntryDataField(const struct inode* procInode)
{
/* newer kernels do no longer export struct proc_dir_entry, so the ->parent and ->data fields are
only accessible through special kernel methods. */
#if defined(KERNEL_HAS_PDE_DATA) || defined(KERNEL_HAS_NEW_PDE_DATA)
return proc_get_parent_data(procInode);
#else
struct proc_dir_entry* procEntry = PDE(procInode);
return procEntry->parent->data; // (app is stored as ->data in parent dir)
#endif // KERNEL_HAS_PDE_DATA
}