How to compare nlink_t to int - c

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.

Related

how to use lstat() to determine if hard link or not

My OS is linux. I program in C. I know I can use the lstat() to recognize the soft link, i.e., use S_ISLNK(st.st_mode). But how can I recognize the link is hard link? if the link is hard link, it will be thought of as regular file. However, I also want to distinguish the regular file from the hard link. Are there any ways to handle this case?
But how can I recognize the link is hard link?
You can't.
A "hard link" isn't actually anything special. It's just a directory entry that happens to point to the same data on disk as a directory entry somewhere else. The only way to reliably identify hard links is to map all the paths on your filesystem to inodes, and then see which ones point to the same value.
struct stat has st_nlink member for number of hard links. It is > 1, file is being stated in one of the hard links to actual file content.
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 */
};
Here is sample program:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main()
{
struct stat buf = {0};
lstat("origfile", &buf);
printf("number of hard links for origfile: %d\n", buf.st_nlink);
}
output:
$ touch origfile
$ ./a.out
number of hard links for origfile: 1
$ ln origfile hardlink1
$ ./a.out
number of hard links for origfile: 2
$ ln origfile hardlink2
$ ./a.out
number of hard links for origfile: 3

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.

Function that returns file size?

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 */
};

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

Is there any C function to get the directory permission?

I am trying to implement the ls command in C without using the system function.
Is there a C function to get the directory/file permissions, so that I can display them?
Thanks.
The stat() system call takes a filename string and returns the following structure:
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 */
};
This works equally well on directory entries as well as files and the st_mode is the specific field you're looking for.
The <sys/stat.h> header file should contain that structure and all the definitions along with #defines and/or macros for intelligently decoding the fields (see here for a sample).
If you're interested in how it's really done, you can look at the ls source code in GNU coreutils. But only use that to educate yourself. It's GPL so you can't just grab it for your own purposes and, if this is homework, your educator will almost certainly fail you if you turn in something that looks too similar to this.
Look into stat(). Sounds like you're on a Linux or POSIX system, so that should be the way.
Then look at the st_mode field of the struct stat, it contains the information about protection bits, which are often collectively called a file's "mode" (as reflected by the chmod command that changes the settings).
Going from the binary bits to a textual representation like ls' is ... an interesting challenge.
The stat functions family (stat(), lstat(), fstat()).

Resources