fstat: st_atime and st_mtime not a member? - c

I am doing an fstat on my file descriptor and dumping that into a struct stat. I read the documentation for fstat (link below) and it claims there are members st_atime and st_mtime.
http://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/stat.h.html
GCC let's me compile, but stepping through GDB, I cannot print out those members (i.e. I can print every other member). GDB claims they don't exist.
In fact, when I print out the struct stat, st_atime is spelt st_atim (i.e. same thing with st_mtime). Then it looks like it's a tuple or something because it holds two values, tv_sec and tv_nsec.
Does anyone know why GDB is claiming they don't exist?
Also, does anyone know how to pass it to memcpy? I am using C90.
This is the line of code it complains about saying I can't pass a time_t in here. How would I cast it to make this line work?
memcpy(&temp.otar_adate, file_statistics.st_atime, OTAR_DATE_SIZE);
MY OS: CentOS

On Linux, at least certain versions, st_atime and some other time fields in struct stat are inside struct timespec and contain proper timestamps with full nanosecond precision. On those systems st_atime is a define to something else. On my CentOS machine it is defined to st_atim.tv_sec.
Throw your code into the preprocessor to see what it is on your system:
$ cat foo.c
#include <sys/stat.h>
void
foo(void)
{
struct stat st;
(void)st.st_atime;
}
$ cc -E foo.c | tail -7
void
foo(void)
{
struct stat st;
(void)st.st_atim.tv_sec;
}
Gdb doesn't know about preprocessor defines, so it can't know how your code got preprocessed. It only knows about the real definition of the struct.

Related

SUNPATHLEN on Linux. Where is it defined?

Recently I begun to port some my TCP code from FreeBSD to Linux. Already had a bunch of questions ;) So, here is another one.
The C struct sockaddr_un on Linux have some different definition than of that on FreeBSD. But, to the question. I have such code in my project:
}else if(AF_UNIX == domain){
if(SUNPATHLEN == strnlen(a, SUNPATHLEN)){
return -ENAMETOOLONG;
}
The above tests that Maximum path should be no more than SUNPATHLEN constant. The SUNPATHLEN is defined on FreeBSD, but apparently not on Linux.
Looking through the gcc -E source.c | grep -n4 sockaddr_un, the struct definition is following:
1724:struct sockaddr_un
1725- {
1726- sa_family_t sun_family;
1727- char sun_path[108];
1728- };
Here the length of a buffer is explicitly set to be of 108.
What is a general rule to check for buffer being trimmed/overflowed in Linux, for the case?
Looking through the gcc -E source.c | grep -n4 sockaddr_un, the struct definition is following:
You don't have to (and shouldn't) trawl the source for this kind of information. You should be looking at user-facing documentation to determine the interface characteristics on which you can rely. In this case, you're looking for unix(7):
A UNIX domain socket address is represented in the following
structure:
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* Pathname */
};
The sun_family field always contains AF_UNIX. On Linux, sun_path is
108 bytes in size
(emphasis added).
What is a general rule to check for buffer being trimmed/overflowed in
Linux, for the case?
No macro is defined for it, but the capacity of the path buffer is explicitly documented as 108 bytes. You can (and probably should) define your own macro for this if you're going to perform tests related to it.
You could possibly do some variation on this to remove system dependencies:
static struct sockaddr_un dummy_sockaddr_un_;
#define MY_SUN_PATH_SIZE (sizeof(dummy_sockaddr_un_.sun_path))

Compiling old C code Y2038 conform still results in 4 byte variables

