Function that returns file size? - c

int filesize(FILE * handle)
{
int filesize;
int old_pointer = ftell(handle);
fseek(handle, 0, SEEK_END);
filesize = ftell(handle);
fseek(handle, old_pointer, SEEK_SET);
return filesize;
}
Is this a good way for a function to return the size of a file?

It is one way to do it, as long as your files aren't too big (which, for a 32-bit system or Windows 64-bit, means not more than 2 GiB). It has the merit of more or less working on any platform. Of course, ftell() returns a long, not an int (so on a 64-bit non-Windows system, the files it can report on are much bigger, as long as you fix your code appropriately). However, requiring four function calls is a trifle expensive.
The POSIX alternatives are stat(), lstat() and fstat().
There will be analogues in the Windows API.

I'd personally use a function from the stat family, like so. Also, note that int may be too small for the return value (especially on 32-bit systems); off_t is guaranteed to work.
off_t filesize(FILE *handle) {
struct stat statbuf;
if (fstat(fileno(handle), &statbuf) != 0) {
// handle an error
} else {
return statbuf.st_size;
}
}
Note also that this can be easily tweaked to work for files that aren't open yet by using standard stat() instead of fstat().

You could use stat() or fstat() if you were on *nix.

Use stat:
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
---> off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};

Related

C open() directory no error

I have a function that opens file then read theses files, with the standard open() et read() functions. I use the errno variables for all possible errors such as permission denied or no such file or directory, but when I use open on a directory it don't return a error it simply open it and try to read it.
So how can I get an error message when I give open a directory ?
Directory is also a file (In terms of Unix/Linux). So generally you won't get an error. You can use stat or fstat function for tracking of ordinary or special files.
When you use either stat or fstat, you need to declare a struct stat variable like struct stat var. The stat structure has a member called st_mode which has the information of what kind of file it is.
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
Directory also a file in Unix*, if you want to avoid to open a directory by open(), you may need to check the file type. As #nayabbashasayed say,you can use the stat to check the file type and more info.
Here is an example code to check the type by stat:
const char *filePath;
struct stat fileStatBuf;
if(stat(filePath,&fileStatBuf) == -1){
perror("stat");
exit(1);
}
/*
*You can use switch to check all
*the types you want, now i just use
*if to check one.
*/
if((fileStatBuf.st_mode & S_IFMT) == S_IFDIR){
printf("This type is directory\n");
}
Hope that can help you.
I don't wanted to use anything else as read and write, I found that the error is returned at using read() not by open()

How to get last modified time of a file in OS X/ C?

I have the following code:
struct stat info;
stat("/Users/john/test.txt", &info);
printf("%s\n", ctime(&info.st_mtimespec));
in which I am trying to get the last modified time of the file as displayed in the ls -l command in the format:
Jan 29 19:39
Unfortunately, above code doesn't work.I get the following error message on xcode:
Conflicting types for ctime
How can I fix it ? If there any alternative approaches to get the time in format mentioned, please do mention.
Checking on struct stat's declaration you'll see that st_mtimespec must be st_mtime.
Then, basing on this question, I rearranged your code like this:
struct stat info;
struct tm* tm_info;
char buffer[16];
stat("/Users/john/test.txt", &info);
tm_info = localtime(&info.st_mtime);
strftime(buffer, sizeof(buffer), "%b %d %H:%M", tm_info);
printf("%s\n", buffer);
Hope it helps.
I believe this is what you are looking for:
#include <sys/stat.h>
#include <time.h>
int main(int argc, char *argv[])
{
struct stat info;
stat("sample.txt", &info);
printf("%.12s\n", 4+ctime(&info.st_mtimespec));
return 0;
}
Output( same as time field of ls -l):
Feb 4 00:43
(This was for a random file on my computer).
does your code have:
#include <time.h>
also, the ctime() function expects the passed parameter to be a POINTER to a time_t.
Here is the struct pointed to by the stat() function:
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
Notice that none of the fields are a st_mtimespec
perhaps you meant st_mtime
Note: your running OS-X and I'm running linux, but the OS-X should have the very same definitions of field names.

