This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
How should I print types like off_t and size_t?
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int fd, offset;
char *data;
struct stat sbuf;
int counter;
if (argc != 2)
{
fprintf(stderr, "usage: mmapdemo offset\n");
exit(1);
}
if ((fd = open("mmapdemo.c", O_RDONLY)) == -1)
{
perror("open");
exit(1);
}
offset = atoi(argv[1]);
if (offset < 0 || offset > sbuf.st_size-1)
{
fprintf(stderr, "mmapdemo: offset must be in the range 0 - %d \n",
sbuf.st_size-1);
exit(1);
}
data = mmap((caddr_t)0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (data == (caddr_t)(-1))
{
perror("mmap");
exit(1);
}
// print the while file byte by byte
while(counter++<=sbuf.st_size)
printf("%c", *data++);
return 0;
}
when i run this code it give me error as
gcc mmap.c -o mmap
mmap.c: In function 'main':
mmap.c:38: warning: format '%d' expects type 'int', but argument 3 has type 'long int'
please tell me, why it is happening?
I believe you are missing code.
But, in one of your printf statements your are using the %d flag but intstead need to use the %ld.
Edit:
Heres the bug:
fprintf(stderr, "mmapdemo: offset must be in the range 0 - %d \n",
sbuf.st_size-1);
should be:
fprintf(stderr, "mmapdemo: offset must be in the range 0 - %ld \n",
sbuf.st_size-1);
Your code doesn't show up properly. The error that you are getting is just a warning. It means that you are using the wrong format string. For long int you probably should use %ld .
Use %ld
Hmm, the snippet you posted doesn't look like it has 38 lines in it, but the error you cite comes from using the format %d instead of %ld or one of its related C99 symbolic formats.
Ok, now there is more code posted. While st_size is technically off_t, and there is no C99 format specifier for off_t, %zd will print a size_t and conforms to C99. It's probably your best best.
However, as a practical matter, %ld will also work and is an acceptable choice.
Update: Ok, I was giving you advice on making your program compile, but R is pointing out that a portable program should run on at least ILP32, LP64, and LLP64, so it will be necessary in that case to cast to whatever type you have in the format, and that if you want all 64-bits to actually print on all those systems, about the only choice is %lld and a cast to (long long).
This:
fprintf(stderr, "mmapdemo: offset must be in the range 0 - %d \n",sbuf.st_size-1);
should be:
fprintf(stderr, "mmapdemo: offset must be in the range 0 - %ld \n",sbuf.st_size-1);
Related
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I am trying to acquire the size of a file after creating it and writing data to it. I get values that don't seem to correspond to the actual file size. Here is my program. Please show me how I can display the file size in Bits, Bytes, Kilobytes, and Megabytes. According to me the file size should be 288 Bits, 36 Bytes, 0.03515626 Kilobytes, and 0.000034332 Megabytes.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#define PERMS 0777
int main(int argc, char *argv[])
{
int createDescriptor;
int openDescriptor;
char fileName[15]="Filename1.txt";
umask(0000);
if ((openDescriptor = creat(fileName, PERMS )) == -1)
{
printf("Error creating %s", fileName);
exit(EXIT_FAILURE);
}
if(write(openDescriptor,"This will be output to testfile.txt\n",36 ) != 36)
{
write(2,"There was an error writing to testfile.txt\n",43);
return 1;
}
if((close(openDescriptor))==-1)
{
write(2, "Error closing file.\n", 19);
}
struct stat buf;
fstat(openDescriptor, &buf);
int size=buf.st_size;
printf("%d\n",size);
printf("%u\n",size);
return 0;
}
The fstat() function has a return code, check it.
int r = fstat(openDescriptor, &buf);
if (r) {
fprintf(stderr, "error: fstat: %s\n", strerror(errno));
exit(1);
}
This will print:
error: fstat: Bad file descriptor
Yep... you closed the file descriptor, it's not a file descriptor any more. You have to fstat() before calling close().
The code worries me.
This is extremely fragile, and cannot be recommended under any circumstances:
if (write(openDescriptor,"This will be output to testfile.txt\n",36 ) != 36)
You can do this:
const char *str = "This will be output to testfile.txt\n";
if (write(fd, str, strlen(str)) != strlen(str))
It will compile to the same machine code, and it's obviously correct (as opposed to the original code, where you have to count the number of characters in a string to figure out if it's correct or not).
Even better, when you are using stderr, just use the standard <stdio.h> functions:
fprintf(stderr, "There was an error writing to %s: %s\n",
fileName, strerror(errno));
The same error appears when defining fileName...
// You should never have to know how to count higher than 4 to figure
// out if code is correct...
char fileName[15]="Filename1.txt";
// Do this instead...
static const char fileName[] = "Filename1.txt";
You actually miscounted this time, [15] should have been [14], but better to leave it to the compiler. There's no benefit to making the compiler's job easier, since the compiler presumably doesn't have better things to do.
About the machine code:
$ cat teststr.c
#include <unistd.h>
void func(int openDescriptor) {
write(openDescriptor,"This will be output to testfile.txt\n",36 );
}
$ cat teststr2.c
#include <string.h>
#include <unistd.h>
void func(int openDescriptor) {
const char *str = "This will be output to testfile.txt\n";
write(openDescriptor, str, strlen(str));
}
$ cc -S -O2 teststr.c
$ cc -S -O2 teststr2.c
$ diff teststr.s teststr2.s
1c1
< .file "teststr.c"
---
> .file "teststr2.c"
Yep. As demonstrated, the call to strlen() does not actually result in different machine code.
Not sure if anyone has any ideas here, I haven't seen this before. I'm writing a stub to test out my kernel module, when I check the value of the command in userspace I get a different value vs. when I take a look at the in kernel space.
Part of the stub:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "ain.h"
#include "ain_ioctl.h"
#define AI_DEVICE "/dev/ain"
void main()
{
int fd, error, ioctl_par = 0;
char* dev;
long ret;
dev = AI_DEVICE;
printf("Starting driver test\n");
fd = open(dev, O_RDWR);
if (fd < 0) {
/* Failed to open -> Print error-message and exit */
printf("%s failed to open, error: %s\n", dev, strerror(errno));
}
printf("Doing the IOCTL now... cmd: %d\n", AIN_IOC_GET_AN0_CONF);
fflush(stdout);
ret = ioctl(fd, AIN_IOC_GET_AN0_CONF, &ioctl_par);
The ain_ioctl.h file:
#define AIN_IOC_MAGIC 'e'
#define AIN_IOC_GET_AN0_CONF _IOR(AIN_IOC_MAGIC, 46, int)
The ioctl routine in the kernel:
int ain_ioctl (struct inode * inodep, struct file * filp, unsigned int cmd, unsigned long arg)
{
printk("In the ain_ioctl function, cmd: %d. type: %d, dir: %d, nr: %d, size: %d\n",
cmd, _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
printk("Testing against command: %d. type: %d, dir: %d, nr: %d, size: %d\n",
AIN_IOC_GET_AN0_CONF, _IOC_TYPE(AIN_IOC_GET_AN0_CONF), _IOC_DIR(AIN_IOC_GET_AN0_CONF),
_IOC_NR(AIN_IOC_GET_AN0_CONF), _IOC_SIZE(AIN_IOC_GET_AN0_CONF));
Now I would have expected identical output in the user space print as in the kernel. And in the first set of prints in the kernel to the second. However that's not what I'm seeing...
Output:
mike#linux-4puc:~> ./a.out
Starting driver test
Doing the IOCTL now... cmd: -2147195602
mike#linux-4puc:~> dmesg | tail
[75253.205136] In the ain_ioctl function, cmd: -1078168112. type: 117, dir: 2, nr: 208, size: 16316
[75253.205140] Testing against cmd: -2147195602. type: 101, dir: 2, nr: 46, size: 4
Anyone have any ideas on why my command is acting differently when I pass it to the kernel via the ioctl command vs when I just check the values by hard coding them (as I am doing in my prints)?
The only warnings I'm seeing when I build seem nothing to do with the ioctl calls:
makedepend: warning: ignoring option -Wall
makedepend: warning: ignoring option -Wall
makedepend: warning: ain.c (reading /usr/src/linux/include/linux/compiler-gcc.h), line 94: incomplete include == "#include gcc_header(__GNUC__)"
makedepend: warning: ain.c (reading /usr/src/linux/include/linux/string.h, line 13): cannot find include file "stdarg.h"
Thanks.
The -1078168112 (why aren't you printing these in hex?) looks like a stack pointer. Possibly &ioctl_par. This suggests that your ioctl method is receiving different parameters than you expected.
In the current kernel source I see ioctl methods taking 3 parameters, not 4. The 4-argument ioctl seems to be an older interface.
Do you get any warnings during the module compilation? Pay attention to them!
Alan Curry's answer was not the complete "correct" answer, but it lead me to the solution. The hex value of the command was way off so I took a look at other ioctl calls in the kernel.
The system I have is based off an older 2.4X kernel, and I'm updating it for 3.1. The issue here is the parameter list for the ioctl call. Having the inode pointer in the parameter list was causing the problem as it was taking the file pointer to be the command.
Proper solution:
long ain_ioctl (struct file * filp, unsigned int cmd, unsigned long arg) {
...
I want to preface this by saying that I've done very little programming in C, so I'd prefer to know why a given solution works rather than just what it is.
I'm trying to write a function which will take a pathname, and return a pathname to a different file in the same directory.
"/example/directory/with/image.png" => "/example/directory/with/thumbnail.png"
What I've tried after reading up on example uses of realpath and dirname (I'm working on Linux; if there's a cross-platform equivalent, let me know) is:
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
char *chop_path(char *orig) {
char buf[PATH_MAX + 1];
char *res, *dname, *thumb;
res = realpath(orig, buf);
if (res) {
dname = dirname(res);
thumb = strcat(dname, "/thumbnail.png");
return thumb;
}
return 0;
}
Compiling it seems to work, but running the program with
int main(void) {
char *res = chop_path("original.png");
if (res) {
printf("Resulting pathname: %s", res);
}
return 0;
}
gives me a segfault. Any hints?
The only problem I see is the signature of your chop_path routine; it should be
char *chop_path(char *orig) {
Your version has a missing *. That makes an enormous difference actually; without the *, you're effectively telling dirname and realpath to interpret the character code of the first character in your argument string as the numerical address (i.e., a pointer to) the path. That's going to point into a location in low memory that you definitely have not allocated; trying to use it results in that "segmentation fault" error, which means, effectively, that you're trying to touch memory you're not allowed to.
The other issue turned out to be that the dirname() function is declared in libgen.h, which you weren't including. If you don't include that header, the compiler assumes dirname() returns int instead of a pointer, and on a 64-bit architecture, the 64-bit return value from the function gets chopped down to 32 bits, a bad pointer is assigned to dname, and that's going to cause your seg fault right there.
If you don't want to use dirname, realpath, unwanted string buffer and string operations, etc - you can do the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define FILE_MAX 100
void chop_path(char path_name[], char new_file[]) {
int len = strlen(path_name);
int i;
for (i=len-1; i>0 ; i--) {
if (path_name[i] == '/') {
strcpy(path_name+i+1, new_file);
break;
}
}
return;
}
int main(void) {
char path[PATH_MAX + 1] = "/this/is/a/path/filename.c";
char new_file[FILE_MAX] = "newfilename.txt";
printf("old : %s \n", path);
chop_path(path, new_file);
printf("new : %s \n", path);
return 0;
}
Output:
$ gcc path.c
$ ./a.out
old : /this/is/a/path/filename.c
new : /this/is/a/path/newfilename.txt
$
I wrote a simple program and ran the program on ext4 and xfs.
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int
main(int argc, char *argv[])
{
int fd;
char *file_name = argv[1];
struct stat buf;
fd = open (file_name, O_RDWR|O_CREAT);
if (fd == -1) {
printf ("Error: %s\n", strerror(errno));
return -1;
}
write (fd, "hello", sizeof ("hello"));
fstat (fd, &buf);
printf ("st_blocks: %lu\n", buf.st_blocks);
stat (file_name, &buf);
printf ("st_blocks: %lu\n", buf.st_blocks);
close (fd);
stat (file_name, &buf);
printf ("st_blocks: %lu\n", buf.st_blocks);
return 0;
}
output on ext4:
st_blocks: 8
st_blocks: 8
st_blocks: 8
output on xfs:
st_blocks: 128
st_blocks: 128
st_blocks: 8
Then I explored about xfs and found an option for changing the extent size while running mkfs.xfs.
example: mkfs.xfs -r extsize=4096 /dev/sda1
But still I get the same output on XFS. Can anyone provide more insight on how to change the st_blocks. Thanks in advance.
I found the answer, posting the answer here so that others facing the problem can refer it.
mount -t xfs -o allocsize=4096 device mount-point
The allocsize option is used to tune the buffer size.
What you are seeing is xfs speculative preallocation, which is a heuristic which is used to avoid fragmentation of files as they grow.
For more info, see this FAQ entry.
You are correct that the "-o allocsize=XXX" option disables that heuristic. Your attempt at using "-r extsize=XXX" failed because that option is only for the realtime subvolume, which you are almost certainly not using.
I have to write a program in C which returns file size in blocks just like ls -s command.
Please help.
I tried using stat() function (st_blksize)...And I am unable to implement it.
My code looks like this
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
void main(int argc, char **argv)
{
DIR *dp;
struct dirent *dirp;
struct stat buf;
if(argc < 2)
{
dp = opendir(".");
}
if(dp == NULL)
{
perror("Cannot open directory ");
exit(2);
}
while ((dirp = readdir(dp)) != NULL)
{
printf("%s\n", dirp->d_name);
if (stat(".", &buf))
printf("%d ", buf.st_blksize);
}
closedir(dp);
exit(0);
}
It is giving error buf size is not declared. Don't know what is the problem.
Addition
Thanks for the correction. I included the <sys/stat.h> header file. Now it is giving a warning:
warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘__blksize_t’
I am new to C so can't make out what should be the possible solution.
You need to include the correct header:
#incude <sys/stat.h>
That declares the structure and associated functions.
Note that stat() returns zero on success, so your test needs changing (and, as #jsmchmier pointed out in a comment, the call to stat should probably use dirp->d_name rather than the string literal "."). Also, st_blksize is the size of the disk blocks, not the size of the file - that is st_size (measured in bytes).
POSIX says:
off_t st_size For regular files, the file size in bytes.
For symbolic links, the length in bytes of the
pathname contained in the symbolic link.
blksize_t st_blksize A file system-specific preferred I/O block size
for this object. In some file system types, this
may vary from file to file.
blkcnt_t st_blocks Number of blocks allocated for this object.
Note that old (very old) versions of Unix did not support st_blksize or st_blocks. I expect most current versions do.
Now it is giving a warning..warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘__blksize_t’
The chances are that __blksize_t is an unisgned integer type similar to size_t. I'd probably use a simple cast:
printf("Block size = %d\n", (int)buf.st_blksize);
Alternatively, if you have C99 available, you could use the facilities from <inttypes.h> to use a bigger size:
printf("Block size = %" PRIu64 "\n", (uint64_t)buf.st_blksize);
In practice, this is overkill; the block size is unlikely to exceed 2 GB this decade, so int is likely to be sufficient for the foreseeable future.
From man 2 stat on my Mac OS X box:
NAME
fstat, fstat64, lstat, lstat64, stat, stat64 -- get file status
SYNOPSIS
#include <sys/stat.h>
int
fstat(int fildes, struct stat *buf);
Note the #include <sys/stat.h> which you have not done. No doubt the actual layout of struct stat is defined in there, which is what your compiler is complaining about.
This is one aspect of the man pages which is not always discussed with beginners but is very useful indeed: the whole unix API is documented in them. Oh, it is not always the easiest place to find a function when you know what it should do but don't know what it is called, but all the answers are there.
Open the file, and stat/fstat it. The struct field st_blocks should contain the information you want. If you're dealing with a directory, use opendir, readdir, closedir (posix)... Just pointers to start your work.
EDIT
Add unistd.h and sys/stat.h. Then remember that stat return 0 on success, so
if (stat(dirp->d_name, &buf) == 0)
and I've changed "." to the name of the "element", which is what you wanted, I suppose. Another change is to use st_blocks and not st_blksize, which says how big is each block (e.g. 1024 or 4096 or...), and -s returns the size in number of blocks, not the size of a block.
The fragment of code is of course incomplete: if you pass an argument, dp is not initialized and even dp == NULL can fail, you shoud have nullified it before:
DIR *dp = NULL;
struct dirent *dirp = NULL;
Careful, one bug in your code is that dp points to garbage and is only initialised if argc is less than 2, but you still try to use it in your while loop and you also try to closedir it. If you invoke your application with any arguments at all, it will probably crash.
To avoid the warning, change the %d to %ld in the line: printf("%d ", buf.st_blksize);