According to this overview in order to compile Y2038 conform old code, we just need to add the preprocessor macro __USE_TIME_BITS64 to gcc, but that does not seem to work on an ARMv7 board with Debian 12 (bookworm):
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
struct stat sb;
printf("sizeof time_t: %zu\n", sizeof(time_t));
printf("sizeof stat timestamp: %zu\n", sizeof(sb.st_atime));
return 0;
}
time_t is still 4 bytes:
root#debian:~# gcc -D__USE_TIME_BITS64 time.c -o time
root#debian:~# ./time
sizeof time_t: 4
sizeof stat timestamp: 4
root#debian:~#
glibc is 2.33, what am I doing wrong here?
According to this post (which is getting a little old now, and some parts of which are probably no longer relevant):
... defining _TIME_BITS=64 would cause all time functions to use 64-bit times by default. The _TIME_BITS=64 option is implemented by transparently mapping the standard functions and types to their internal 64-bit variants. Glibc would also set __USE_TIME_BITS64, which user code can test for to determine if the 64-bit variants are available.
Presumably, this includes making time_t 64 bit.
So if your version of glibc supports this at all, it looks like you're setting the wrong macro. You want:
-D_TIME_BITS=64

readdir() 32/64 compatibility issues

I'm trying to get some old legacy code working on new 64-bit systems, and I'm currently stuck. Below is a small C file I'm using to test functionality that exists in the actual program that is currently breaking.
#define _POSIX_SOURCE
#include <dirent.h>
#include <sys/types.h>
#undef _POSIX_SOURCE
#include <stdio.h>
main(){
DIR *dirp;
struct dirent *dp;
char *const_dir;
const_dir = "/any/path/goes/here";
if(!(dirp = opendir(const_dir)))
perror("opendir() error");
else{
puts("contents of path:");
while(dp = readdir(dirp))
printf(" %s\n", dp->d_name);
closedir(dirp);
}
}
The Problem:
The OS is Red Hat 7.0 Maipo x86_64.
The legacy code is 32-bit, and must be kept that way.
I've gotten the compile for the program working fine using the -m32 flag with g++. The problem that arises is during runtime, readdir() gets a 64-bit inode and then throws an EOVERFLOW errno and of course nothing gets printed out.
I've tried using readdir64() in place of readdir() to some success. I no longer get the errno EOVERFLOW, and the lines come out on the terminal, but the files themselves don't get printed. I'm assuming this is due to the buffer not being what dirent expects.
I've attempted to use dirent64 to try to alleviate this problem but whenever I attempt this I get:
test.c:19:22 error: dereferencing pointer to incomplete type
printf(" %s\n", dp->d_name);
I'm wondering if there's a way to manually shift the dp->d_name buffer for dirent to be used with readdir(). I've noticed in Gdb that using readdir() and dirent results in dp->d_name having directories listed at dp->d_name[1], whereas readdir64() and dirent gives the first directory at dp->d_name[8].
That or somehow get dirent64 to work, or maybe I'm just on the wrong path completely.
Lastly, it's worth noting that the program functions perfectly without the -m32 flag included, so I'm assuming it has to be a 32/64 compatibility error somewhere. Any help is appreciated.
Thanks to #Martin in the comments above I was led to try defining the dirent64 struct in my code. This works. There's probably a #define that can be used to circumvent pasting libc .h code into my own code, but this works for now.
The code I needed was found in <bits/dirent.h>
I guess I should also note that this makes it work using both readdir64() and dirent64
In order to get a 64-bit ino_t with GCC and Glibc, you need to define the features _XOPEN_SOURCE and _FILE_OFFSET_BITS=64.
$ echo '#include <dirent.h>' | gcc -m32 -E -D_XOPEN_SOURCE -D_FILE_OFFSET_BITS=64 - | grep ino
__extension__ typedef unsigned long int __ino_t;
__extension__ typedef __u_quad_t __ino64_t;
typedef __ino64_t ino_t;
__ino64_t d_ino;
I say this from documentation reading and checking the preprocessor, not from deep experience or testing with a filesystem with inode numbers above 2^32, so I don't guarantee that you won't run into other problems down the line.

System call stat() converted to stat64() without any cpp option

