How to intercept storage size query commands - c

I am developing a file system using libfuse and need to find a way to intercept calls for storage size querying, i.e. du and df.
But I have been unable to identify how to this and unable to find an example that showcase this.
Looking at the debug output from my filesystem, doesn't give much information either, as I am unsure which call I should intercept.

For the df you can implement the statfs() operation, something like this:
static int do_statfs(const char *path, struct statvfs *st)
{
int rv;
rv = statvfs("/", st);
st->f_bavail = 15717083;
return rv;
}
In the example above, just to simplify, I query the root filesystem, than modify blocks available, but you can (and should) feel the complete statvfs structure with the information regarding your filesystem.
Now for the du, the man says: "Summarize disk usage of each FILE, recursively for directories", so every file will be queried. For this you need to implement the stat() operation.
static int do_getattr(const char *path, struct stat *st)
{
st->st_uid = getuid();
st->st_gid = getgid();
st->st_atime = time(NULL);
st->st_mtime = time(NULL);
// fill the rest of the stat structure
return 0;
}
Once those are implemented you have to add them do fuse_operations structure:
static struct fuse_operations operations = {
.open = do_open,
.getattr = do_getattr,
.readdir = do_readdir,
.read = do_read,
.statfs = do_statfs,
.release = do_release,
};
and pass it as a parameter to the fuse_main()
int main(int argc, char *argv[])
{
return fuse_main(argc, argv, &operations, NULL);
}

Related

What is the intended usage of DEVICE_INT_ATTR?

I would like to expose some settings of my device via sysfs. If I understand it right, a driver can have multiple devices, so there should be one instance of the settings variable per device. This should be easy enough using DEVICE_ATTR macro.
Checking the sources I noticed there is also DEVICE_INT_ATTR and other with different type. I wonder what is the intended usage, as they use device_show_int functions that get pointer to device, but don't actually use it:
ssize_t device_store_int(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct dev_ext_attribute *ea = to_ext_attr(attr);
int ret;
long new;
ret = kstrtol(buf, 0, &new);
if (ret)
return ret;
if (new > INT_MAX || new < INT_MIN)
return -EINVAL;
*(int *)(ea->var) = new;
/* Always return full write size even if we didn't consume all */
return size;
}
EXPORT_SYMBOL_GPL(device_store_int);
I searched kernel sources for those macros, and it seems that they work with a global variable. For example DEVICE_INT_ATTR is used in drivers/base/core.c for mca_cfg.tolerant:
static DEVICE_INT_ATTR(tolerant, 0644, mca_cfg.tolerant);
but the mca_cfg varaible is actually global, not tied to a device:
struct mca_config mca_cfg __read_mostly = {
.bootlog = -1,
/* ... */
.tolerant = 1,
.monarch_timeout = -1
};
which makes it look like a driver (not device) attribute.
I also checked commit that adds these macros but it did not help me much.
You correctly take that DEVICE_INT_ATTR and other macros from that family are for "global" attributes, which store and show methods doesn't use dev parameter.
If you want to define attribute, which can be bound to several devices, then you could write your own store and show methods which gets information about the value from dev.
E.g. by having device
struct my_device
{
struct device base;
int repetition;
};
you could expose its repetition field in the attribute using following show method:
// Shows value of 'repetition' field of my_device.
static ssize_t repetition_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// Obtain pointer to the real device structure.
struct my_device* my_dev = container_of(dev, struct my_device, base);
return sprintf(buf, "%d\n", my_dev->repetition);
}
Structure of such attribute could be initialized using __ATTR macro:
static struct device_attribute repetition_attr =
__ATTR(repetition, S_IRUGO, repetition_show, NULL);
Making "generic" attributes
Assume your device struct contains many int fields, which you want to expose via attributes:
struct my_device
{
struct device base;
int repetition;
int counter;
int value;
};
In that case you could generalize attribute definition, so you don't need to create many show (and store) functions.
E.g. you could store offset of the exposed field in your attribute structure:
struct device_bounded_attr
{
struct device_attribute base_attr;
size_t field_offset;
};
// Initializer for struct device_bounded_attr
//
// - real_device_type - type of the actual device structure
// - device_member - member of type 'struct device' in the actual device structure
// - field_member - member in actual device structure which you want to expose as attribute.
#define BOUNDED_ATTR(name, mode, show, store, real_device_type, device_member, field_member) { \
.base_attr = __ATTR(name, mode, show, store), \
.field_offset = offsetof(real_device_type, field_member) - offsetof(real_device_type, device_member)
}
Using this field, you could rewrite show method as follows:
// Shows value of integer field, stored in device.
static ssize_t bounded_attr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// Obtain pointer to the real attribute structure.
struct device_bounded_attr* bounded_attr = container_of(attr, struct device_bounded_attr, base_attr);
// Having offset of the field, calculate pointer to it
int field_ptr* = (int*)(((char*)dev) + bounded_attr->field_offset);
return sprintf(buf, "%d\n", *field_ptr);
}
So attributes can be declared as follows:
static struct device_bounded_attr repetition_attr =
BOUNDED_ATTR(repetition, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, repetition);
static struct device_bounded_attr counter_attr =
BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, counter);
static struct device_bounded_attr value_attr =
BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, value);

