I am trying to write a very simple echo Linux driver.
The driver takes a maximum of 250 characters from command-line and just writes it into a dummy device 'mydev'. This is again read back from the device. The front end and driver code is pasted below for reference.
The issue is I am able to write but not read. There is no error on compilation or segmentation fault. But none of the messages in printk in the driver's read are printed. I am puzzled as to what is happening. Can I get some clue here?
I am just sharing the code copy for better clarity:
mydriver.c :
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
static int major;
static char kbuf[250];
static int dv_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int dv_close(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t dv_read(struct file *filp, char __user *buf,
size_t sz, loff_t *fpos)
{
int r;
int L;
printk("READ:Entering\n");
L = strlen(kbuf);
r = copy_to_user(buf, kbuf, L);
printk("READ:Ends\n");
return L;
}
static ssize_t dv_write(struct file *filp, const char __user *buf,
size_t sz, loff_t *fpos)
{
int r, wr_sz;
printk("WRITE:Entering\n");
memset(kbuf,'\0', 250);
if ( sz <= 250 ) {
wr_sz = sz;
} else {
wr_sz = 250;
}
r = copy_from_user(kbuf, buf, wr_sz);
printk("WRITE:Rx buf = %s\n", kbuf);
return 0;
}
static struct file_operations dv_fops = {
.open = dv_open,
.release = dv_close,
.read = dv_read,
.write = dv_write,
.owner = THIS_MODULE,
};
int init_module(void)
{
major = register_chrdev(0, "dvdev", &dv_fops);
if ( major < 0 ) {
printk("Error in registering driver\n");
return -1;
}
else printk("Success. major = %d\n", major);
return 0;
}
void cleanup_module(void)
{
unregister_chrdev(major, "dvdev");
}
myuserapp.c
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
static char buf[250];
static char * wbuf;
int main(int argc, char **argv)
{
int fd;
int option;
int nbr = 0, len;
if ( argc != 2 ) {
printf("usage: front <devName>\n");
return -1;
}
fd = open("mydev", O_RDONLY | O_WRONLY);
if ( fd < 0 ) {
printf("Error opening file. %s does not exist\n", argv[1]);
return -2;
}
wbuf = argv[1];
len = strlen(wbuf);
nbr = write(fd, wbuf, len);
printf("USR: Buf written = %s, nbr = %d\n", wbuf ,nbr);
nbr = read(fd, buf, 250);
printf("USR RD: %s", buf);
close(fd);
return 0;
}
Your code has at least one error:
fd = open("mydev", O_RDONLY | O_WRONLY);
That is an improper open() call.
The man page for open() specifies that:
Applications shall specify exactly one of the first three values (file access modes) below in the value of oflag:
O_RDONLY Open for reading only.
O_WRONLY Open for writing only.
O_RDWR Open for reading and writing. The result is undefined if this flag is applied to a FIFO.
Instead of specifying only one, you have an expression of two values.
I guess O_RDONLY and O_WRONLY are bits individually, so it would be something like O_RDONLY | O_WRONLY = 10|01 = 11 . Both the bits of Read and write are set.
The bit values are irrelevant, since combining these values is not allowed.
You seem to be ignoring the exclusionary suffix "ONLY".
RDONLY means "allow read and disallow write".
WRONLY means "allow write and disallow read".
"O_RDONLY | O_WRONLY" is a logical contradiction.
If you want to allow both reading and writing, then you have to specify O_RDWR.
And Mark Stevens has provided the correct values and Boolean arithmetic to prove that your inproper expression is not equivalent to O_RDWR.
sawdust gave the right answer, but there's another problem in your code.
If you write 250 bytes to your device, the buffer is not null-terminated. Then, when reading, strlen will read beyond it, leading to an unexpected result.
Each calls to your write function will result in calling memset on the buffer.This is the root cause you are not able to retrieve the message.
Related
I'm trying to create a simple character device LKM but I've been stuck for days trying to get my read and write to work correctly. Currently when I do something like:
echo hi > /dev/simple_character_device
I am able to see I'm writing the correct amount of bytes.
But when I attempt to cat out the contents of that device it will continuously loop hi until reaching a bad address. Currently I'm trying to keep track of how many bytes I've written in a global counter. But that doesn't seem right. Any help on implementing the read and write would be appreciated.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
MODULE_LICENSE("GPL");
#define BUFFER 1024
char * buffer_data;
// Count open and closed
size_t current_count;
int my_open(struct inode *, struct file *);
int my_release(struct inode *, struct file *);
ssize_t my_read(struct file *, char __user *, size_t count, loff_t *);
ssize_t my_write(struct file *, const char __user *, size_t count, loff_t *offp);
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write
};
int reg_init(void)
{
// Allocate memory to store information
buffer_data = kmalloc(BUFFER, GFP_KERNEL); // Use Kernel Flag
register_chrdev(240, "simple_character_device", &fops);
printk(KERN_ALERT "Init Allocating Memory");
return 0;
}
void reg_exit(void)
{
// Free and unregister device and data
kfree(buffer_data);
unregister_chrdev(240, "simple_character_device");
printk(KERN_ALERT "Deregister Simple Character Device");
}
int my_open(struct inode *inode, struct file *file){
printk(KERN_ALERT "Open File Device.\n");
return 0;
}
int my_release(struct inode *inode, struct file *file){
printk(KERN_ALERT "Close File Device.\n");
return 0;
}
ssize_t my_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){
// Check if we are reading within the Buffer Size
if(BUFFER - *offp < 0){
printk(KERN_ALERT "Out of buffer range.\n");
return -EFAULT;
}
// Check if we fail to copy to user
if (copy_to_user(buff, buffer_data, current_count) != 0){
printk(KERN_ALERT "Failed to send character to user\n");
return -EFAULT;
}
(*offp) += current_count;
printk(KERN_ALERT "Read %zu bytes.\n", current_count);
return current_count;
}
ssize_t my_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp){
// We need to get data FROM the user space
// Make sure we are reading within the buffer
if (*offp >= BUFFER || BUFFER - count < *offp){
printk(KERN_ALERT "ATTEMPTING TO WRITE TO OUSIDE OF BUFFER!\n");
return EFAULT;
}
// Get the amount of bytes from the user
copy_from_user(buffer_data + *offp, buff, count);
*offp += count;
printk(KERN_ALERT "Wrote %zu to the device.\n", count);
current_count = count;
return current_count;
}
module_init(reg_init);
module_exit(reg_exit);
In my_read() you must not allow reading the buffer after current_count (after the point where initialized data ends) . If the requested count goes farther you must first trim the passed count. If the resulting count is <= 0, or if the offset is after current_count you must return 0 to indicate end of file.
Also you must return the trimmed count (which was really copied to the user).
I expect that you test with echo and cat. But if you test with your own code, by reading and writing in sequence, don't forget to reset the offset using lseek() between reading and writing.
As far as I can tell your code looks like a good start but the counts and file position management require some cleanup.
Obviuosly, it's a unsuprising newbie's question after a lot of troubles with kernel programming. I try to launch a program that gets driver file in /dev folder available for some reading and writing (indeed, I realize it's rather unsafe idea, but I need strongly going ahead with all that experience). Let's look at a module source code:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
int init_module(void); // driver file initialization as opening it
void cleanup_module(void); // exec files removal ahead of shutting driver file
static int device_open(struct inode *, struct file *); // driver file opening
static int device_release(struct inode *, struct file *); // return of system resource control
static ssize_t device_read(struct file *, char *, size_t, loff_t *); // reading from driver file
static ssize_t device_write(struct file *, const char *, size_t, loff_t *); // writing into driver file
#define SUCCESS 1
#define DEVICE_NAME "sample device"
#define BUF_LEN 80
static int Major; // device's major number
static int Device_Open = 0; // device access counter
static char message[BUF_LEN]; // buffer for both read and write operations
static char *message_ptr;
// list of basic operations executable by driver
static struct file_operations ops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
int init_module(void)
{
Major = register_chrdev(0, DEVICE_NAME, &ops); // major number assignment
// evaluate whether driver file is accessible
if(Major < 0) {
printk(KERN_ALERT "Device registration attempt failed\n");
return Major;
}
return SUCCESS;
}
void cleanup_module(void)
{
unregister_chrdev(Major, DEVICE_NAME); // cancelling driver registration in file system before exit
printk(KERN_ALERT "Driver file of /dev/%s c %d 0 has been destroyed\n", DEVICE_NAME, Major);
return;
}
static int device_open(struct inode * node, struct file * file)
{
printk(KERN_INFO "Trying access /dev/%s c %d 0\n", DEVICE_NAME, Major);
static int counter = 0; // access counter initializing
// file control evaluation
if(Device_Open)
return -EBUSY;
Device_Open++; // increment counter to avert driver's immanent running
sprintf(message, "This sentence displayed %d times\n", counter++);
message_ptr = message;
try_module_get(THIS_MODULE);
return SUCCESS;
}
static int device_release(struct inode * node, struct file * file)
{
printk(KERN_INFO "Trying closure of /dev/%s c %d 0\n", DEVICE_NAME, Major);
Device_Open--; // decrement counter to keep driver file removable as well
module_put(THIS_MODULE);
return SUCCESS;
}
static ssize_t device_read(struct file * file, char * ch, size_t num, loff_t * off)
{
int read_bytes = 0; // output size
printk(KERN_INFO "Trying read from /dev/%s c %d 0\n", DEVICE_NAME, Major);
if(*message_ptr == 0)
return 0;
// loop-executed reading from file
while(num && *message_ptr) {
put_user(*(message_ptr++), ch++);
num--;
read_bytes++;
}
printk("%d bytes read, %d bytes to be handled", read_bytes, num);
return read_bytes;
}
// updated stuff
static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
char message_from_user[BUF_LEN];
if(copy_from_user(message_from_user, buff, len)) return -EINVAL;
printk(KERN_INFO "length of message:%d message:'%s'", (int)len, message_from_user);
return len;
}
To test reading/writing, I use this code:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <linux/unistd.h>
extern int errno;
int main()
{
int fd; // file descriptor id
size_t cnt = 0; // input / output number of bytes
size_t cnt_2 = 0;
char inputBuffer[30] = "Device file is open"; // write operation buffer
char outputBuffer[50]; // read operation buffer
printf("Continuing with basics of Linux drivers...\n");
// evaluate accessibility of driver file
fd = open("/dev/dev", O_RDWR);
if(fd == -1) {
close(fd);
printf("File opening isn't completed\n");
return 1;
}
printf("Driver file is open now\n");
// writing from file
cnt = write(fd, inputBuffer, sizeof(inputBuffer));
printf("Driver got written %d bytes\n", cnt);
// read into file
cnt = read(fd, outputBuffer, sizeof(outputBuffer));
printf("Driver received %d bytes\n", cnt);
int i = 0;
// display an input message
while(i < cnt) {
printf("%c", outputBuffer[i]);
printf("%s", "\n");
i++;
}
close(fd); // wrap up driver connection and clear memory
printf("Driver file is close\n");
return 0;
}
Altough the module was built in as well as dev file was made by mknod (I run it on Ubuntu 18.04), I'm stuck at write operation due to some miscomprehension of driver calls in user/kernel spaces. Once I start my program, outputs are here as follows:
Continuing with basics of Linux drivers...
Driver file is open now
Driver got written -1 bytes
Followed by last line output, the system becomes inoperable (no response until I make off PC). That's a case I think of like a matter of memory control or, most probably, some driver file properties. However, user rights have been granted to reading / writing / executing, no access restrictions are inferable indeed. Hopefully, it's possible to point out to what's wrongness in the code posted here.
Seeing your code you don't handle the writing part.
static ssize_t device_write(struct file * file, const char * ch, size_t num, loff_t * off)
{
printk(KERN_ALERT "Operation denied\n");
return -EINVAL;
}
Thus there is no way your module can possibly work.
But your crash comes from memory accesses in your reading function (check this with strace). I let you understand your issue. dmesg should help (or in the case your system panics you can make the log persistant to debug it after rebooting your system).
So I'm using mmap to then write to another file. But the weird thing is, when my code hits mmap, what it does is clears the file. So I have a file that's populated with random characters (AB, HAA, JAK, etc...). What it's supposed to do is use mmap as read basically and then write that file to the new file. So that first if (argc == 3) is the normal read and write, the second if (argc ==4) is supposed to use mmap. Does anyone have any idea why on Earth this is happening?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/io.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(int argc, char const *argv[])
{
int nbyte = 512;
char buffer[nbyte];
unsigned char *f;
int bytesRead = 0;
int size;
int totalBuffer;
struct stat s;
const char * file_name = argv[1];
int fd = open (argv[1], O_RDONLY);
int i = 0;
char c;
int fileInput = open(argv[1], O_RDONLY);
int fileOutPut = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
fstat(fileInput, &s);
size = s.st_size;
printf("%d\n", size);
if (argc == 3)
{
printf("size: %d\n", size);
printf("nbyte: %d\n", nbyte);
while (size - bytesRead >= nbyte)
{
read(fileInput, buffer, nbyte);
bytesRead += nbyte;
write(fileOutPut, buffer, nbyte);
}
read(fileInput, buffer, size - bytesRead);
write(fileOutPut, buffer, size - bytesRead);
}
else if (argc == 4)
{
int i = 0;
printf("4 arg\n");
f = (char *) mmap (0, size, PROT_READ, MAP_PRIVATE, fileInput, 0);
/* This is where it is being wipped */
}
close(fileInput);
close(fileOutPut);
int who = RUSAGE_SELF;
struct rusage usage;
int ret;
/* Get the status of the file and print some. Easy to do what "ls" does with fstat system call... */
int status = fstat (fd, & s);
printf("File Size: %d bytes\n",s.st_size);
printf("Number of Links: %d\n",s.st_nlink);
return 0;
}
EDIT: I wanted to mention that the first read and write works perfectly, it is only when you try to do it through the mmap.
If you mean it's clearing your destination file, then yes, that's exactly what your code will do.
It opens the destination with truncation and then, in your argc==4 section, you map the input file but do absolutely nothing to transfer the data to the output file.
You'll need a while loop of some description, similar to the one in the argc==3 case, but which writes the bytes in mapped memory to the fileOutput descriptor.
I am writing a simple program to flip all the bits in a file, but right now it only does the first 1000 bytes until I get that much working. Why does my call to read() ignore \r characters? When I run this code on a file that only contains \r\n\r\n, the read call returns 2 and the buffer contains \n\n. The \r characters are completely ignored. I'm running this on Windows (this wouldn't even be an issue on Linux machines)
Why does read(2) skip over the \r character when it finds it? Or is that what is happening?
EDIT: Conclusion is that windows defaults to opening files in "text" mode as opposed to "binary" mode. For this reason, when calling open, we must specify O_BINARY as the mode.
Thanks, code below.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
void invertBytes(size_t amount, char* buffer);
int main(int argv, char** argc)
{
int fileCount = 1;
char* fileName;
int fd = 0;
size_t bufSize = 1000;
size_t amountRead = 0;
char* text;
int offset = 0;
if(argv <= 1)
{
printf("Usages: encode [filenames...]\n");
return 0;
}
text = (char *)malloc(sizeof(char) * bufSize);
for(fileCount = 1; fileCount < argv; fileCount++)
{
fileName = argc[fileCount];
fd = open(fileName, O_RDWR);
printf("fd: %d\n", fd);
amountRead = read(fd, (void *)text, bufSize);
printf("Amount read: %d\n", amountRead);
invertBytes(amountRead, text);
offset = (int)lseek(fd, 0, SEEK_SET);
printf("Lseek to %d\n", offset);
offset = write(fd, text, amountRead);
printf("write returned %d\n", offset);
close(fd);
}
return 0;
}
void invertBytes(size_t amount, char* buffer)
{
int byteCount = 0;
printf("amount: %d\n", amount);
for(byteCount = 0; byteCount < amount; byteCount++)
{
printf("%x, ", buffer[byteCount]);
buffer[byteCount] = ~buffer[byteCount];
printf("%x\r\n", buffer[byteCount]);
}
printf("byteCount: %d\n", byteCount);
}
fd = open(fileName, O_RDWR);
should be
fd = open(fileName, O_RDWR | O_BINARY);
See read() only reads a few bytes from file for details.
Try opening with O_BINARY to use binary mode, text mode may be default and may ignore \r.
open(fileName, O_RDWR|O_BINARY);
I'm studying UNIX programming and was experimenting with read/write system calls.
I have a file with a pair of integer:
4 5
and I wrote this code to read the numbers:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
typedef struct prova {
int first;
int second;
} prova_t;
int main(void) {
int fd;
prova_t origin;
prova_t result;
ssize_t bytes_read;
size_t nbytes;
fd = open("file.bin", O_WRONLY | O_CREAT);
origin.first = 24;
origin.second = 3;
write(fd, &origin, sizeof(prova_t));
close(fd);
fd = open("file.bin", O_RDONLY);
nbytes = sizeof(prova_t);
/* 1.BAD */
bytes_read = read(fd, &result, nbytes);
write(STDOUT_FILENO, &(result.first), sizeof(int));
write(STDOUT_FILENO, &(result.second), sizeof(int));
close(fd);
/* 2.GOOD */
nbytes = sizeof(int);
bytes_read = read(fd, &(result.first), nbytes);
write(STDOUT_FILENO, &(result.first), bytes_read);
bytes_read = read(fd, &(result.second), nbytes);
write(STDOUT_FILENO, &(result.second), bytes_read);
return 0;
}
In my first attempt I tried to read the whole struct from file and write its members to stdout. In this way, along with the numbers, I get some weird characters
4 5
E�^�
In my second attempt I read the numbers one by one and there were no problems in the output.
Is there any way to read and write the struct using the first method?
Edit: I updated the code to reflect suggestion from other users but still getting strange characters instead of numbers
First, let's do a hex dump to see what is really stored in the file.
hexdump -C b.txt or od -t x2 -t c b.txt are two examples (od is for octal dump, more common, less pretty output in my opinion)
00000000 34 20 35 0a |4 5.|
00000004
That's is what the file looks like if it was a created as an ASCII text file (such as using a text editor like vi). You can use man ascii to double check the hexadecimal values.
Now if you had a binary file that only contains two 8-bit bytes, in the system's native byte ordering (e.g. little-endian for x86, big endian for MIPS, PA-RISC, 680x0) then the hexdump would look like:
00000000 04 05 |..|
00000004
Here is the code to both create (open & write) a binary file, and read it back.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h> /* uint32_t */
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
/* User has read & write perms, group and others have read permission */
const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
typedef struct prova {
uint32_t first;
uint32_t second;
} prova_t;
#define FILENAME "file.b"
/* 'Safe' write */
int safewrite( int fd, const void *p, size_t want) {
int ret;
errno = 0;
while (want) {
ret = write(fd, (uint8_t *)p, want);
if (ret <= 0) {
if (errno != EINTR && errno != EAGAIN) {
return -1;
}
errno = 0;
continue;
}
want -= ret;
p = (uint8_t*) p + ret;
}
return 0;
}
int saferead(int fd, const void *p, size_t want) {
int ret;
errno = 0;
while (want) {
ret = read(fd, (uint8_t*)p, want);
if( ret == 0 )
return -1; /* EOF */
if (ret <= 0) {
if( errno != EINTR && errno != EAGAIN ) {
return -1;
}
errno = 0;
continue;
}
want -= ret;
p = (uint8_t*) p + ret;
}
return 0;
}
int main(int argc, char **argv) {
int fd;
prova_t result;
size_t nbytes;
/* Create file */
fd = creat(FILENAME, mode);
if (fd < 0) {
fprintf(stderr, "Unable to open " FILENAME ": %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
nbytes = sizeof(prova_t);
result.first = 4;
result.second = 5;
if (0 != safewrite(fd, &result, nbytes)) {
fprintf(stderr, "Unable to write to " FILENAME ": %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
close(fd);
fd = -1;
/* Reopen and read from binary file */
fd = open(FILENAME, O_RDONLY);
nbytes = sizeof(prova_t);
if (0 != saferead(fd, &result, nbytes)) {
fprintf(stderr, "Unable to read file \"" FILENAME "\": %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
close(fd);
printf( "Read: %d %d (%#.02x%.02x)\n",
result.first, result.second,
result.first, result.second);
return EXIT_SUCCESS;
}
Now the data file contents look like:
00000000 04 00 00 00 05 00 00 00 |........|
00000008
Because the integers were specified as 32-bit integers (32-bits / 8 bits per byte = 4 bytes). I'm using a 64-bit system (little endian, x86), so I wanted to be explicit so the your results should match, assuming little-endian.
You tried to read to a struct containing two ints, by passing a pointer to some data and telling read that you had one int's worth of storage. The first should be
bytes_read = read(fd, &result, sizeof(prova_t));
From the name of your file, I assume that you are trying to read a text file. read from unistd.h reads from binary files. If you are indeed trying to read from a text file, you should use fscanf or in ifstream
To read a struct in binary:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
typedef struct prova {
int first;
int second;
} prova_t;
int main(void) {
int fd;
prova_t result;
ssize_t bytes_read;
size_t nbytes;
prova_t initial;
// create a binary file
fd = open("file.bin", O_WRONLY | O_CREAT);
initial.first = 4;
initial.second = 5;
write(fd, &initial, sizeof(prova_t));
close(fp);
// read it back
fd = open("file.bin", O_RDONLY);
nbytes = sizeof(prova_t);
bytes_read = read(fd, &result, nbytes);
write(STDOUT_FILENO, &(result.first), sizeof(int));
write(STDOUT_FILENO, &(result.second), sizeof(int));
close(fp);
return 0;
}
Include flatbuffers/util.h, there are save and load functions sepeartely
SaveFile(const char *name, const char *buf, size_t len,
bool binary);
LoadFile(const char *name, bool binary, std::string *buf);