I am running 32-bit SUSE Linux with kernel level 3.0.76.
I can see a stat() call in my code translate to stat64() in the strace output, without me specifying any CPP options like _LARGEFILE64_SOURCE or _FILE_OFFSET_BITS=64.
#include<stdio.h>
#include<sys/stat.h>
int main ( int argc, char * argv[] )
{
char * path = "nofile";
struct stat b;
if (stat(path, &b) != 0) {
}
}
I compiled this file with gcc with no compiler options/flags.
On running the program, relevant strace output is:
munmap(0xb770a000, 200704) = 0
stat64("nofile", 0xbfb17834) = -1 ENOENT (No such file or directory)
exit_group(-1)
Can anyone please tell me how the stat() was converted to stat64() ?
Thanks in advance!
The answer seems to be found in the stat man page
Over time, increases in the size of the stat structure have led to
three successive versions of stat(): sys_stat() (slot __NR_oldstat),
sys_newstat() (slot __NR_stat), and sys_stat64() (new in kernel 2.4;
slot __NR_stat64). The glibc stat() wrapper function hides these
details from applications, invoking the most recent version of the
system call provided by the kernel, and repacking the returned
information if required for old binaries. Similar remarks apply for
fstat() and lstat().
Basically, glibc always call stat64.
If you add a printf("%zu\n", sizeof b); , the struct stat sizes are likely different depending on whether you use _FILE_OFFSET_BITS=64 or not, and glibc converts the struct stat between the kernel and your code.
I can see a stat() call in my code translate to stat64() in the strace output
You need to analyze the way syscall stat is wrapped on your system http://sourceware.org/glibc/wiki/SyscallWrappers (one way that you can see this code is to use gdb: execute this command under gdb: catch syscall stat and when you program will stop on this breakpoint get backtrace and analyze the function that called stat).
From http://linux.die.net/man/2/stat64
Over time, increases in the size of the stat structure have led to
three successive versions of stat(): sys_stat() (slot __NR_oldstat),
sys_newstat() (slot __NR_stat), and sys_stat64() (new in kernel 2.4;
slot __NR_stat64). The glibc stat() wrapper function hides these
details from applications, invoking the most recent version of the
system call provided by the kernel, and repacking the returned
information if required for old binaries.
So it is glibc_wrapper that decides based on sizeof(struct stat) that it should call why stat64.

uses undefined struct compile error - C

The compiler doesn't know where stat.h is?
Error:
c:\Projects\ADC_HCI\mongoose.c(745) : error C2079: 'st' uses undefined struct '_stat64'
#include <sys/types.h>
#include <sys/stat.h>
static int
mg_stat(const char *path, struct mgstat *stp)
{
struct _stat64 st; //<-- ERROR
int ok;
wchar_t wbuf[FILENAME_MAX];
to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
if (_wstat64(wbuf, &st) == 0) {
ok = 0;
stp->size = st.st_size;
stp->mtime = st.st_mtime;
stp->is_directory = S_ISDIR(st.st_mode);
} else {
ok = -1;
}
return (ok);
}
...downloaded the files straight from the source.
See MSDN: _wstat64 takes a parameter of struct __stat64 (with two underscores). Redeclare your variable st to be of type struct __stat64.
Note that neither _stat64 nor __stat64 is 'standard' in the sense of documented by any standard, such as POSIX. You would normally use struct stat; if you are worried about whether that will work with big files (over 2 GiB), then check what compilation options are required on your platform to obtain 'large file support'. For 64-bit machines and 64-bit compilations (not necessarily Windows 64), you usually don't need to worry. You can often obtain large file support using:
-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
These are at least semi-standardized. Systems such as autoconf detect these things automatically (if you ask them to do so).
Change the _stat64 to stat64. At least in my Linux machines that's the name of the structure. I don't know if it is different in Windows.
I suggest you to sync to SVN trunk.
If you don't have SVN client, simply download two files:
http://mongoose.googlecode.com/svn/trunk/mongoose.h (and .c file too)
The reason is that recently the code was refactored, and CRT _stat function was substituted
with WinAPI one, GetFileAttributesExW().

Resources