How to compare nlink_t to int

I use stat system call on Linux and retrieve file information.
char *parent_dir; // for example: /run/atd.pid/
struct stat buf;
stat(parent_dir, &buf);
buf structure type:
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
I get number of hard links like this: buf.st_nlink.
My problem is that I can't compare number of hard links to integer value. I've tried to initialize another nlink_t and then compare my variable to stat variable, but it doesn't work. I've also tried this link.
Alternative way to cast nlink_t to int, but it doesn't work. always returns the same number.
int
parse_to_int(nlink_t *source)
{
int buffer_size = sizeof(*source);
char buffer[buffer_size];
snprintf(&buffer[0], buffer_size, "%lu", (unsigned long)source);
int val = atoi(buffer);
return val;
}
Any idea?
Program output when I use parse_to_int function:
get stat for: /run/nm-dhclient-wlan0.conf/
nlink_t: 321
get stat for: /run/wpa_supplicant/
nlink_t: 321
get stat for: /run/udisks2/
nlink_t: 321
get stat for: /run/nm-dns-dnsmasq.conf/
nlink_t: 321
...
nlink_t is typedef'd as an integer type (e.g., unsigned short or unsigned int), so you should be able to cast stat.st_nlink to an unsigned or unsigned long without the compiler complaining.
Your parse_to_int() function is incorrect, because you're casting a pointer (nlink_t*) to an unsigned int, instead of the value of the nlink_t variable. But you don't need the function, just use the cast properly.
Addendum
Also make sure that you're not comparing an unsigned type to -1, which will give you unexpected results.
So I had several problems:
You can't get stat from file, if the path ends with /
There are some folders in your computer's folder list that are created by your operating system. Some kind of data in memory, interpreted as folders and files(called virtual file-system). So if you try to get i-node number from this kind of file, you get some garbage value.
You should be able to compare to a uint. If you've got more than 64k hard links I'd be surprised.
Trying to guess the size it will take to print a string representation of a nlink_t from how many bytes long the nlink_t is a bug. nlink_t may be 8 bytes but 4000000000 is more than 8 bytes of digits printed out.
The right way to find exactly the right size is to check the return value of snprintf, it'll tell you how large a buffer was actually needed (or you could punt and allocate something larger than 8. sizeof(*source) isn't a solid solution.
int size = snprintf(NULL, 0, "%lu", (unsigned long)source);
buffer = new chara[size + 1]; //+1 for a NULL
int size = snprintf(buffer, size, "%lu", (unsigned long)source);
Also, the number of hard links should be consistent unless your file system is changing. Take a look at the output of
tmp]$ ls -ld /home/
drwxr-xr-x. 5 root root 4096 Feb 3 2012 /home/
That number before the owner is the number of hard links, 5 in this case.

Is it possible getting and setting file name with struct stat descriptor?

Is it possible at get or set ( rename ) a file name with giving the absolute path of file, and a struct stat instance as parameters to lstat function. As I find in documentation struct seems like that;
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
What I want is something like that;
struct stat *s;
char *path; // assigning the ablosute path of file
int res = lstat(path, s);
if(res == -1)
return errno;
char *name = s->(someFielAboutFileName);
or
s->(someFieldAboutFileName) = name; // setting name
No. In unix, the name is not an inherent property of the file. A file can have multiple names (see hard links) or even none.
The names are just entries in directories.
In Unix, the name of the file belongs to the directory, not the file. Meaning, the same file can have multiple names (it's called "hard links" in Unix). This also means you can not find out the name from stat, since it deals with files, not names. You can use realpath to find real name of the file, with resolving symlinks, etc.
It seems that you want to modify a symbolic link. lstat() won't help you with that, as it only returns the status of the link, not even the name of the file it points to, and cannot be used to modify anything about the file anyway.
You'll probably have to unlink() the symbolic link, then call symlink() to recreate it and make it point to the other file:
unlink(path);
symlink(name, path);

How can I get exact total space of a drive using C language program running on Linux?

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);

Resources