Tcl pathInFilesystemProc get current filesystem

When creating a vfs using the tcl api how do you get the current filesystem in Tcl_Filesystem.pathInFilesystemProc
My code looks something like this:
typedef struct {
FILE* dbFile;
/*...*/
} FSBackend;
void createFS(const char* dbFile)
{
FSBackend* fsback = (FSBackend*)malloc(sizeof(FSBackend));
initDb(fsback,dbFile);
Tcl_Filesystem tfs;
tfs.typeName="Db Fs";
tfs.structureLength = sizeof(Tcl_Filesystem);
tfs.version = TCL_FILESYSTEM_VERSION_1;
tfs.pathInFilesystemProc = inFsProc;
/*...*/
Tcl_FSRegister((void*),tfs);
}
int inFsProc(Tcl_Obj* pathPtr,ClientData* cd)
{
/* How do I get my FSBackend struct here */
FSBackend* bk = /* ? */
int len;
const char* searchPath = Tcl_GetStringFromObj(pathPtr,&len);
char* foundPath = findFileInDb(searchPath,bk);
if (foundPath == 0) {
return -1;
}
cd = buildInternalRep(foundPath,bk);
return TCL_OK;
}
/**
...
*/
int main()
{
createFS("db1.db");
createFS("db2.db");
}
How do I, in inFsProc get back the struct I passed into Tcl_FSRegister?
The Tcl_FSData function says it can get it but I would then need to get a Tcl_Filesystem pointer
That's a weird one. The clientData handle there is not used to specify a mount point, but rather a separate capability of the filesystem type. Tcl's internal use of Tcl_FSRegister doesn't use it at all. The code which is as close as anything to a canonical use of it is the tclvfs package.
https://github.com/tcl-mirror/tclvfs/blob/master/generic/vfs.c#L385 shows us the use:
static void
Vfs_RegisterWithInterp(interp)
Tcl_Interp *interp;
{
ClientData vfsAlreadyRegistered;
/*
* We need to know if the interpreter is deleted, so we can
* remove all interp-specific mounts.
*/
Tcl_SetAssocData(interp, "vfs::inUse", (Tcl_InterpDeleteProc*)
Vfs_UnregisterWithInterp, (ClientData) 1);
/*
* Perform one-off registering of our filesystem if that
* has not happened before.
*/
vfsAlreadyRegistered = Tcl_FSData(&vfsFilesystem);
if (vfsAlreadyRegistered == NULL) {
Tcl_FSRegister((ClientData)1, &vfsFilesystem);
Tcl_CreateExitHandler(VfsExitProc, (ClientData)NULL);
Tcl_CreateThreadExitHandler(VfsThreadExitProc, NULL);
}
}
As you can see, the clientData there is really just being used as a marker so the code knows whether to do one-time initialisation.
To discover what the mount mapping is, you'll need to keep internal structures. You're strongly recommended to make the Tcl_Filesystem structure instance itself be global (or rather static at file scope) in your code.

How to set the mode for a character device in a Linux kernel module?

I'm creating a character device module that plays a game of Tic-tac-toe. I'm trying to program it so it sets it's /dev/ticactoe mode to 666, instead of having a user use the chmod command.
My main.c contains the following with implementations of tictactoe's init and exit (redacted for conciseness):
static dev_t device_number;
static struct cdev our_cdev;
static struct class* my_class = NULL;
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = tictactoe_read,
.write = tictactoe_write,
.open = tictactoe_open,
.release = tictactoe_release,
};
I have a tictactoe.h containing the following:
#define MODULE_NAME "tictactoe"
int tictactoe_open(struct inode *pinode, struct file *pfile);
ssize_t tictactoe_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset);
ssize_t tictactoe_write(struct file *pfile, const char __user *buffer, size_t length, loff_t *offset);
int tictactoe_release(struct inode *pinode, struct file *pfile);
I read about umode_t, but I'm not sure how I might use that for this module. Can anyone lead me in the right direction or explain how to implement the umode_t variable? Any help is appreciated.
The kernel source code for /dev/{null,zero,...} is a good place to look for this kind of stuff when you are in doubt, take a look at how this is implemented in drivers/char/mem.c.
Once you've created the class my_class for your device, you should set the ->devnode field to a function to set the mode you want. You can find modes in the <linux/stat.h> header, setting to 666 means rw-rw-rw-, which is S_IRUGO|S_IWUGO. It's a good idea to make this a constant somewhere in the code.
Here's the solution:
#define DEV_CLASS_MODE ((umode_t)(S_IRUGO|S_IWUGO))
static char *my_class_devnode(struct device *dev, umode_t *mode)
{
if (mode != NULL)
*mode = DEV_CLASS_MODE;
return NULL;
}
Then in your module init function:
my_class = class_create(THIS_MODULE, "tictactoe");
if (IS_ERR(my_class)) {
// Abort...
}
my_class->devnode = my_class_devnode;
Oh, and by the way, you do not need to #define MODULE_NAME, it's already defined automatically and it's KBUILD_MODNAME.

