cstdio中的文件操作函数

stdio.h中定义了文件删除函数remove,文件重命名函数rename,打开临时文件函数tmpfile,生成临时文件名函数tmpnam。接下来我们一起来分析一下rename对应的源码实现。

文件重命名函数rename

使用新的文件名替换旧的文件名

int rename ( const char * oldname, const char * newname );

在glibc/libio/stdio.h中与rename相关的函数增加了两个renameat(替换时增加了FD的关联),renameat2(替换时增加FD的关联,flags信息,表示三种不同的模式:RENAME_NOREPLACE,RENAME_EXCHANGE,RENAME_WHITEOUT)

153 /* Rename file OLD to NEW.  */
154 extern int rename (const char *__old, const char *__new) __THROW;
155
156 #ifdef __USE_ATFILE
157 /* Rename file OLD relative to OLDFD to NEW relative to NEWFD. */
158 extern int renameat (int __oldfd, const char *__old, int __newfd,
159 const char *__new) __THROW;
160 #endif
161
162 #ifdef __USE_GNU
163 /* Flags for renameat2. */
164 # define RENAME_NOREPLACE (1 << 0)
165 # define RENAME_EXCHANGE (1 << 1)
166 # define RENAME_WHITEOUT (1 << 2)
167
168 /* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
169 additional flags. */
170 extern int renameat2 (int __oldfd, const char *__old, int __newfd,
171 const char *__new, unsigned int __flags) __THROW;
172 #endif

实现方式---unix方式

代码参考:/glibc/sysdeps/unix/sysv/linux/rename.c

实际上就是通过调用INLINE_SYSCALL_CALL实现的

