I've got some linux drivers I'm trying to port from linux 2.4 to 3.0. During this lengthy span of time, the argument list of ioctl (unlocked_ioctl now) changed a bit:
-static int can_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long can_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
The code was using the inode to get the minor version and was passing it to some other commands. Now that inode isn't a "free-be" given in the ioctl parameter list, how can I get it?
Is it possible to derive from the file pointer? or should I "save" a global pointer to it when it shows up in the _open() method? I'd rather avoid that if there's a better way.
You can get the inode from struct file * file in the kernel space. here is the brief struct map
file->f_path.d.dentry->d_inode; you can find the definition followed.
for dentry struct in dcache.h
struct dentry {
atomic_t d_count;
unsigned int d_flags; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
int d_mounted;
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */
for the file structure in fs.h
file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
...
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
#ifdef CONFIG_SMP
oop, just figured it out by poking around the kernel and looking at other drivers (don't know why it didn't occur to me to do that before). In case anyone else is interested you can get the inode from the file pointer passed into the ioctl as such:
long can_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file->f_path.dentry->d_inode;
If anyone knows why this is a bad idea (I just took it from another driver), or if there's a better/preferred way let me know.
Related
I'm currently working on a project involving the exploration of the linux kernel, particularly the XFS filesystem , right now I'm trying to read where a file resides on disk and i stumbled upon the xfs_ifork struct which looks like:
typedef struct xfs_ifork {
int if_bytes; /* bytes in if_u1 */
int if_real_bytes; /* bytes allocated in if_u1 */
struct xfs_btree_block *if_broot; /* file's incore btree root */
short if_broot_bytes; /* bytes allocated for root */
unsigned char if_flags; /* per-fork flags */
union {
xfs_bmbt_rec_host_t *if_extents;/* linear map file exts */
xfs_ext_irec_t *if_ext_irec; /* irec map file exts */
char *if_data; /* inline file data */
} if_u1;
union {
xfs_bmbt_rec_host_t if_inline_ext[XFS_INLINE_EXTS];
/* very small file extents */
char if_inline_data[XFS_INLINE_DATA];
/* very small file data */
xfs_dev_t if_rdev; /* dev number if special */
uuid_t if_uuid; /* mount point value */
} if_u2;
} xfs_ifork_t;
my lead on the u1 union, which encases different scenarios of file data fork, but i cant seem to have figured out the exact way to read them , the xfs_bmbt_rec_host_t struct refers to the disk address , written inside 2 uint64 types which encapsulates the entire address i need,
but somehow, the address I'm able to read isn't the correct one, so goes for the xfs_ext_irec_t struct, which holds the xfs_bmbt_rec_host_t struct and counters.
if someone happens to know theses structs well or have used them i would appreciate some explaining
I'm aware that we are not able to pass parameters to xv6 system call directly and we are forced to use it's built in methods.
But all examples and questions in this site is about how to send integer to system call. Which it's answer is using argint() method.
But my question is, is there anyway to pass "struct" to a xv6 system call? Are there any bulit-in methods for this purpose too?
If there is, could you please say a simple example?
Passing a struct through system call is possible.
While one can't pass a struct itself as a system call parameter, passing a pointer to it is possible and will allow using it as both an input or output parameter.
Allowing to use as argument the data itself and not a pointer to it will damage the requirement of the system calls mechanism- as passing data must be implemented in a generic way to allow all data types to (as well as future structs) be used.
Let's have a look on an existing implementation of the system call fstat.
int fstat(int fd, struct stat *st);
fstat requires a file descriptor number as an input and outputs a matching stats information using struct stat.
struct stat {
short type; // Type of file
int dev; // File system's disk device
uint ino; // Inode number
short nlink; // Number of links to file
uint size; // Size of file in bytes
};
Although fstat uses a struct pointer as an output parameter, using it as an input will be similar.
The function sys_fstat in kernel code starts the implementation of fstat system call (XV6's convention is to handle parameter fetching from user space by sys_* functions).
int sys_fstat(void)
{
struct file *f;
struct stat *st;
if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0)
return -1;
return filestat(f, st);
}
This function first gets a corresponding struct file to the file descriptor number received by the first fstat function argument (using argfd). Then, fetches the struct stat pointer received by the second fstat function argument using argptr and saves the given pointer in a local (function scope) pointer variable.
At this point, all arguments were fetched and can be used by the kernel implementation.
Note: Although the struct stat pointer is a user-space pointer (located on the lower half of the virtual space), it is safe for the kernel to use it here because when the kernel is serving a process' system call, it uses the process' own paging table.
Although the above answer is correct but i prefered to write my own solutions to make it more usable for other viwers.
i used argptr to pass a pointer-to-struct to my system call.
in sysproc.c:
int sys_counts (void){
struct countTable *ct;
argptr (0 , (void*)&ct ,sizeof(*ct));
return counts(ct);
}
in proc.c:
int counts (struct countTable *ct){
for (int i=0 ; i<22 ; i++){
(ct->system_calls)[i] = count_calls[i] ;
}
return 22;
}
and finally in my user-space-program:
int main (){
struct countTable *ct = malloc (sizeof (struct countTable));
// call system call
counts(ct);
exit();
}
Although one of the answers is acceptable I wrote my answer that is clear and complete.
Note that passing an argument to system-call directly is impossible. we will use argptr to do that.
In userspace, we define a struct that we want to work with. in a user-level file like test.c
#include "types.h"
#include "stat.h"
#include "user.h"
struct Data
{
...
int id; // some fields
...
};
int main(int argc, char *argv[])
{
struct Data *data = malloc(sizeof(struct Data));
// call the systemcall
doSomeWork((void *)data);
exit();
}
In sysproc.c we define system-call and use argptr to get arguments:
int sys_doSomeWork(void){
struct Data *data;
argptr(0, (void *)&data, sizeof(*data));
return doSomeWork((void *)data);
}
and in proc.c we can write the functionality of system-call:
int doSomeWork(void *data){
// cast to (struct Data *)
struct Data *my_data = (struct Data *)data;
...
// work with my_data
...
return 0;
}
and to make Data struct accessible inside sysproc.c and proc.c we define Data struct inside defs.h:
struct Data
{
...
int id; // some fields
...
};
I want to know the free space and total space on a nfs share.
I am using ubuntu linux computers for this.
I can do that through commands but I need a C program for this.
I looked into libnfs.h, it contains some functions declarations that I think can be used :
EXTERN int nfs_stat(struct nfs_context *nfs, const char *path, struct stat *st);
EXTERN int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st);
EXTERN int nfs_statvfs(struct nfs_context *nfs, const char *path, struct statvfs *svfs);
But I don't know which one should be used and what to pass for the first parameter(what is context?)
Please help.
Thanks for help in advance.
As #remyabel suggested, I wrote following:
#include<sys/time.h>
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/statvfs.h>
#include<nfsc/libnfs.h>
#define MAX 63
int main()
{
struct nfs_context *nfs = NULL;
struct statvfs st;
char path[MAX];
strcpy(path,"nfs://192.168.2.73/home/sumit/music2/");
nfs = nfs_init_context();
int ret;
ret = nfs_mount(nfs, "192.168.2.73", path);
perror("Err1");
ret = nfs_statvfs(nfs, "//", &st);
printf("\nret=%d",ret);
printf("\nf_bsize= %lu",st.f_bsize);
printf("\nf_frsize= %lu",st.f_frsize);
printf("\nf_blocks= %lu",st.f_blocks);
printf("\nf_bfree= %lu\n",st.f_bfree);
return 0;
}
Now it works :)
There's a lot more to it, waiting for responses and so on. I had exactly the same problem a few months ago when i wanted to write a nagios plugin to check the space on a filesystem that isn't already mounted. The source code is available at http://www.gbl-software.de/nagiosbinaries/check_nfs/check_nfs-src.tgz, feel free to use and modify as you wish. This uses the NFS libraries from nfsreplay and has the advantage that it's ready to compile for Linux, Solaris and AIX.
Note that, for most NFS servers, your program needs to be suid root to be able to use a reserved port (<1024), because NFS servers won't talk to any port for security reasons.
First you declare the context at the beginning of your program:
struct nfs_context *nfs = NULL;
Here we'll hold the information you want:
struct statvfs st;
Then we initialize the context:
nfs = nfs_init_context();
Mount the share:
struct client client;
client.server = server;
client.export = path;
client.is_finished = 0;
ret = nfs_mount(nfs, client.server, client.export);
And you can use nfs_statvfs like so;
ret = nfs_statvfs(nfs, path, &st);
Where nfs is the context from earlier, path is some filename or directory, and st is the struct that will hold the information. ret contains errno if there was a problem.
Here's statvfs:
struct statvfs {
uint32_t f_bsize;
uint32_t f_frsize;
uint64_t f_blocks;
uint64_t f_bfree;
the sched.h in Linux-0.12:
struct task_struct {
/* these are hardcoded - don't touch */
long state; /* -1 unrunnable, 0 runnable, >0 stopped */
long counter;
long priority;
long signal;
struct sigaction sigaction[32];
long blocked; /* bitmap of masked signals */
/* various fields */
int exit_code;
unsigned long start_code,end_code,end_data,brk,start_stack;
long pid,pgrp,session,leader;
int groups[NGROUPS];
/*
* pointers to parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
* p->p_pptr->pid)
*/
struct task_struct *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
unsigned short uid,euid,suid;
unsigned short gid,egid,sgid;
unsigned long timeout,alarm;
long utime,stime,cutime,cstime,start_time;
struct rlimit rlim[RLIM_NLIMITS];
unsigned int flags; /* per process flags, defined below */
unsigned short used_math;
/* file system info */
int tty; /* -1 if no tty, so it must be signed */
unsigned short umask;
struct m_inode * pwd;
struct m_inode * root;
struct m_inode * executable;
struct m_inode * library;
unsigned long close_on_exec;
struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
struct desc_struct ldt[3];
/* tss for this task */
struct tss_struct tss;
};
What's different between struct m_inode * root and struct m_inode * pwd ?
Thank you.
The pwd is a dentry struct. A dentry is what maps file names to inode numbers. The pwd is just the current directory you are in. The root, is the root dentry, so I assume it is the root directory /.
http://www.makelinux.net/books/lkd2/ch12lev1sec10
the pwd in such structs presents current working directory, usually not the same thing as the command pwd in Linux.
If you want a example, use ps to get a pid XXX of a running process in terminal, and cd /proc/XXX/,there are root and cwd(current working directory) of the process.
cwd is the current working directory which you can change with chdir(). root is the root directory that can be changed with chroot() (look up chroot jail).
How can I get exact total space of a drive using C language program running on Linux? I dont want to use shell script. Any suggestions?
statfs/statfs64
#include <sys/vfs.h> /* or <sys/statfs.h> */
int statfs(const char *path, struct statfs *buf);
int fstatfs(int fd, struct statfs *buf);
From the man page:
The function statfs() returns information about a mounted file system.
path is the pathname of any file within the mounted file system.
buf is a pointer to a statfs structure defined approximately as follows:
struct statfs {
long f_type; /* type of file system (see below) */
long f_bsize; /* optimal transfer block size */
long f_blocks; /* total data blocks in file system */
long f_bfree; /* free blocks in fs */
long f_bavail; /* free blocks avail to non-superuser */
long f_files; /* total file nodes in file system */
long f_ffree; /* free file nodes in fs */
fsid_t f_fsid; /* file system id */
long f_namelen; /* maximum length of filenames */
};
You can use it like this:
struct statfs buf;
statfs("/", &buf);