Share variables internally across functions

Suppose I have a function like this:
static int init_processing(char *buf, FILE *stream, enum operationMode mode) {
/* save index of `stream' in current operations */
/* start processing */
/* save some important variables for continue_processing */
off_t position;
enum operationMode _mode;
return num_processing_operations_left;
}
.. that I would be calling occasionally. And I have another function that does the actual processing I want:
static int continue_processing(FILE *stream) {
/* lookup the index of `stream' in current operations */
/* do some stuff */
/* save some static variables */
static off_t left = position;
static void *some_ptr;
return --num_processing_operations_left;
}
I also have a cleaning function to invoke when finishing up a certain operation:
static int end_processing(FILE *stream) {
/* check */
if (num_processing_operations_left)
return 1;
/* clean everything */
return 0;
}
As you can see, this related functions technique is very familiar that it is used by the standard library itself (e.g [malloc, free, realloc], [fdopen, fopen, fclose]).
What I want to achieve here, is how to share some variables across a bunch of functions ?
I thought of two solutions:
Put each set of functions in a file of their own, providing static variables valid only for the file itself.
Use only one function that takes an extra enum parameter as a mode and structure the function accordingly.
But these solutions aren't actually solutions, they're just workarounds to cope with the problem. So, is there any standard technique to share variables among functions ?
Create a context structure keeping all you need:
struct Context
{
FILE * stream;
off_t position;
unsigned num_processing_operations_left;
/* define some more important variables */
}
and pass it to all functions in question:
static int init_processing(struct Context * pctx, char *buf, enum operationMode mode);
static int continue_processing(struct Context * pctx);
static int end_processing(struct Context * pctx);
int main(void)
{
struct Context ctx = {0};
/* init ctx here */
int result = init_processing(&ctx, ...);
...
result = continue_processing(&ctx);
...
result = end_processing(&ctx);
...
}

C - segmentation fault using struct member values

I'm running head-long into a segmentation fault that I'm not sure of the reason behind.
Short story... I store file names into members of a struct, then use those members to open files to load their data into linked lists. This is working fine when I only have two file, but when I go to add a third, I get a segmentation fault opening the first file.
Code will hopefully illustrate better...
int main(int argc, char* argv[])
{
/* Initalise tennisStore struct */
TennisStoreType *ts;
systemInit(ts);
/* Variables */
ts->stockFile = "stock.csv";
ts->custFile = "customer.csv";
ts->salesFile = "sales.csv";
/* Load data from files */
loadData(ts, ts->custFile, ts->stockFile);
...
}
The struct details for ts...
typedef struct tennisStore
{
CustomerNodePtr headCust;
unsigned customerCount;
StockNodePtr headStock;
unsigned stockCount;
char *custFile;
char *stockFile;
char *salesFile;
} TennisStoreType;
systemInit() seems pretty innocuous, but here's the code just in case...
void systemInit(TennisStoreType *ts)
{
/* Set ts options to be ready */
ts->headCust = NULL;
ts->headStock = NULL;
ts->customerCount = 0;
ts->stockCount = 0;
}
loadData()...
void loadData(TennisStoreType* ts, char* customerFile, char* stockFile)
{
/* Load customer data */
addCustNode(ts, customerFile);
/* Load stock data */
addStockNode(ts, stockFile);
}
Here's where the problem occurs...
void addStockNode(TennisStoreType* ts, char* stockFile)
{
/* Variables */
StockNodePtr head, new, current, previous;
unsigned stkLevel;
char *stkTok1, *stkTok2, *stkTok3, *stkTok4;
char buf[BUFSIZ];
float stkPrice;
FILE *stream;
/* Set head */
head = NULL;
/* Open stock file */
stream = fopen(stockFile, "r"); <-- segmentation fault when sales.csv line included
assert(stream);
while (fgets(buf, BUFSIZ, stream))
{
...
}
...
}
As above, when the ts->salesFile = "sales.csv" line is included in main, the segmentation fault occurs. When it isn't, all is fine (file opens, I can read from it, write to it etc). Cannot for the life of me understand why, so I'm appealing to your good nature and superior knowledge of C for potential causes of this problem.
Thanks!
ts is uninitialized, and used as is, in systemInit().
It should be malloc()ed..
change
TennisStoreType *ts;
to
TennisStoreType *ts=malloc(sizeof(TennisStoreType));
or
change
TennisStoreType *ts;
systemInit(ts);
to
TennisStoreType ts;
systemInit(&ts);
You never actually created your TennisStoreType object.
int main(int argc, char* argv[])
{
TennisStoreType *ts; // <-- allocates 4 bytes for a pointer
systemInit(ts); // <-- pass the pointer to nowhere around.
Try inserting ts = malloc(sizeof(TennisStoreType)) in between those two lines.

Resources