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

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

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

What is lstat() alternative in windows?

In linux, when stat() is used with broken link files, it fails with -1. So I used lstat() which succeeded.
For the same case in windows, _stat() fails with broken shortcuts, but there is no _lstat() in windows. Please help to find the alternative for lstat() in windows.
The accepted answer does not provide a full stat equivalent. The stat struct is defined as
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 */
};
but GetFileAttributes.. does not provide any owner information (it returns data in a WIN32_FIND_DATA object). If you need that owner information, it looks like you can use GetSecurityInfo [1].
[1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa446629%28v=vs.85%29.aspx
GetFileAttributes or GetFileAttributesEx probably (if I understood stat and lstat right). Quoting from the docs:
Symbolic link behavior—If the path points to a symbolic link, the function returns attributes for the symbolic link.
hey _stat() or stat() works fine on broken shortcuts as well. Thats the reason,there is no alternative like lstat(UNIX) in windows.
Where in Unix, stat() fails with broken links, so lstat is provided to fix the problem.
Thank you all for your help.

How to get file creation date in Linux?

I am working with batches of files that contain information about the same object at the different times of its life, and the only way to order them is by creation date.
I was using this:
//char* buffer has the name of file
struct stat buf;
FILE *tf;
tf = fopen(buffer,"r");
//check handle
fstat(tf, &buf);
fclose(tf);
pMyObj->lastchanged=buf.st_mtime;
But that does not seems to work.
What am I doing wrong? Are there other, more reliable/simple ways to get file creation date under Linux?
The nearest approximation to 'creation date' is the st_ctime member in the struct stat, but that actually records the last time the inode changed. If you create the file and never modify its size or permissions, that works as a creation time. Otherwise, there is no record of when the file was created, at least in standard Unix systems.
For your purposes, sort by st_mtime...or get the files named with a timestamp in the name.
Note that if you are on Darwin (Mac OS X), the creation time is available. From the man page for stat(2):
However, when the macro _DARWIN_FEATURE_64_BIT_INODE is defined, the stat structure will now be defined as:
struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is defined */
dev_t st_dev; /* ID of device containing file */
mode_t st_mode; /* Mode of file (see below) */
nlink_t st_nlink; /* Number of hard links */
ino_t st_ino; /* File serial number */
uid_t st_uid; /* User ID of the file */
gid_t st_gid; /* Group ID of the file */
dev_t st_rdev; /* Device ID */
struct timespec st_atimespec; /* time of last access */
struct timespec st_mtimespec; /* time of last data modification */
struct timespec st_ctimespec; /* time of last status change */
struct timespec st_birthtimespec; /* time of file creation(birth) */
off_t st_size; /* file size, in bytes */
blkcnt_t st_blocks; /* blocks allocated for file */
blksize_t st_blksize; /* optimal blocksize for I/O */
uint32_t st_flags; /* user defined flags for file */
uint32_t st_gen; /* file generation number */
int32_t st_lspare; /* RESERVED: DO NOT USE! */
int64_t st_qspare[2]; /* RESERVED: DO NOT USE! */
};
Note the st_birthtimespec field. Note, too, that all the times are in struct timespec values, so there is sub-second timing (tv_nsec gives nanosecond resolution). POSIX 2008 <sys/stat.h> requires the struct timespec time keeping on the standard times; Darwin follows that.
fstat works on file descriptors, not FILE structures. The simplest version:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef HAVE_ST_BIRTHTIME
#define birthtime(x) x.st_birthtime
#else
#define birthtime(x) x.st_ctime
#endif
int main(int argc, char *argv[])
{
struct stat st;
size_t i;
for( i=1; i<argc; i++ )
{
if( stat(argv[i], &st) != 0 )
perror(argv[i]);
printf("%i\n", birthtime(st));
}
return 0;
}
You will need to figure out if your system has st_birthtime in its stat structure by inspecting sys/stat.h or using some kind of autoconf construct.
File creation time is not stored anywhere, you can only retrieve one of the following:
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 */
Your code should give you the last modification time, however. Note: you can use stat() instead of fstat() without opening the file (stat() takes the file name as param).
To get the file creation date in linux, I use the following method
root#sathishkumar# cat << _eof > test.txt
> Hello
> This is my test file
> _eof
root#sathishkumar# cat test.txt
Hello
This is my test file
root#sathishkumar# ls -i test.txt
2097517 test.txt
root#sathishkumar# debugfs -R 'stat <2097517>' /dev/sda5
Inode: 2097517 Type: regular Mode: 0664 Flags: 0x80000
Generation: 4245143992 Version: 0x00000000:00000001
User: 1000 Group: 1000 Size: 27
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 8
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x50ea6d84:4826cc94 -- Mon Jan 7 12:09:00 2013
atime: 0x50ea6d8e:75ed8a04 -- Mon Jan 7 12:09:10 2013
mtime: 0x50ea6d84:4826cc94 -- Mon Jan 7 12:09:00 2013
crtime: 0x5056d493:bbabf49c -- Mon Sep 17 13:13:15 2012
Size of extra inode fields: 28
EXTENTS:
(0):8421789
atime: Last time file was opened or executed
ctime: Time the inode information was updated. ctime also gets updated when file is modified
mtime: Last modified time
crtime: File creation time
For recent Linux versions, there is xstat which includes btime. https://unix.stackexchange.com/a/407305/103120

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