24 /* Rename the file OLD to NEW.  */25 int 
26 rename (const char *old, const char *new)
27 {
28 #if defined (__NR_rename)29 return INLINE_SYSCALL_CALL (rename, old, new);
30 #elif defined (__NR_renameat)31 return INLINE_SYSCALL_CALL (renameat, AT_FDCWD, old, AT_FDCWD, new);
32 #else33 return INLINE_SYSCALL_CALL (renameat2, AT_FDCWD, old, AT_FDCWD, new, 0);
34 #endif35 }

INLINE_SYSCALL_CALL的实现,继续调用__INLINE_SYSCALL_DISP,注意这里实际上是将__INLINE_SYSCALL和后面的数据做了连接操作("##"在宏中做字符串连接)

//glibc/sysdeps/unix/sysdep.h
103 /* Issue a syscall defined by syscall number plus any other argument
104 required. Any error will be handled using arch defined macros and errno
105 will be set accordingly.
106 It is similar to INLINE_SYSCALL macro, but without the need to pass the
107 expected argument number as second parameter. */
108 #define INLINE_SYSCALL_CALL(...) \
109 __INLINE_SYSCALL_DISP (__INLINE_SYSCALL, __VA_ARGS__)100 #define __INLINE_SYSCALL_DISP(b,...) \
101 __SYSCALL_CONCAT (b,__INLINE_SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)27 #define __SYSCALL_CONCAT_X(a,b) a##b28 #define __SYSCALL_CONCAT(a,b) __SYSCALL_CONCAT_X (a, b)31 #define __INTERNAL_SYSCALL0(name) \
32 INTERNAL_SYSCALL (name, 0)33 #define __INTERNAL_SYSCALL1(name, a1) \
34 INTERNAL_SYSCALL (name, 1, a1)35 #define __INTERNAL_SYSCALL2(name, a1, a2) \
36 INTERNAL_SYSCALL (name, 2, a1, a2)37 #define __INTERNAL_SYSCALL3(name, a1, a2, a3) \
38 INTERNAL_SYSCALL (name, 3, a1, a2, a3)39 #define __INTERNAL_SYSCALL4(name, a1, a2, a3, a4) \
40 INTERNAL_SYSCALL (name, 4, a1, a2, a3, a4)41 #define __INTERNAL_SYSCALL5(name, a1, a2, a3, a4, a5) \
42 INTERNAL_SYSCALL (name, 5, a1, a2, a3, a4, a5)43 #define __INTERNAL_SYSCALL6(name, a1, a2, a3, a4, a5, a6) \
44 INTERNAL_SYSCALL (name, 6, a1, a2, a3, a4, a5, a6)45 #define __INTERNAL_SYSCALL7(name, a1, a2, a3, a4, a5, a6, a7) \
46 INTERNAL_SYSCALL (name, 7, a1, a2, a3, a4, a5, a6, a7)47
48 #define __INTERNAL_SYSCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n49 #define __INTERNAL_SYSCALL_NARGS(...) \
50 __INTERNAL_SYSCALL_NARGS_X (__VA_ARGS__,7,6,5,4,3,2,1,0,)

这里有必要分析一下​​__SYSCALL_CONCAT (b,__INLINE_SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)​​中​​__INLINE_SYSCALL_NARGS(__VA_ARGS__)​​的作用,

可以看到,它在传入参数的后面添加了7-0共8个参数传递给__INTERNAL_SYSCALL_NARGS_X,该宏按顺序筛选出第9个数n,即如果__VA_ARGS__只有一个参数,n=0,两个参数n=1,

所以​​INLINE_SYSCALL_CALL (rename, old, new)​​三个参数,n=2,被宏展开为​​__INLINE_SYSCALL2(rename,old,new)​​,再按照宏展开为​​INTERNAL_SYSCALL (rename, 2, old, new)​

对应架构的实现如下: glibc/sysdeps/unix/sysv/linux/x86_64/sysdep.h

其中number为连接出的_NR_rename,这也说明了最开始判断​​#if defined (__NR_rename)​​的原因,

arg1,arg2分别为输入的old,new,调用汇编进行执行。

29 /* For Linux we can use the system call table in the header file 
30 /usr/include/asm/unistd.h
31 of the kernel. But these symbols do not follow the SYS_* syntax
32 so we have to redefine the `SYS_ify' macro here. */
33 #undef SYS_ify
34 #define SYS_ify(syscall_name) __NR_##syscall_name 233 #undef INTERNAL_SYSCALL
234 #define INTERNAL_SYSCALL(name, nr, args...) \
235 internal_syscall##nr (SYS_ify (name), args)267 #undef internal_syscall2
268 #define internal_syscall2(number, arg1, arg2) \
269 ({ \
270 unsigned long int resultvar; \
271 TYPEFY (arg2, __arg2) = ARGIFY (arg2); \
272 TYPEFY (arg1, __arg1) = ARGIFY (arg1); \
273 register TYPEFY (arg2, _a2) asm ("rsi") = __arg2; \
274 register TYPEFY (arg1, _a1) asm ("rdi") = __arg1; \
275 asm volatile ( \
276 "syscall\n\t" \
277 : "=a" (resultvar) \
278 : "0" (number), "r" (_a1), "r" (_a2) \
279 : "memory", REGISTERS_CLOBBERED_BY_SYSCALL); \
280 (long int) resultvar; \
281 })

renameat和renameat2的调用与之类似,这里就不做过多说明了,需要注意的是renameat调用中使用的flags是0(默认值)。

实现方式---mach内核架构实现

glibc/sysdeps/mach/hurd/rename.c,实际上是调用__dir_rename实现的,不做更深入解读了

21 /* Rename the file OLD to NEW.  */22 int23 rename (const char *old, const char *new)
24 {
25 error_t err;
26 file_t olddir, newdir;
27 const char *oldname, *newname;
28
29 olddir = __directory_name_split (old, (char **) &oldname);
30 if (olddir == MACH_PORT_NULL)
31 return -1;
32 newdir = __directory_name_split (new, (char **) &newname);
33 if (newdir == MACH_PORT_NULL)
34 {
35 __mach_port_deallocate (__mach_task_self (), olddir);
36 return -1;
37 }
38
39 err = __dir_rename (olddir, oldname, newdir, newname, 0);
40 __mach_port_deallocate (__mach_task_self (), olddir);
41 __mach_port_deallocate (__mach_task_self (), newdir);
42 if (err)
43 return __hurd_fail (err);
44 return 0;
45 }

实现方式---posix实现

glibc/sysdeps/posix/rename.c

逻辑也比较好理解,使用__link完成对应的rename操作,然后使用__unlink断开oldname,大部分代码是针对出现异常时,即返回值小于0的异常处理,如前一处条件中考虑到了EEXIST(文件存在)的情况,那就需要删除新文件,重新尝试__link,删除old文件时也是,出现异常,同时也要将新文件删除,保证最后失败不会带来额外的影响。具体__link和__unlink的调用基本也都是基于syscall实现的,就不展开细说了。

22 /* Rename the file OLD to NEW.  */23 int24 rename (const char *old, const char *new)
25 {
26 int save = errno;
27 if (__link (old, new) < 0)
28 {
29 if (errno == EEXIST)
30 {
31 __set_errno (save);
32 /* Race condition, required for 1003.1 conformance. */33 if (__unlink (new) < 034 || __link (old, new) < 0)
35 return -1;
36 }
37 else38 return -1;
39 }
40 if (__unlink (old) < 0)
41 {
42 save = errno;
43 if (__unlink (new) == 0)
44 __set_errno (save);
45 return -1;
46 }
47 return 0;
48 }