664 lines
14 KiB
C
664 lines
14 KiB
C
|
/* Copyright (C) 2005 David Decotigny
|
||
|
Copyright (C) 2003 Thomas Petazzoni
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License
|
||
|
as published by the Free Software Foundation; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||
|
USA.
|
||
|
*/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @file crt.c
|
||
|
*
|
||
|
* The C RunTime environment for the basic support of SOS C user
|
||
|
* programs
|
||
|
*/
|
||
|
|
||
|
#include <hwcore/swintr.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "libc.h" /* putenv */
|
||
|
#include "crt.h"
|
||
|
|
||
|
/**
|
||
|
* Macro used to retrieve the 2 parameters passed to any new thread
|
||
|
* (architecture-dependent)
|
||
|
*/
|
||
|
#define GET_THREAD_PARAMETERS(param1, param2) \
|
||
|
asm volatile ("movl %%eax, %0 ; movl %%ebx, %1" \
|
||
|
: "=g"(param1), "=g"(param2) : : "%eax", "%ebx" )
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Helper function to retrieve the arg array from the parameter area
|
||
|
* sent by the parent.
|
||
|
*
|
||
|
* Format of the parent's args area:
|
||
|
* Number of arguments
|
||
|
* Offset of arg 0 (program's name)
|
||
|
* Offset of arg 1
|
||
|
* ...
|
||
|
* Offset of arg N (last argument)
|
||
|
* NULL
|
||
|
* Offset of environnement variable 0
|
||
|
* Offset of environnement variable 1
|
||
|
* ...
|
||
|
* Offset of environnement variable N
|
||
|
* NULL
|
||
|
* Contents of arg 0 with terminal \0
|
||
|
* Contents of arg 1 with terminal \0
|
||
|
* ...
|
||
|
* Contents of arg N with terminal \0
|
||
|
* Contents of env 0 with terminal \0
|
||
|
* Contents of env 1 with terminal \0
|
||
|
* ...
|
||
|
* Contents of env N with terminal \0
|
||
|
*
|
||
|
* This function simply transforms the N offsets into the real
|
||
|
* addresses of the arg X contents (ie it simply adds the base address
|
||
|
* of the area).
|
||
|
*/
|
||
|
static void unmarshall_argv_envp(void * args,
|
||
|
int * argc,
|
||
|
const char ** * argv,
|
||
|
const char ** * envp)
|
||
|
{
|
||
|
addr_t * offset = (addr_t*) args;
|
||
|
*argc = 0;
|
||
|
*argv = NULL;
|
||
|
|
||
|
offset = args;
|
||
|
|
||
|
/* Get argc */
|
||
|
*argc = * (int*) offset;
|
||
|
offset ++;
|
||
|
|
||
|
/* Get base of argv */
|
||
|
*argv = (const char **) offset;
|
||
|
|
||
|
/* Walk the offsets array and transform these offsets into memory
|
||
|
addresses */
|
||
|
for ( ; 0 != *offset ; offset ++)
|
||
|
*offset += (addr_t) args;
|
||
|
|
||
|
/* Skip the NULL separating argv from envp */
|
||
|
offset ++;
|
||
|
|
||
|
/* Get base of envp */
|
||
|
*envp = (const char **) offset;
|
||
|
|
||
|
/* Walk the offsets array and transform these offsets into memory
|
||
|
addresses */
|
||
|
for ( ; 0 != *offset ; offset ++)
|
||
|
{
|
||
|
*offset += (addr_t) args;
|
||
|
|
||
|
/* Register this environment variable */
|
||
|
putenv((char*) *offset);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Starter function !
|
||
|
*/
|
||
|
/** Function called by crt_asm.S:_start to start user program */
|
||
|
void _cmain(const char* arg_env_area[]) __attribute__((noreturn));
|
||
|
void _cmain(const char* arg_env_area[])
|
||
|
{
|
||
|
|
||
|
/* Will hold the parameters that will be passed to main */
|
||
|
const char** argv;
|
||
|
const char** envp;
|
||
|
int argc;
|
||
|
|
||
|
|
||
|
/* This starter function expects a main() function somewhere */
|
||
|
extern int main(int argc, char const* argv[],
|
||
|
char const* envp[]);
|
||
|
|
||
|
/* Setup the arguments list and the environment variables */
|
||
|
unmarshall_argv_envp (arg_env_area, & argc, & argv, & envp);
|
||
|
|
||
|
_sos_exit(main(argc, argv, envp));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* By convention, the USER SOS programs always pass 4 arguments to the
|
||
|
* kernel syscall handler: in eax/../edx. For less arguments, the
|
||
|
* unused registers are filled with 0s. For more arguments, the 4th
|
||
|
* syscall parameter gives the address of the array containing the
|
||
|
* remaining arguments. In any case, eax corresponds to the syscall
|
||
|
* IDentifier.
|
||
|
*/
|
||
|
|
||
|
|
||
|
inline
|
||
|
int _sos_syscall3(int id,
|
||
|
unsigned int arg1,
|
||
|
unsigned int arg2,
|
||
|
unsigned int arg3)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
asm volatile("movl %1,%%eax \n"
|
||
|
"movl %2,%%ebx \n"
|
||
|
"movl %3,%%ecx \n"
|
||
|
"movl %4,%%edx \n"
|
||
|
"int %5\n"
|
||
|
"movl %%eax, %0"
|
||
|
:"=g"(ret)
|
||
|
:"g"(id),"g"(arg1),"g"(arg2),"g"(arg3)
|
||
|
,"i"(SOS_SWINTR_SOS_SYSCALL)
|
||
|
:"eax","ebx","ecx","edx","memory");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_syscall0(int id)
|
||
|
{
|
||
|
return _sos_syscall3(id, 0, 0, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_syscall1(int id,
|
||
|
unsigned int arg1)
|
||
|
{
|
||
|
return _sos_syscall3(id, arg1, 0, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_syscall2(int id,
|
||
|
unsigned int arg1,
|
||
|
unsigned int arg2)
|
||
|
{
|
||
|
return _sos_syscall3(id, arg1, arg2, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_syscall4(int id,
|
||
|
unsigned int arg1,
|
||
|
unsigned int arg2,
|
||
|
unsigned int arg3,
|
||
|
unsigned int arg4)
|
||
|
{
|
||
|
unsigned int args[] = { arg3, arg4 };
|
||
|
return _sos_syscall3(id, arg1, arg2, (unsigned)args);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_syscall5(int id,
|
||
|
unsigned int arg1,
|
||
|
unsigned int arg2,
|
||
|
unsigned int arg3,
|
||
|
unsigned int arg4,
|
||
|
unsigned int arg5)
|
||
|
{
|
||
|
unsigned int args[] = { arg3, arg4, arg5 };
|
||
|
return _sos_syscall3(id, arg1, arg2, (unsigned)args);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_syscall6(int id,
|
||
|
unsigned int arg1,
|
||
|
unsigned int arg2,
|
||
|
unsigned int arg3,
|
||
|
unsigned int arg4,
|
||
|
unsigned int arg5,
|
||
|
unsigned int arg6)
|
||
|
{
|
||
|
unsigned int args[] = { arg3, arg4, arg5, arg6 };
|
||
|
return _sos_syscall3(id, arg1, arg2, (unsigned)args);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_syscall7(int id,
|
||
|
unsigned int arg1,
|
||
|
unsigned int arg2,
|
||
|
unsigned int arg3,
|
||
|
unsigned int arg4,
|
||
|
unsigned int arg5,
|
||
|
unsigned int arg6,
|
||
|
unsigned int arg7)
|
||
|
{
|
||
|
unsigned int args[] = { arg3, arg4, arg5, arg6, arg7 };
|
||
|
return _sos_syscall3(id, arg1, arg2, (unsigned)args);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_syscall8(int id,
|
||
|
unsigned int arg1,
|
||
|
unsigned int arg2,
|
||
|
unsigned int arg3,
|
||
|
unsigned int arg4,
|
||
|
unsigned int arg5,
|
||
|
unsigned int arg6,
|
||
|
unsigned int arg7,
|
||
|
unsigned int arg8)
|
||
|
{
|
||
|
unsigned int args[] = { arg3, arg4, arg5, arg6, arg7, arg8 };
|
||
|
return _sos_syscall3(id, arg1, arg2, (unsigned)args);
|
||
|
}
|
||
|
|
||
|
|
||
|
void _sos_exit(int status)
|
||
|
{
|
||
|
_sos_syscall1(SOS_SYSCALL_ID_EXIT, (unsigned)status);
|
||
|
|
||
|
/* Never reached ! */
|
||
|
for ( ; ; )
|
||
|
;
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_bochs_write(const char * str, unsigned length)
|
||
|
{
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_BOCHS_WRITE,
|
||
|
(unsigned)str,
|
||
|
length);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_fork()
|
||
|
{
|
||
|
return _sos_syscall0(SOS_SYSCALL_ID_FORK);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_getpid()
|
||
|
{
|
||
|
return _sos_syscall0(SOS_SYSCALL_ID_GETPID);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_exec(const char * prog,
|
||
|
void const* args,
|
||
|
size_t arglen)
|
||
|
{
|
||
|
return _sos_syscall4(SOS_SYSCALL_ID_EXEC, (unsigned int)prog,
|
||
|
(unsigned int)strlen(prog),
|
||
|
(unsigned int)args,
|
||
|
(unsigned int)arglen);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_munmap(void * start, size_t length)
|
||
|
{
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_MUNMAP,
|
||
|
(unsigned int)start,
|
||
|
length);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_mprotect(const void *addr, size_t len, int prot)
|
||
|
{
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_MPROTECT,
|
||
|
(unsigned int)addr,
|
||
|
len,
|
||
|
(unsigned int)prot);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_mresize(void * old_addr, size_t old_len,
|
||
|
void * *new_addr, size_t new_len,
|
||
|
unsigned long flags)
|
||
|
{
|
||
|
return _sos_syscall5(SOS_SYSCALL_ID_MRESIZE,
|
||
|
(unsigned int)old_addr,
|
||
|
old_len,
|
||
|
(unsigned int)new_addr,
|
||
|
new_len,
|
||
|
flags);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_msync(void *start, size_t length, int flags)
|
||
|
{
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_MSYNC,
|
||
|
(unsigned int)start,
|
||
|
length,
|
||
|
flags);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Helper function that represents the start routine of a new user
|
||
|
* thread created from user space (syscall new_thread). It takes 2
|
||
|
* arguments that are passsed in the eax/ebx registers (@see
|
||
|
* cpu_ustate_init() in hwcore/cpu_context.c): the start function of
|
||
|
* the new thread (eax), the argument passed to it (ebx).
|
||
|
*/
|
||
|
static void thread_routine(void)
|
||
|
{
|
||
|
sos_thread_func_t * func;
|
||
|
unsigned long int arg;
|
||
|
|
||
|
GET_THREAD_PARAMETERS(func, arg);
|
||
|
|
||
|
func(arg);
|
||
|
_sos_exit(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_new_thread(sos_thread_func_t *func,
|
||
|
void* arg,
|
||
|
size_t stack_size)
|
||
|
{
|
||
|
return _sos_syscall4(SOS_SYSCALL_ID_NEW_THREAD,
|
||
|
(unsigned)thread_routine,
|
||
|
(unsigned)func, (unsigned)arg,
|
||
|
stack_size);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_nanosleep(unsigned long int sec,
|
||
|
unsigned long int nanosec)
|
||
|
{
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_NANOSLEEP,
|
||
|
sec, nanosec);
|
||
|
}
|
||
|
|
||
|
|
||
|
void * _sos_brk(void * new_top_address)
|
||
|
{
|
||
|
return (void*)_sos_syscall1(SOS_SYSCALL_ID_BRK,
|
||
|
(unsigned)new_top_address);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_mount(const char *source, const char *target,
|
||
|
const char *filesystemtype, unsigned long mountflags,
|
||
|
const char *args)
|
||
|
{
|
||
|
if (!target || !filesystemtype)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall7(SOS_SYSCALL_ID_MOUNT,
|
||
|
(unsigned int)source, source?strlen(source):0,
|
||
|
(unsigned int)target, strlen(target),
|
||
|
(unsigned int)filesystemtype,
|
||
|
mountflags,
|
||
|
(unsigned int)args);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_umount(const char *target)
|
||
|
{
|
||
|
if (!target)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_UMOUNT,
|
||
|
(unsigned int)target,
|
||
|
strlen(target));
|
||
|
}
|
||
|
|
||
|
|
||
|
void _sos_sync(void)
|
||
|
{
|
||
|
_sos_syscall0(SOS_SYSCALL_ID_SYNC);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_statvfs(const char *path, struct statvfs *buf)
|
||
|
{
|
||
|
if (! path)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_VFSTAT64,
|
||
|
(unsigned)path,
|
||
|
strlen(path),
|
||
|
(unsigned)buf);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_open(const char * pathname, int flags, int mode)
|
||
|
{
|
||
|
if (! pathname)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall4(SOS_SYSCALL_ID_OPEN,
|
||
|
(unsigned)pathname,
|
||
|
strlen(pathname),
|
||
|
flags,
|
||
|
mode);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_close(int fd)
|
||
|
{
|
||
|
return _sos_syscall1(SOS_SYSCALL_ID_CLOSE, fd);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_read(int fd, char * buf, size_t * len)
|
||
|
{
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_READ, fd,
|
||
|
(unsigned int) buf,
|
||
|
(unsigned int) len);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_write(int fd, const char * buf, size_t * len)
|
||
|
{
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_WRITE, fd,
|
||
|
(unsigned int) buf,
|
||
|
(unsigned int) len);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_seek64(int fd, loff_t * offset, int whence)
|
||
|
{
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_SEEK64, fd,
|
||
|
(unsigned int)offset,
|
||
|
(unsigned int)whence);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_fmmap(void ** ptr_hint_addr, size_t len, int prot, int flags,
|
||
|
int fd, loff_t offset)
|
||
|
{
|
||
|
return _sos_syscall7(SOS_SYSCALL_ID_FSMMAP,
|
||
|
(unsigned int)ptr_hint_addr, len, prot, flags,
|
||
|
(unsigned int)fd,
|
||
|
/* offs64_hi */(offset >> 32),
|
||
|
/* offs64_lo */(offset & 0xffffffff));
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_ftruncate64(int fd, loff_t length)
|
||
|
{
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_FTRUNCATE64, fd,
|
||
|
(unsigned int)length);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_fcntl(int fd, int cmd, int arg)
|
||
|
{
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_FCNTL, fd,
|
||
|
(unsigned int)cmd,
|
||
|
(unsigned int)arg);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_ioctl(int fd, int cmd, int arg)
|
||
|
{
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_IOCTL, fd,
|
||
|
(unsigned int)cmd,
|
||
|
(unsigned int)arg);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_creat(const char *pathname, int mode)
|
||
|
{
|
||
|
if (! pathname)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_CREAT,
|
||
|
(unsigned int)pathname,
|
||
|
strlen(pathname),
|
||
|
mode);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_link (const char *oldpath, const char *newpath)
|
||
|
{
|
||
|
if (!oldpath || !newpath)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall4(SOS_SYSCALL_ID_LINK,
|
||
|
(unsigned int)oldpath,
|
||
|
strlen(oldpath),
|
||
|
(unsigned int)newpath,
|
||
|
strlen(newpath));
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_unlink(const char *pathname)
|
||
|
{
|
||
|
if (! pathname)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_UNLINK,
|
||
|
(unsigned int)pathname,
|
||
|
strlen(pathname));
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_rename (const char *oldpath, const char *newpath)
|
||
|
{
|
||
|
if (!oldpath || !newpath)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall4(SOS_SYSCALL_ID_RENAME,
|
||
|
(unsigned int)oldpath,
|
||
|
strlen(oldpath),
|
||
|
(unsigned int)newpath,
|
||
|
strlen(newpath));
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_symlink(const char *target, const char *path)
|
||
|
{
|
||
|
if (!path || !target)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall4(SOS_SYSCALL_ID_SYMLINK,
|
||
|
(unsigned int)path,
|
||
|
strlen(path),
|
||
|
(unsigned int)target,
|
||
|
strlen(target));
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_mknod(const char *pathname, mode_t mode,
|
||
|
int type,
|
||
|
unsigned int major, unsigned minor)
|
||
|
{
|
||
|
if (!pathname)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall6(SOS_SYSCALL_ID_MKNOD,
|
||
|
(unsigned int)pathname,
|
||
|
strlen(pathname),
|
||
|
type, mode, major, minor);
|
||
|
}
|
||
|
|
||
|
|
||
|
struct dirent; /* Forward declaration */
|
||
|
int _sos_readdir(int fd, struct dirent * dirent)
|
||
|
{
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_READDIR,
|
||
|
fd,
|
||
|
(unsigned int)dirent);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_mkdir(const char *pathname, mode_t mode)
|
||
|
{
|
||
|
if (!pathname)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_MKDIR,
|
||
|
(unsigned int)pathname,
|
||
|
strlen(pathname),
|
||
|
mode);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_rmdir(const char *pathname)
|
||
|
{
|
||
|
if (!pathname)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_RMDIR,
|
||
|
(unsigned int)pathname,
|
||
|
strlen(pathname));
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_chmod(const char *pathname, mode_t mode)
|
||
|
{
|
||
|
if (!pathname)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall3(SOS_SYSCALL_ID_CHMOD,
|
||
|
(unsigned int)pathname,
|
||
|
strlen(pathname),
|
||
|
mode);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_stat(const char *pathname, int nofollow, struct stat * st)
|
||
|
{
|
||
|
if (!pathname || !st)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall4(SOS_SYSCALL_ID_STAT64,
|
||
|
(unsigned int)pathname,
|
||
|
strlen(pathname),
|
||
|
nofollow,
|
||
|
(unsigned int)st);
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_chroot(const char *dirname)
|
||
|
{
|
||
|
if (!dirname)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_CHROOT,
|
||
|
(unsigned int)dirname,
|
||
|
strlen(dirname));
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_chdir(const char *dirname)
|
||
|
{
|
||
|
if (!dirname)
|
||
|
return -1;
|
||
|
|
||
|
return _sos_syscall2(SOS_SYSCALL_ID_CHDIR,
|
||
|
(unsigned int)dirname,
|
||
|
strlen(dirname));
|
||
|
}
|
||
|
|
||
|
|
||
|
int _sos_fchdir(int fd)
|
||
|
{
|
||
|
return _sos_syscall1(SOS_SYSCALL_ID_FCHDIR,
|
||
|
(unsigned int)fd);
|
||
|
}
|