]>
Commit | Line | Data |
---|---|---|
2aa62f2b | 1 | /** @file\r |
2 | EFI versions of NetBSD system calls.\r | |
3 | \r | |
450ea6d5 | 4 | Copyright (c) 2016, Daryl McDaniel. All rights reserved.<BR>\r |
a7a8363d | 5 | Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.<BR>\r |
2aa62f2b | 6 | This program and the accompanying materials are licensed and made available under\r |
7 | the terms and conditions of the BSD License that accompanies this distribution.\r | |
8 | The full text of the license may be found at\r | |
9 | http://opensource.org/licenses/bsd-license.\r | |
10 | \r | |
11 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r | |
12 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r | |
13 | \r | |
14 | **/\r | |
15 | #include <Uefi.h>\r | |
16 | #include <Library/UefiLib.h>\r | |
53e1e5c6 | 17 | #include <Library/UefiBootServicesTableLib.h>\r |
2aa62f2b | 18 | #include <Library/BaseLib.h>\r |
19 | #include <Library/MemoryAllocationLib.h>\r | |
20 | #include <Library/ShellLib.h>\r | |
21 | \r | |
22 | #include <LibConfig.h>\r | |
23 | #include <sys/EfiCdefs.h>\r | |
24 | \r | |
25 | #include <sys/ansi.h>\r | |
26 | #include <errno.h>\r | |
27 | #include <stdarg.h>\r | |
53e1e5c6 | 28 | #include <stdlib.h>\r |
2aa62f2b | 29 | #include <string.h>\r |
30 | #include <wchar.h>\r | |
53e1e5c6 | 31 | #include <sys/poll.h>\r |
2aa62f2b | 32 | #include <sys/fcntl.h>\r |
33 | #include <sys/stat.h>\r | |
34 | #include <sys/syslimits.h>\r | |
0c1992fb | 35 | #include <sys/filio.h>\r |
53e1e5c6 | 36 | #include <Efi/SysEfi.h>\r |
0c1992fb | 37 | #include <unistd.h>\r |
53e1e5c6 | 38 | #include <kfile.h>\r |
39 | #include <Device/Device.h>\r | |
6c6c850a | 40 | #include <Device/IIO.h>\r |
2aa62f2b | 41 | #include <MainData.h>\r |
0c1992fb | 42 | #include <extern.h>\r |
2aa62f2b | 43 | \r |
44 | /* EFI versions of BSD system calls used in stdio */\r | |
45 | \r | |
2aa62f2b | 46 | /* Validate that fd refers to a valid file descriptor.\r |
47 | IsOpen is interpreted as follows:\r | |
48 | - Positive fd must be OPEN\r | |
49 | - Zero fd must be CLOSED\r | |
50 | - Negative fd may be OPEN or CLOSED\r | |
51 | \r | |
52 | @retval TRUE fd is VALID\r | |
53 | @retval FALSE fd is INVALID\r | |
54 | */\r | |
53e1e5c6 | 55 | BOOLEAN\r |
2aa62f2b | 56 | ValidateFD( int fd, int IsOpen)\r |
57 | {\r | |
53e1e5c6 | 58 | struct __filedes *filp;\r |
2aa62f2b | 59 | BOOLEAN retval = FALSE;\r |
60 | \r | |
61 | if((fd >= 0) && (fd < OPEN_MAX)) {\r | |
53e1e5c6 | 62 | filp = &gMD->fdarray[fd];\r |
2aa62f2b | 63 | retval = TRUE;\r |
64 | if(IsOpen >= 0) {\r | |
53e1e5c6 | 65 | retval = (BOOLEAN)((filp->f_iflags != 0) && // TRUE if OPEN\r |
66 | FILE_IS_USABLE(filp)); // and Usable (not Larval or Closing)\r | |
2aa62f2b | 67 | if(IsOpen == VALID_CLOSED) {\r |
68 | retval = (BOOLEAN)!retval; // We want TRUE if CLOSED\r | |
69 | }\r | |
70 | }\r | |
71 | }\r | |
72 | return retval;\r | |
73 | }\r | |
74 | \r | |
75 | /* Find and reserve a free File Descriptor.\r | |
76 | \r | |
77 | Returns the first free File Descriptor greater than or equal to the,\r | |
78 | already validated, fd specified by Minfd.\r | |
79 | \r | |
80 | @return Returns -1 if there are no free FDs. Otherwise returns the\r | |
81 | found fd.\r | |
82 | */\r | |
53e1e5c6 | 83 | int\r |
2aa62f2b | 84 | FindFreeFD( int MinFd )\r |
85 | {\r | |
86 | struct __filedes *Mfd;\r | |
87 | int i;\r | |
88 | int fd = -1;\r | |
89 | \r | |
90 | Mfd = gMD->fdarray;\r | |
91 | \r | |
92 | // Get an available fd\r | |
93 | for(i=MinFd; i < OPEN_MAX; ++i) {\r | |
53e1e5c6 | 94 | if(Mfd[i].f_iflags == 0) {\r |
95 | Mfd[i].f_iflags = FIF_LARVAL; // Temporarily mark this fd as reserved\r | |
2aa62f2b | 96 | fd = i;\r |
97 | break;\r | |
98 | }\r | |
99 | }\r | |
100 | return fd;\r | |
101 | }\r | |
102 | \r | |
53e1e5c6 | 103 | /* Mark that an open file is to be deleted when closed. */\r |
104 | int\r | |
105 | DeleteOnClose(int fd)\r | |
106 | {\r | |
107 | int retval = 0;\r | |
108 | \r | |
109 | if(ValidateFD( fd, VALID_OPEN)) {\r | |
110 | gMD->fdarray[fd].f_iflags |= FIF_DELCLOSE;\r | |
111 | }\r | |
112 | else {\r | |
113 | errno = EBADF;\r | |
114 | retval = -1;\r | |
115 | }\r | |
116 | return retval;\r | |
117 | }\r | |
118 | \r | |
6c6c850a | 119 | /** The isatty() function tests whether fd, an open file descriptor,\r |
2aa62f2b | 120 | is associated with a terminal device.\r |
121 | \r | |
6c6c850a | 122 | @param[in] fd File Descriptor for the file to be examined.\r |
123 | \r | |
124 | @retval 1 fd is associated with a terminal.\r | |
125 | @retval 0 fd is not associated with a terminal. errno is set to\r | |
126 | EBADF if fd is not a valid open FD.\r | |
2aa62f2b | 127 | **/\r |
128 | int\r | |
53e1e5c6 | 129 | isatty (int fd)\r |
2aa62f2b | 130 | {\r |
131 | int retval = 0;\r | |
53e1e5c6 | 132 | struct __filedes *Fp;\r |
2aa62f2b | 133 | \r |
53e1e5c6 | 134 | if(ValidateFD( fd, VALID_OPEN)) {\r |
135 | Fp = &gMD->fdarray[fd];\r | |
6c6c850a | 136 | retval = (Fp->f_iflags & _S_ITTY) ? 1 : 0;\r |
2aa62f2b | 137 | }\r |
138 | else {\r | |
139 | errno = EBADF;\r | |
140 | }\r | |
141 | return retval;\r | |
142 | }\r | |
143 | \r | |
0c1992fb | 144 | /** Determine if file descriptor fd is a duplicate of some other fd.\r |
145 | \r | |
146 | @param[in] fd The file descriptor to check.\r | |
147 | \r | |
148 | @retval TRUE fd is a duplicate of another fd.\r | |
149 | @retval FALSE fd is unique.\r | |
150 | **/\r | |
2aa62f2b | 151 | static BOOLEAN\r |
152 | IsDupFd( int fd)\r | |
153 | {\r | |
53e1e5c6 | 154 | void * DevData;\r |
155 | const struct fileops *FileOps;\r | |
2aa62f2b | 156 | int i;\r |
157 | BOOLEAN Ret = FALSE;\r | |
158 | \r | |
159 | if(ValidateFD( fd, VALID_OPEN )) {\r | |
53e1e5c6 | 160 | FileOps = gMD->fdarray[fd].f_ops;\r |
161 | DevData = gMD->fdarray[fd].devdata;\r | |
2aa62f2b | 162 | for(i=0; i < OPEN_MAX; ++i) {\r |
163 | if(i == fd) continue;\r | |
53e1e5c6 | 164 | if(ValidateFD( i, VALID_OPEN )) { // TRUE if fd is valid and OPEN\r |
165 | if((gMD->fdarray[i].f_ops == FileOps)\r | |
166 | &&(gMD->fdarray[i].devdata == DevData )) {\r | |
2aa62f2b | 167 | Ret = TRUE;\r |
168 | break;\r | |
169 | }\r | |
170 | }\r | |
171 | }\r | |
172 | }\r | |
173 | return Ret;\r | |
174 | }\r | |
175 | \r | |
6c6c850a | 176 | /** Worker function to Close a file and set its fd to the specified state.\r |
0c1992fb | 177 | \r |
178 | @param[in] fd The file descriptor to close.\r | |
179 | @param[in] NewState State to set the fd to after the file is closed.\r | |
180 | \r | |
181 | @retval 0 The operation completed successfully.\r | |
182 | @retval -1 The operation failed. Further information is in errno.\r | |
6c6c850a | 183 | * EBADF fd is not a valid or open file descriptor.\r |
0c1992fb | 184 | **/\r |
2aa62f2b | 185 | static int\r |
186 | _closeX (int fd, int NewState)\r | |
187 | {\r | |
53e1e5c6 | 188 | struct __filedes *Fp;\r |
2aa62f2b | 189 | int retval = 0;\r |
190 | \r | |
2aa62f2b | 191 | // Verify my pointers and get my FD.\r |
192 | if(ValidateFD( fd, VALID_OPEN )) {\r | |
53e1e5c6 | 193 | Fp = &gMD->fdarray[fd];\r |
194 | // Check if there are other users of this FileHandle\r | |
195 | if(Fp->RefCount == 1) { // There should be no other users\r | |
450ea6d5 DM |
196 | if(! IsDupFd(fd)) {\r |
197 | // Only do the close if no one else is using the FileHandle\r | |
198 | if(Fp->f_iflags & FIF_DELCLOSE) {\r | |
199 | /* Handle files marked "Delete on Close". */\r | |
200 | if(Fp->f_ops->fo_delete != NULL) {\r | |
201 | retval = Fp->f_ops->fo_delete(Fp);\r | |
202 | }\r | |
48831246 | 203 | }\r |
450ea6d5 | 204 | else {\r |
53e1e5c6 | 205 | retval = Fp->f_ops->fo_close( Fp);\r |
450ea6d5 | 206 | }\r |
2aa62f2b | 207 | }\r |
53e1e5c6 | 208 | Fp->f_iflags = NewState; // Close this FD or reserve it\r |
209 | Fp->RefCount = 0; // No one using this FD\r | |
210 | }\r | |
211 | else {\r | |
212 | --Fp->RefCount; /* One less user of this FD */\r | |
2aa62f2b | 213 | }\r |
214 | }\r | |
215 | else {\r | |
216 | // Bad FD\r | |
2aa62f2b | 217 | retval = -1;\r |
53e1e5c6 | 218 | errno = EBADF;\r |
2aa62f2b | 219 | }\r |
220 | return retval;\r | |
221 | }\r | |
222 | \r | |
0c1992fb | 223 | /** The close() function deallocates the file descriptor indicated by fd.\r |
2aa62f2b | 224 | To deallocate means to make the file descriptor available for return by\r |
225 | subsequent calls to open() or other functions that allocate file\r | |
226 | descriptors. All outstanding record locks owned by the process on the file\r | |
0c1992fb | 227 | associated with the file descriptor are removed (that is, unlocked).\r |
2aa62f2b | 228 | \r |
6c6c850a | 229 | @param[in] fd Descriptor for the File to close.\r |
230 | \r | |
0c1992fb | 231 | @retval 0 Successful completion.\r |
232 | @retval -1 An error occurred and errno is set to identify the error.\r | |
2aa62f2b | 233 | **/\r |
234 | int\r | |
235 | close (int fd)\r | |
236 | {\r | |
2aa62f2b | 237 | return _closeX(fd, 0);\r |
238 | }\r | |
239 | \r | |
6c6c850a | 240 | /** Delete the file specified by path.\r |
241 | \r | |
242 | @param[in] path The MBCS path of the file to delete.\r | |
243 | \r | |
244 | @retval -1 Unable to open the file specified by path.\r | |
245 | @retval -1 If (errno == EPERM), unlink is not permited for this file.\r | |
246 | @retval -1 Low-level delete filed. Reason is in errno.\r | |
247 | @retval 0 The file was successfully deleted.\r | |
53e1e5c6 | 248 | **/\r |
2aa62f2b | 249 | int\r |
53e1e5c6 | 250 | unlink (const char *path)\r |
2aa62f2b | 251 | {\r |
53e1e5c6 | 252 | struct __filedes *Fp;\r |
253 | int fd;\r | |
254 | int retval = -1;\r | |
2aa62f2b | 255 | \r |
256 | EFIerrno = RETURN_SUCCESS;\r | |
257 | \r | |
53e1e5c6 | 258 | fd = open(path, O_WRONLY, 0);\r |
259 | if(fd >= 0) {\r | |
260 | Fp = &gMD->fdarray[fd];\r | |
261 | \r | |
262 | if(Fp->f_ops->fo_delete != NULL) {\r | |
263 | retval = Fp->f_ops->fo_delete(Fp);\r | |
2aa62f2b | 264 | }\r |
53e1e5c6 | 265 | Fp->f_iflags = 0; // Close this FD\r |
266 | Fp->RefCount = 0; // No one using this FD\r | |
2aa62f2b | 267 | }\r |
53e1e5c6 | 268 | return retval;\r |
2aa62f2b | 269 | }\r |
270 | \r | |
271 | /** The fcntl() function shall perform the operations described below on open\r | |
272 | files. The fildes argument is a file descriptor.\r | |
273 | \r | |
274 | The available values for cmd are defined in <fcntl.h> and are as follows:\r | |
275 | - F_DUPFD - Return a new file descriptor which shall be the lowest\r | |
276 | numbered available (that is, not already open) file\r | |
277 | descriptor greater than or equal to the third argument, arg,\r | |
278 | taken as an integer of type int. The new file descriptor\r | |
279 | shall refer to the same open file description as the original\r | |
280 | file descriptor, and shall share any locks. The FD_CLOEXEC\r | |
281 | flag associated with the new file descriptor shall be cleared\r | |
282 | to keep the file open across calls to one of the exec functions.\r | |
283 | - F_GETFD - Get the file descriptor flags defined in <fcntl.h> that are\r | |
284 | associated with the file descriptor fildes. File descriptor\r | |
285 | flags are associated with a single file descriptor and do not\r | |
286 | affect other file descriptors that refer to the same file.\r | |
287 | - F_SETFD - Set the file descriptor flags defined in <fcntl.h>, that are\r | |
288 | associated with fildes, to the third argument, arg, taken\r | |
289 | as type int. If the FD_CLOEXEC flag in the third argument\r | |
290 | is 0, the file shall remain open across the exec\r | |
291 | functions; otherwise, the file shall be closed upon\r | |
292 | successful execution of one of the exec functions.\r | |
293 | - F_GETFL - Get the file status flags and file access modes, defined in\r | |
294 | <fcntl.h>, for the file description associated with fildes.\r | |
295 | The file access modes can be extracted from the return\r | |
296 | value using the mask O_ACCMODE, which is defined in\r | |
297 | <fcntl.h>. File status flags and file access modes are\r | |
298 | associated with the file description and do not affect\r | |
299 | other file descriptors that refer to the same file with\r | |
300 | different open file descriptions.\r | |
301 | - F_SETFL - Set the file status flags, defined in <fcntl.h>, for the file\r | |
302 | description associated with fildes from the corresponding\r | |
303 | bits in the third argument, arg, taken as type int. Bits\r | |
304 | corresponding to the file access mode and the file creation\r | |
305 | flags, as defined in <fcntl.h>, that are set in arg shall\r | |
306 | be ignored. If any bits in arg other than those mentioned\r | |
307 | here are changed by the application, the result is unspecified.\r | |
308 | - F_GETOWN - If fildes refers to a socket, get the process or process group\r | |
309 | ID specified to receive SIGURG signals when out-of-band\r | |
310 | data is available. Positive values indicate a process ID;\r | |
311 | negative values, other than -1, indicate a process group\r | |
312 | ID. If fildes does not refer to a socket, the results are\r | |
313 | unspecified.\r | |
314 | - F_SETOWN - If fildes refers to a socket, set the process or process\r | |
315 | group ID specified to receive SIGURG signals when\r | |
316 | out-of-band data is available, using the value of the third\r | |
317 | argument, arg, taken as type int. Positive values indicate\r | |
318 | a process ID; negative values, other than -1, indicate a\r | |
319 | process group ID. If fildes does not refer to a socket, the\r | |
320 | results are unspecified.\r | |
321 | \r | |
322 | The fcntl() function shall fail if:\r | |
323 | \r | |
324 | [EBADF] The fildes argument is not a valid open file descriptor.\r | |
325 | [EINVAL] The cmd argument is invalid, or the cmd argument is F_DUPFD\r | |
326 | and arg is negative or greater than or equal to {OPEN_MAX}.\r | |
327 | [EMFILE] The argument cmd is F_DUPFD and {OPEN_MAX} file descriptors\r | |
328 | are currently open in the calling process, or no file\r | |
329 | descriptors greater than or equal to arg are available.\r | |
330 | [EOVERFLOW] One of the values to be returned cannot be represented correctly.\r | |
331 | \r | |
6c6c850a | 332 | @param[in] fildes Descriptor for the file to be controlled.\r |
333 | @param[in] cmd Command to be acted upon.\r | |
334 | @param[in,out] ... Optional additional parameters as required by cmd.\r | |
335 | \r | |
2aa62f2b | 336 | @return Upon successful completion, the value returned shall depend on\r |
337 | cmd as follows:\r | |
338 | - F_DUPFD - A new file descriptor.\r | |
339 | - F_GETFD - Value of flags defined in <fcntl.h>. The return value\r | |
340 | shall not be negative.\r | |
341 | - F_SETFD - Value other than -1.\r | |
342 | - F_GETFL - Value of file status flags and access modes. The return\r | |
343 | value is not negative.\r | |
344 | - F_SETFL - Value other than -1.\r | |
345 | - F_GETOWN - Value of the socket owner process or process group;\r | |
346 | this will not be -1.\r | |
347 | - F_SETOWN - Value other than -1.\r | |
348 | Otherwise, -1 shall be returned and errno set to indicate the error.\r | |
349 | \r | |
350 | **/\r | |
351 | int\r | |
352 | fcntl (int fildes, int cmd, ...)\r | |
353 | {\r | |
354 | va_list p3;\r | |
355 | struct __filedes *MyFd;\r | |
356 | int retval = -1;\r | |
357 | int temp;\r | |
358 | \r | |
359 | //Print(L"%a( %d, %d, ...)\n", __func__, fildes, cmd);\r | |
360 | va_start(p3, cmd);\r | |
361 | \r | |
362 | if(ValidateFD( fildes, VALID_OPEN )) {\r | |
363 | MyFd = &gMD->fdarray[fildes];\r | |
364 | \r | |
365 | switch(cmd) {\r | |
366 | case F_DUPFD:\r | |
367 | temp = va_arg(p3, int);\r | |
368 | if(ValidateFD( temp, VALID_DONT_CARE )) {\r | |
369 | temp = FindFreeFD( temp );\r | |
370 | if(temp < 0) {\r | |
371 | errno = EMFILE;\r | |
372 | break;\r | |
373 | }\r | |
374 | /* temp is now a valid fd reserved for further use\r | |
375 | so copy fd into temp.\r | |
376 | */\r | |
377 | (void)memcpy(&gMD->fdarray[temp], MyFd, sizeof(struct __filedes));\r | |
378 | retval = temp;\r | |
379 | }\r | |
380 | else {\r | |
381 | errno = EINVAL;\r | |
382 | }\r | |
383 | break;\r | |
0c1992fb | 384 | \r |
2aa62f2b | 385 | case F_SETFL:\r |
386 | retval = MyFd->Oflags; // Get original value\r | |
387 | temp = va_arg(p3, int);\r | |
388 | temp &= O_SETMASK; // Only certain bits can be set\r | |
389 | temp |= retval & O_SETMASK;\r | |
390 | MyFd->Oflags = temp; // Set new value\r | |
391 | break;\r | |
0c1992fb | 392 | \r |
2aa62f2b | 393 | case F_SETFD:\r |
53e1e5c6 | 394 | retval = MyFd->f_iflags;\r |
2aa62f2b | 395 | break;\r |
53e1e5c6 | 396 | //case F_SETOWN:\r |
397 | // retval = MyFd->SocProc;\r | |
398 | // MyFd->SocProc = va_arg(p3, int);\r | |
399 | // break;\r | |
2aa62f2b | 400 | case F_GETFD:\r |
53e1e5c6 | 401 | retval = MyFd->f_iflags;\r |
2aa62f2b | 402 | break;\r |
403 | case F_GETFL:\r | |
2aa62f2b | 404 | retval = MyFd->Oflags;\r |
405 | break;\r | |
53e1e5c6 | 406 | //case F_GETOWN:\r |
407 | // retval = MyFd->SocProc;\r | |
408 | // break;\r | |
2aa62f2b | 409 | default:\r |
410 | errno = EINVAL;\r | |
411 | break;\r | |
412 | }\r | |
413 | }\r | |
414 | else {\r | |
415 | // Bad FD\r | |
416 | errno = EBADF;\r | |
417 | }\r | |
418 | va_end(p3);\r | |
419 | return retval;;\r | |
420 | }\r | |
421 | \r | |
422 | /** The dup() function provides an alternative interface to the\r | |
423 | service provided by fcntl() using the F_DUPFD command. The call:\r | |
424 | - fid = dup(fildes);\r | |
425 | shall be equivalent to:\r | |
426 | - fid = fcntl(fildes, F_DUPFD, 0);\r | |
427 | \r | |
6c6c850a | 428 | @param[in] fildes Descriptor for the file to be examined.\r |
429 | \r | |
2aa62f2b | 430 | @return Upon successful completion a non-negative integer, namely the\r |
431 | file descriptor, shall be returned; otherwise, -1 shall be\r | |
432 | returned and errno set to indicate the error.\r | |
433 | **/\r | |
434 | int\r | |
435 | dup (int fildes)\r | |
436 | {\r | |
437 | return fcntl(fildes, F_DUPFD, 0);\r | |
438 | }\r | |
439 | \r | |
6c6c850a | 440 | /** Make fildes2 refer to a duplicate of fildes.\r |
441 | \r | |
442 | The dup2() function provides an alternative interface to the\r | |
2aa62f2b | 443 | service provided by fcntl() using the F_DUPFD command. The call:\r |
444 | - fid = dup2(fildes, fildes2);\r | |
445 | shall be equivalent to:\r | |
446 | - close(fildes2);\r | |
447 | - fid = fcntl(fildes, F_DUPFD, fildes2);\r | |
448 | except for the following:\r | |
449 | - If fildes2 is less than 0 or greater than or equal to {OPEN_MAX},\r | |
450 | dup2() shall return -1 with errno set to [EBADF].\r | |
451 | - If fildes is a valid file descriptor and is equal to fildes2, dup2()\r | |
452 | shall return fildes2 without closing it.\r | |
453 | - If fildes is not a valid file descriptor, dup2() shall return -1 and\r | |
454 | shall not close fildes2.\r | |
455 | - The value returned shall be equal to the value of fildes2 upon\r | |
456 | successful completion, or -1 upon failure.\r | |
457 | \r | |
6c6c850a | 458 | @param[in] fildes File Descriptor to be duplicated.\r |
459 | @param[in] fildes2 File Descriptor to be made a duplicate of fildes.\r | |
460 | \r | |
2aa62f2b | 461 | @return Upon successful completion a non-negative integer, namely\r |
462 | fildes2, shall be returned; otherwise, -1 shall be\r | |
463 | returned and errno set to EBADF indicate the error.\r | |
464 | **/\r | |
465 | int\r | |
466 | dup2 (int fildes, int fildes2)\r | |
467 | {\r | |
468 | int retval = -1;\r | |
469 | \r | |
470 | if(ValidateFD( fildes, VALID_OPEN)) {\r | |
471 | retval = fildes2;\r | |
472 | if( fildes != fildes2) {\r | |
473 | if(ValidateFD( fildes2, VALID_DONT_CARE)) {\r | |
53e1e5c6 | 474 | gMD->fdarray[fildes2].f_iflags = FIF_LARVAL; // Mark the file closed, but reserved\r |
2aa62f2b | 475 | (void)memcpy(&gMD->fdarray[fildes2], // Duplicate fildes into fildes2\r |
476 | &gMD->fdarray[fildes], sizeof(struct __filedes));\r | |
53e1e5c6 | 477 | gMD->fdarray[fildes2].MyFD = (UINT16)fildes2;\r |
2aa62f2b | 478 | }\r |
479 | else {\r | |
480 | errno = EBADF;\r | |
481 | retval = -1;\r | |
482 | }\r | |
483 | }\r | |
484 | }\r | |
485 | else {\r | |
486 | errno = EBADF;\r | |
487 | }\r | |
488 | return retval;\r | |
489 | }\r | |
490 | \r | |
491 | /** Reposition a file's read/write offset.\r | |
492 | \r | |
493 | The lseek() function repositions the offset of the file descriptor fildes\r | |
494 | to the argument offset according to the directive how. The argument\r | |
495 | fildes must be an open file descriptor. lseek() repositions the file\r | |
496 | pointer fildes as follows:\r | |
497 | \r | |
6c6c850a | 498 | - If how is SEEK_SET, the offset is set to offset bytes.\r |
2aa62f2b | 499 | \r |
6c6c850a | 500 | - If how is SEEK_CUR, the offset is set to its current location\r |
501 | plus offset bytes.\r | |
2aa62f2b | 502 | \r |
6c6c850a | 503 | - If how is SEEK_END, the offset is set to the size of the file\r |
504 | plus offset bytes.\r | |
2aa62f2b | 505 | \r |
506 | The lseek() function allows the file offset to be set beyond the end of\r | |
507 | the existing end-of-file of the file. If data is later written at this\r | |
508 | point, subsequent reads of the data in the gap return bytes of zeros\r | |
509 | (until data is actually written into the gap).\r | |
510 | \r | |
511 | Some devices are incapable of seeking. The value of the pointer associ-\r | |
512 | ated with such a device is undefined.\r | |
513 | \r | |
6c6c850a | 514 | @param[in] fd Descriptor for the File to be affected.\r |
515 | @param[in] offset Value to adjust the file position by.\r | |
516 | @param[in] how How the file position is to be adjusted.\r | |
517 | \r | |
2aa62f2b | 518 | @return Upon successful completion, lseek() returns the resulting offset\r |
519 | location as measured in bytes from the beginning of the file.\r | |
520 | Otherwise, a value of -1 is returned and errno is set to\r | |
521 | indicate the error.\r | |
522 | **/\r | |
523 | __off_t\r | |
53e1e5c6 | 524 | lseek (int fd, __off_t offset, int how)\r |
2aa62f2b | 525 | {\r |
526 | __off_t CurPos = -1;\r | |
53e1e5c6 | 527 | // RETURN_STATUS Status = RETURN_SUCCESS;\r |
528 | struct __filedes *filp;\r | |
2aa62f2b | 529 | \r |
530 | EFIerrno = RETURN_SUCCESS; // In case of error without an EFI call\r | |
531 | \r | |
532 | if( how == SEEK_SET || how == SEEK_CUR || how == SEEK_END) {\r | |
53e1e5c6 | 533 | if(ValidateFD( fd, VALID_OPEN)) {\r |
534 | filp = &gMD->fdarray[fd];\r | |
2aa62f2b | 535 | // Both of our parameters have been verified as valid\r |
53e1e5c6 | 536 | CurPos = filp->f_ops->fo_lseek( filp, offset, how);\r |
537 | if(CurPos >= 0) {\r | |
538 | filp->f_offset = CurPos;\r | |
2aa62f2b | 539 | }\r |
540 | }\r | |
541 | else {\r | |
542 | errno = EBADF; // Bad File Descriptor\r | |
543 | }\r | |
544 | }\r | |
545 | else {\r | |
546 | errno = EINVAL; // Invalid how argument\r | |
547 | }\r | |
548 | return CurPos;\r | |
549 | }\r | |
550 | \r | |
551 | /** The directory path is created with the access permissions specified by\r | |
552 | perms.\r | |
553 | \r | |
554 | The directory is closed after it is created.\r | |
555 | \r | |
6c6c850a | 556 | @param[in] path The path to a directory to create.\r |
557 | @param[in] perms Permissions as defined in <sys/stat.h>\r | |
558 | \r | |
2aa62f2b | 559 | @retval 0 The directory was created successfully.\r |
53e1e5c6 | 560 | @retval -1 An error occurred and error codes are stored in errno and EFIerrno.\r |
2aa62f2b | 561 | **/\r |
562 | int\r | |
563 | mkdir (const char *path, __mode_t perms)\r | |
564 | {\r | |
53e1e5c6 | 565 | wchar_t *NewPath;\r |
566 | DeviceNode *Node;\r | |
567 | char *GenI;\r | |
0c1992fb | 568 | RETURN_STATUS Status;\r |
53e1e5c6 | 569 | int Instance = 0;\r |
570 | int retval = 0;\r | |
2aa62f2b | 571 | \r |
d7ce7006 | 572 | Status = ParsePath(path, &NewPath, &Node, &Instance, NULL);\r |
2aa62f2b | 573 | if(Status == RETURN_SUCCESS) {\r |
53e1e5c6 | 574 | GenI = Node->InstanceList;\r |
575 | if(GenI == NULL) {\r | |
576 | errno = EPERM;\r | |
577 | retval = -1;\r | |
2aa62f2b | 578 | }\r |
53e1e5c6 | 579 | else {\r |
d7ce7006 | 580 | //GenI += (Instance * Node->InstanceSize);\r |
53e1e5c6 | 581 | retval = ((GenericInstance *)GenI)->Abstraction.fo_mkdir( path, perms);\r |
582 | }\r | |
583 | free(NewPath);\r | |
2aa62f2b | 584 | }\r |
53e1e5c6 | 585 | else {\r |
586 | retval = -1;\r | |
2aa62f2b | 587 | }\r |
53e1e5c6 | 588 | return retval;\r |
2aa62f2b | 589 | }\r |
590 | \r | |
591 | /** Open a file.\r | |
a7a8363d | 592 | The open() function establishes the connection between a file and a file\r |
593 | descriptor. It creates an open file description that refers to a file\r | |
594 | and a file descriptor that refers to that open file description. The file\r | |
595 | descriptor is used by other I/O functions to refer to that file.\r | |
596 | \r | |
597 | The open() function returns a file descriptor for the named file that is\r | |
598 | the lowest file descriptor not currently open for that process. The open\r | |
599 | file description is new, and therefore the file descriptor shall not\r | |
600 | share it with any other process in the system.\r | |
601 | \r | |
602 | The file offset used to mark the current position within the file is set\r | |
603 | to the beginning of the file.\r | |
2aa62f2b | 604 | \r |
605 | The EFI ShellOpenFileByName() function is used to perform the low-level\r | |
606 | file open operation. The primary task of open() is to translate from the\r | |
607 | flags used in the <stdio.h> environment to those used by the EFI function.\r | |
608 | \r | |
a7a8363d | 609 | The file status flags and file access modes of the open file description\r |
610 | are set according to the value of oflags.\r | |
611 | \r | |
612 | Values for oflags are constructed by a bitwise-inclusive OR of flags from\r | |
613 | the following list, defined in <fcntl.h>. Applications shall specify\r | |
614 | exactly one of { O_RDONLY, O_RDWR, O_WRONLY } in the value of oflags.\r | |
615 | Any combination of { O_NONBLOCK, O_APPEND, O_CREAT, O_TRUNC, O_EXCL } may\r | |
616 | also be specified in oflags.\r | |
617 | \r | |
2aa62f2b | 618 | The only valid flag combinations for ShellOpenFileByName() are:\r |
619 | - Read\r | |
620 | - Read/Write\r | |
621 | - Create/Read/Write\r | |
622 | \r | |
a7a8363d | 623 | Values for mode specify the access permissions for newly created files.\r |
2aa62f2b | 624 | The mode value is saved in the FD to indicate permissions for further operations.\r |
625 | \r | |
626 | O_RDONLY -- flags = EFI_FILE_MODE_READ -- this is always done\r | |
627 | O_WRONLY -- flags |= EFI_FILE_MODE_WRITE\r | |
628 | O_RDWR -- flags |= EFI_FILE_MODE_WRITE -- READ is already set\r | |
629 | \r | |
630 | O_NONBLOCK -- ignored\r | |
631 | O_APPEND -- Seek to EOF before every write\r | |
632 | O_CREAT -- flags |= EFI_FILE_MODE_CREATE\r | |
633 | O_TRUNC -- delete first then create new\r | |
634 | O_EXCL -- if O_CREAT is also set, open will fail if the file already exists.\r | |
a7a8363d | 635 | \r |
636 | @param[in] Path The path argument points to a pathname naming the\r | |
637 | object to be opened.\r | |
638 | @param[in] oflags File status flags and file access modes of the\r | |
639 | open file description.\r | |
640 | @param[in] mode File access permission bits as defined in\r | |
6c6c850a | 641 | <sys/stat.h>. Only used if a file is created\r |
642 | as a result of the open.\r | |
a7a8363d | 643 | \r |
644 | @return Upon successful completion, open() opens the file and returns\r | |
645 | a non-negative integer representing the lowest numbered\r | |
646 | unused file descriptor. Otherwise, open returns -1 and sets\r | |
647 | errno to indicate the error. If a negative value is\r | |
648 | returned, no files are created or modified.\r | |
6c6c850a | 649 | - EMFILE - No file descriptors available -- Max number already open.\r |
650 | - EINVAL - Bad value specified for oflags or mode.\r | |
651 | - ENOMEM - Failure allocating memory for internal buffers.\r | |
652 | - EEXIST - File exists and open attempted with (O_EXCL | O_CREAT) set.\r | |
653 | - EIO - UEFI failure. Check value in EFIerrno.\r | |
2aa62f2b | 654 | **/\r |
655 | int\r | |
d7ce7006 | 656 | open(\r |
657 | const char *path,\r | |
658 | int oflags,\r | |
659 | int mode\r | |
660 | )\r | |
2aa62f2b | 661 | {\r |
53e1e5c6 | 662 | wchar_t *NewPath;\r |
d7ce7006 | 663 | wchar_t *MPath;\r |
53e1e5c6 | 664 | DeviceNode *Node;\r |
53e1e5c6 | 665 | struct __filedes *filp;\r |
6c6c850a | 666 | struct termios *Termio;\r |
53e1e5c6 | 667 | int Instance = 0;\r |
2aa62f2b | 668 | RETURN_STATUS Status;\r |
6c6c850a | 669 | UINT32 OpenMode;\r |
2aa62f2b | 670 | int fd = -1;\r |
53e1e5c6 | 671 | int doresult;\r |
2aa62f2b | 672 | \r |
d7ce7006 | 673 | Status = ParsePath(path, &NewPath, &Node, &Instance, &MPath);\r |
53e1e5c6 | 674 | if(Status == RETURN_SUCCESS) {\r |
450ea6d5 DM |
675 | if ((Node == NULL) ||\r |
676 | (Node->InstanceList == NULL))\r | |
6c6c850a | 677 | {\r |
53e1e5c6 | 678 | errno = EPERM;\r |
6c6c850a | 679 | }\r |
53e1e5c6 | 680 | else {\r |
450ea6d5 DM |
681 | // Could add a test to see if the file name begins with a period.\r |
682 | // If it does, then add the HIDDEN flag to Attributes.\r | |
2aa62f2b | 683 | \r |
450ea6d5 | 684 | // Get an available fd\r |
53e1e5c6 | 685 | fd = FindFreeFD( VALID_CLOSED );\r |
2aa62f2b | 686 | \r |
450ea6d5 DM |
687 | if( fd < 0 ) {\r |
688 | // All available FDs are in use\r | |
689 | errno = EMFILE;\r | |
690 | }\r | |
d7ce7006 | 691 | else {\r |
450ea6d5 DM |
692 | filp = &gMD->fdarray[fd];\r |
693 | // Save the flags and mode in the File Descriptor\r | |
694 | filp->Oflags = oflags;\r | |
695 | filp->Omode = mode;\r | |
53e1e5c6 | 696 | \r |
d7ce7006 | 697 | doresult = Node->OpenFunc(Node, filp, Instance, NewPath, MPath);\r |
450ea6d5 DM |
698 | if(doresult < 0) {\r |
699 | filp->f_iflags = 0; // Release this FD\r | |
700 | fd = -1; // Indicate an error\r | |
6c6c850a | 701 | }\r |
450ea6d5 DM |
702 | else {\r |
703 | // Build our final f_iflags value\r | |
704 | OpenMode = ( mode & S_ACC_READ ) ? S_ACC_READ : 0;\r | |
705 | OpenMode |= ( mode & S_ACC_WRITE ) ? S_ACC_WRITE : 0;\r | |
706 | \r | |
707 | filp->f_iflags |= OpenMode;\r | |
708 | \r | |
709 | if((oflags & O_TTY_INIT) && (filp->f_iflags & _S_ITTY) && (filp->devdata != NULL)) {\r | |
710 | // Initialize the device's termios flags to a "sane" value\r | |
711 | Termio = &((cIIO *)filp->devdata)->Termio;\r | |
712 | Termio->c_iflag = ICRNL | IGNSPEC;\r | |
713 | Termio->c_oflag = OPOST | ONLCR | OXTABS | ONOEOT | ONOCR | ONLRET | OCTRL;\r | |
714 | Termio->c_lflag = ECHO | ECHOE | ECHONL | ICANON;\r | |
715 | Termio->c_cc[VERASE] = 0x08; // ^H Backspace\r | |
716 | Termio->c_cc[VKILL] = 0x15; // ^U\r | |
717 | Termio->c_cc[VINTR] = 0x03; // ^C Interrupt character\r | |
2aa62f2b | 718 | }\r |
450ea6d5 DM |
719 | ++filp->RefCount;\r |
720 | FILE_SET_MATURE(filp);\r | |
721 | }\r | |
722 | }\r | |
d7ce7006 | 723 | }\r |
53e1e5c6 | 724 | free(NewPath);\r |
450ea6d5 DM |
725 | }\r |
726 | free(MPath); // We don't need this any more.\r | |
6c6c850a | 727 | \r |
53e1e5c6 | 728 | // return the fd of our now open file\r |
729 | return fd;\r | |
730 | }\r | |
731 | \r | |
732 | \r | |
733 | /**\r | |
734 | Poll a list of file descriptors.\r | |
735 | \r | |
736 | The ::poll routine waits for up to timeout milliseconds for an event\r | |
737 | to occur on one or more of the file descriptors listed. The event\r | |
738 | types of interested are specified for each file descriptor in the events\r | |
739 | field. The actual event detected is returned in the revents field of\r | |
740 | the array. The\r | |
741 | <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html">POSIX</a>\r | |
742 | documentation is available online.\r | |
743 | \r | |
6c6c850a | 744 | @param[in] pfd Address of an array of pollfd structures.\r |
53e1e5c6 | 745 | \r |
6c6c850a | 746 | @param[in] nfds Number of elements in the array of pollfd structures.\r |
53e1e5c6 | 747 | \r |
6c6c850a | 748 | @param[in] timeout Length of time in milliseconds to wait for the event\r |
53e1e5c6 | 749 | \r |
7dc13291 | 750 | @return The number of file descriptors with detected events. Zero\r |
53e1e5c6 | 751 | indicates that the call timed out and -1 indicates an error.\r |
752 | \r | |
753 | **/\r | |
754 | int\r | |
755 | poll (\r | |
756 | struct pollfd * pfd,\r | |
757 | nfds_t nfds,\r | |
758 | int timeout\r | |
759 | )\r | |
760 | {\r | |
761 | struct __filedes * pDescriptor;\r | |
762 | struct pollfd * pEnd;\r | |
763 | struct pollfd * pPollFD;\r | |
764 | int SelectedFDs;\r | |
765 | EFI_STATUS Status;\r | |
766 | EFI_EVENT Timer;\r | |
767 | UINT64 TimerTicks;\r | |
768 | \r | |
769 | //\r | |
770 | // Create the timer for the timeout\r | |
771 | //\r | |
772 | Timer = NULL;\r | |
773 | Status = EFI_SUCCESS;\r | |
774 | if ( INFTIM != timeout ) {\r | |
775 | Status = gBS->CreateEvent ( EVT_TIMER,\r | |
776 | TPL_NOTIFY,\r | |
777 | NULL,\r | |
778 | NULL,\r | |
779 | &Timer );\r | |
780 | if ( !EFI_ERROR ( Status )) {\r | |
781 | //\r | |
782 | // Start the timeout timer\r | |
783 | //\r | |
784 | TimerTicks = timeout;\r | |
785 | TimerTicks *= 1000 * 10;\r | |
786 | Status = gBS->SetTimer ( Timer,\r | |
787 | TimerRelative,\r | |
788 | TimerTicks );\r | |
789 | }\r | |
790 | else {\r | |
791 | SelectedFDs = -1;\r | |
792 | errno = ENOMEM;\r | |
793 | }\r | |
794 | }\r | |
795 | if ( !EFI_ERROR ( Status )) {\r | |
796 | //\r | |
797 | // Poll until an event is detected or the timer fires\r | |
798 | //\r | |
799 | SelectedFDs = 0;\r | |
800 | errno = 0;\r | |
801 | do {\r | |
802 | //\r | |
803 | // Poll the list of file descriptors\r | |
804 | //\r | |
805 | pPollFD = pfd;\r | |
806 | pEnd = &pPollFD [ nfds ];\r | |
807 | while ( pEnd > pPollFD ) {\r | |
808 | //\r | |
809 | // Validate the file descriptor\r | |
810 | //\r | |
811 | if ( !ValidateFD ( pPollFD->fd, VALID_OPEN )) {\r | |
812 | errno = EINVAL;\r | |
2aa62f2b | 813 | return -1;\r |
814 | }\r | |
53e1e5c6 | 815 | \r |
816 | //\r | |
817 | // Poll the device or file\r | |
818 | //\r | |
819 | pDescriptor = &gMD->fdarray [ pPollFD->fd ];\r | |
820 | pPollFD->revents = pDescriptor->f_ops->fo_poll ( pDescriptor,\r | |
821 | pPollFD->events );\r | |
822 | \r | |
823 | //\r | |
824 | // Determine if this file descriptor detected an event\r | |
825 | //\r | |
826 | if ( 0 != pPollFD->revents ) {\r | |
827 | //\r | |
828 | // Select this descriptor\r | |
829 | //\r | |
830 | SelectedFDs += 1;\r | |
831 | }\r | |
832 | \r | |
833 | //\r | |
834 | // Set the next file descriptor\r | |
835 | //\r | |
836 | pPollFD += 1;\r | |
2aa62f2b | 837 | }\r |
53e1e5c6 | 838 | \r |
839 | //\r | |
840 | // Check for timeout\r | |
841 | //\r | |
842 | if ( NULL != Timer ) {\r | |
843 | Status = gBS->CheckEvent ( Timer );\r | |
844 | if ( EFI_SUCCESS == Status ) {\r | |
845 | //\r | |
846 | // Timeout\r | |
847 | //\r | |
848 | break;\r | |
849 | }\r | |
850 | else if ( EFI_NOT_READY == Status ) {\r | |
851 | Status = EFI_SUCCESS;\r | |
2aa62f2b | 852 | }\r |
2aa62f2b | 853 | }\r |
53e1e5c6 | 854 | } while (( 0 == SelectedFDs )\r |
855 | && ( EFI_SUCCESS == Status ));\r | |
d7ce7006 | 856 | \r |
53e1e5c6 | 857 | //\r |
858 | // Stop the timer\r | |
859 | //\r | |
860 | if ( NULL != Timer ) {\r | |
861 | gBS->SetTimer ( Timer,\r | |
862 | TimerCancel,\r | |
863 | 0 );\r | |
2aa62f2b | 864 | }\r |
2aa62f2b | 865 | }\r |
866 | else {\r | |
53e1e5c6 | 867 | SelectedFDs = -1;\r |
868 | errno = EAGAIN;\r | |
2aa62f2b | 869 | }\r |
870 | \r | |
53e1e5c6 | 871 | //\r |
872 | // Release the timer\r | |
873 | //\r | |
874 | if ( NULL != Timer ) {\r | |
875 | gBS->CloseEvent ( Timer );\r | |
876 | }\r | |
2aa62f2b | 877 | \r |
53e1e5c6 | 878 | //\r |
879 | // Return the number of selected file system descriptors\r | |
880 | //\r | |
881 | return SelectedFDs;\r | |
882 | }\r | |
2aa62f2b | 883 | \r |
2aa62f2b | 884 | \r |
2aa62f2b | 885 | /** The rename() function changes the name of a file.\r |
6c6c850a | 886 | The From argument points to the pathname of the file to be renamed. The To\r |
2aa62f2b | 887 | argument points to the new pathname of the file.\r |
888 | \r | |
6c6c850a | 889 | If the From argument points to the pathname of a file that is not a\r |
890 | directory, the To argument shall not point to the pathname of a\r | |
891 | directory. If the file named by the To argument exists, it shall be\r | |
892 | removed and From renamed to To. Write access permission is required for\r | |
893 | both the directory containing old and the directory containing To.\r | |
2aa62f2b | 894 | \r |
6c6c850a | 895 | If the From argument points to the pathname of a directory, the To\r |
2aa62f2b | 896 | argument shall not point to the pathname of a file that is not a\r |
6c6c850a | 897 | directory. If the directory named by the To argument exists, it shall be\r |
898 | removed and From renamed to To.\r | |
2aa62f2b | 899 | \r |
6c6c850a | 900 | The To pathname shall not contain a path prefix that names From. Write\r |
901 | access permission is required for the directory containing From and the\r | |
902 | directory containing To. If the From argument points to the pathname of a\r | |
2aa62f2b | 903 | directory, write access permission may be required for the directory named\r |
6c6c850a | 904 | by From, and, if it exists, the directory named by To.\r |
2aa62f2b | 905 | \r |
906 | If the rename() function fails for any reason other than [EIO], any file\r | |
6c6c850a | 907 | named by To shall be unaffected.\r |
2aa62f2b | 908 | \r |
6c6c850a | 909 | @param[in] From Path to the file to be renamed.\r |
910 | @param[in] To The new name of From.\r | |
911 | \r | |
912 | @retval 0 Successful completion.\r | |
913 | @retval -1 An error has occured and errno has been set to further specify the error.\r | |
914 | Neither the file named by From nor the file named by To are\r | |
915 | changed or created.\r | |
916 | - ENXIO: Path specified is not supported by any loaded driver.\r | |
917 | - ENOMEM: Insufficient memory to calloc a MapName buffer.\r | |
918 | - EINVAL: The path parameter is not valid.\r | |
2aa62f2b | 919 | **/\r |
920 | int\r | |
53e1e5c6 | 921 | rename(\r |
6c6c850a | 922 | const char *From,\r |
923 | const char *To\r | |
53e1e5c6 | 924 | )\r |
2aa62f2b | 925 | {\r |
53e1e5c6 | 926 | wchar_t *FromPath;\r |
927 | DeviceNode *FromNode;\r | |
928 | char *GenI;\r | |
929 | int Instance = 0;\r | |
930 | RETURN_STATUS Status;\r | |
931 | int retval = -1;\r | |
932 | \r | |
6c6c850a | 933 | Status = ParsePath(From, &FromPath, &FromNode, &Instance, NULL);\r |
2aa62f2b | 934 | if(Status == RETURN_SUCCESS) {\r |
53e1e5c6 | 935 | GenI = FromNode->InstanceList;\r |
936 | if(GenI == NULL) {\r | |
937 | errno = EPERM;\r | |
938 | retval = -1;\r | |
2aa62f2b | 939 | }\r |
940 | else {\r | |
d7ce7006 | 941 | //GenI += (Instance * FromNode->InstanceSize);\r |
6c6c850a | 942 | retval = ((GenericInstance *)GenI)->Abstraction.fo_rename( From, To);\r |
2aa62f2b | 943 | }\r |
53e1e5c6 | 944 | free(FromPath);\r |
2aa62f2b | 945 | }\r |
53e1e5c6 | 946 | return retval;\r |
2aa62f2b | 947 | }\r |
948 | \r | |
6c6c850a | 949 | /** Delete a specified directory.\r |
950 | \r | |
951 | @param[in] path Path to the directory to delete.\r | |
952 | \r | |
953 | @retval -1 The directory couldn't be opened (doesn't exist).\r | |
954 | @retval -1 The directory wasn't empty or an IO error occured.\r | |
53e1e5c6 | 955 | **/\r |
956 | int\r | |
53e1e5c6 | 957 | rmdir(\r |
958 | const char *path\r | |
959 | )\r | |
2aa62f2b | 960 | {\r |
53e1e5c6 | 961 | struct __filedes *filp;\r |
962 | int fd;\r | |
963 | int retval = -1;\r | |
964 | \r | |
965 | fd = open(path, O_RDWR, 0);\r | |
966 | if(fd >= 0) {\r | |
967 | filp = &gMD->fdarray[fd];\r | |
968 | \r | |
969 | retval = filp->f_ops->fo_rmdir(filp);\r | |
0c1992fb | 970 | filp->f_iflags = 0; // Close this FD\r |
971 | filp->RefCount = 0; // No one using this FD\r | |
48831246 | 972 | }\r |
53e1e5c6 | 973 | return retval;\r |
2aa62f2b | 974 | }\r |
975 | \r | |
976 | /** The fstat() function obtains information about an open file associated\r | |
6c6c850a | 977 | with the file descriptor fd, and writes it to the area pointed to\r |
978 | by statbuf.\r | |
2aa62f2b | 979 | \r |
6c6c850a | 980 | The statbuf argument is a pointer to a stat structure, as defined\r |
2aa62f2b | 981 | in <sys/stat.h>, into which information is placed concerning the file.\r |
982 | \r | |
983 | The structure members st_mode, st_ino, st_dev, st_uid, st_gid, st_atime,\r | |
984 | st_ctime, and st_mtime shall have meaningful values. The value of the\r | |
985 | member st_nlink shall be set to the number of links to the file.\r | |
986 | \r | |
987 | The fstat() function shall update any time-related fields before writing\r | |
988 | into the stat structure.\r | |
989 | \r | |
990 | The fstat() function is implemented using the ShellGetFileInfo()\r | |
991 | function.\r | |
992 | \r | |
993 | The stat structure members which don't have direct analogs to EFI file\r | |
994 | information are filled in as follows:\r | |
6c6c850a | 995 | - st_mode Populated with information from fd\r |
2aa62f2b | 996 | - st_ino Set to zero. (inode)\r |
997 | - st_dev Set to zero.\r | |
998 | - st_uid Set to zero.\r | |
999 | - st_gid Set to zero.\r | |
1000 | - st_nlink Set to one.\r | |
1001 | \r | |
53e1e5c6 | 1002 | @param[in] fd File descriptor as returned from open().\r |
2aa62f2b | 1003 | @param[out] statbuf Buffer in which the file status is put.\r |
1004 | \r | |
1005 | @retval 0 Successful Completion.\r | |
1006 | @retval -1 An error has occurred and errno has been set to\r | |
1007 | identify the error.\r | |
1008 | **/\r | |
1009 | int\r | |
53e1e5c6 | 1010 | fstat (int fd, struct stat *statbuf)\r |
2aa62f2b | 1011 | {\r |
53e1e5c6 | 1012 | int retval = -1;\r |
1013 | struct __filedes *filp;\r | |
2aa62f2b | 1014 | \r |
53e1e5c6 | 1015 | if(ValidateFD( fd, VALID_OPEN)) {\r |
1016 | filp = &gMD->fdarray[fd];\r | |
1017 | retval = filp->f_ops->fo_stat(filp, statbuf, NULL);\r | |
2aa62f2b | 1018 | }\r |
1019 | else {\r | |
53e1e5c6 | 1020 | errno = EBADF;\r |
2aa62f2b | 1021 | }\r |
53e1e5c6 | 1022 | return retval;\r |
2aa62f2b | 1023 | }\r |
1024 | \r | |
1025 | /** Obtains information about the file pointed to by path.\r | |
1026 | \r | |
1027 | Opens the file pointed to by path, calls _EFI_FileInfo with the file's handle,\r | |
1028 | then closes the file.\r | |
1029 | \r | |
6c6c850a | 1030 | @param[in] path Path to the file to obtain information about.\r |
1031 | @param[out] statbuf Buffer in which the file status is put.\r | |
1032 | \r | |
2aa62f2b | 1033 | @retval 0 Successful Completion.\r |
1034 | @retval -1 An error has occurred and errno has been set to\r | |
1035 | identify the error.\r | |
1036 | **/\r | |
1037 | int\r | |
41b152c5 | 1038 | stat (const char *path, struct stat *statbuf)\r |
2aa62f2b | 1039 | {\r |
53e1e5c6 | 1040 | int fd;\r |
1041 | int retval = -1;\r | |
1042 | struct __filedes *filp;\r | |
1043 | \r | |
1044 | fd = open(path, O_RDONLY, 0);\r | |
1045 | if(fd >= 0) {\r | |
1046 | filp = &gMD->fdarray[fd];\r | |
1047 | retval = filp->f_ops->fo_stat( filp, statbuf, NULL);\r | |
1048 | close(fd);\r | |
2aa62f2b | 1049 | }\r |
53e1e5c6 | 1050 | return retval;\r |
2aa62f2b | 1051 | }\r |
1052 | \r | |
6c6c850a | 1053 | /** Same as stat since EFI doesn't have symbolic links.\r |
1054 | \r | |
1055 | @param[in] path Path to the file to obtain information about.\r | |
1056 | @param[out] statbuf Buffer in which the file status is put.\r | |
1057 | \r | |
1058 | @retval 0 Successful Completion.\r | |
1059 | @retval -1 An error has occurred and errno has been set to\r | |
1060 | identify the error.\r | |
1061 | **/\r | |
2aa62f2b | 1062 | int\r |
1063 | lstat (const char *path, struct stat *statbuf)\r | |
1064 | {\r | |
1065 | return stat(path, statbuf);\r | |
1066 | }\r | |
1067 | \r | |
53e1e5c6 | 1068 | /** Control a device.\r |
6c6c850a | 1069 | \r |
1070 | @param[in] fd Descriptor for the file to be acted upon.\r | |
1071 | @param[in] request Specifies the operation to perform.\r | |
1072 | @param[in,out] ... Zero or more parameters as required for request.\r | |
1073 | \r | |
1074 | @retval >=0 The operation completed successfully.\r | |
1075 | @retval -1 An error occured. More information is in errno.\r | |
53e1e5c6 | 1076 | **/\r |
1077 | int\r | |
1078 | ioctl(\r | |
1079 | int fd,\r | |
1080 | unsigned long request,\r | |
1081 | ...\r | |
1082 | )\r | |
1083 | {\r | |
1084 | int retval = -1;\r | |
1085 | struct __filedes *filp;\r | |
1086 | va_list argp;\r | |
1087 | \r | |
1088 | va_start(argp, request);\r | |
1089 | \r | |
1090 | if(ValidateFD( fd, VALID_OPEN)) {\r | |
1091 | filp = &gMD->fdarray[fd];\r | |
0c1992fb | 1092 | \r |
1093 | if(request == FIODLEX) {\r | |
1094 | /* set Delete-on-Close */\r | |
1095 | filp->f_iflags |= FIF_DELCLOSE;\r | |
1096 | retval = 0;\r | |
1097 | }\r | |
1098 | else if(request == FIONDLEX) {\r | |
1099 | /* clear Delete-on-Close */\r | |
1100 | filp->f_iflags &= ~FIF_DELCLOSE;\r | |
1101 | retval = 0;\r | |
1102 | }\r | |
1103 | else {\r | |
1104 | /* All other requests. */\r | |
48831246 | 1105 | retval = filp->f_ops->fo_ioctl(filp, request, argp);\r |
1106 | }\r | |
0c1992fb | 1107 | }\r |
53e1e5c6 | 1108 | else {\r |
1109 | errno = EBADF;\r | |
1110 | }\r | |
1111 | va_end(argp);\r | |
1112 | \r | |
1113 | return retval;\r | |
1114 | }\r | |
1115 | \r | |
2aa62f2b | 1116 | /** Read from a file.\r |
1117 | \r | |
1118 | The read() function shall attempt to read nbyte bytes from the file\r | |
1119 | associated with the open file descriptor, fildes, into the buffer pointed\r | |
1120 | to by buf.\r | |
1121 | \r | |
1122 | Before any action described below is taken, and if nbyte is zero, the\r | |
1123 | read() function may detect and return errors as described below. In the\r | |
1124 | absence of errors, or if error detection is not performed, the read()\r | |
1125 | function shall return zero and have no other results.\r | |
1126 | \r | |
1127 | On files that support seeking (for example, a regular file), the read()\r | |
1128 | shall start at a position in the file given by the file offset associated\r | |
1129 | with fildes. The file offset shall be incremented by the number of bytes\r | |
1130 | actually read.\r | |
1131 | \r | |
1132 | Files that do not support seeking - for example, terminals - always read\r | |
1133 | from the current position. The value of a file offset associated with\r | |
1134 | such a file is undefined.\r | |
1135 | \r | |
1136 | No data transfer shall occur past the current end-of-file. If the\r | |
1137 | starting position is at or after the end-of-file, 0 shall be returned.\r | |
1138 | \r | |
1139 | The read() function reads data previously written to a file. If any\r | |
1140 | portion of a regular file prior to the end-of-file has not been written,\r | |
1141 | read() shall return bytes with value 0. For example, lseek() allows the\r | |
1142 | file offset to be set beyond the end of existing data in the file. If data\r | |
1143 | is later written at this point, subsequent reads in the gap between the\r | |
1144 | previous end of data and the newly written data shall return bytes with\r | |
1145 | value 0 until data is written into the gap.\r | |
1146 | \r | |
1147 | Upon successful completion, where nbyte is greater than 0, read() shall\r | |
1148 | mark for update the st_atime field of the file, and shall return the\r | |
1149 | number of bytes read. This number shall never be greater than nbyte. The\r | |
1150 | value returned may be less than nbyte if the number of bytes left in the\r | |
1151 | file is less than nbyte, if the read() request was interrupted by a\r | |
1152 | signal, or if the file is a pipe or FIFO or special file and has fewer\r | |
1153 | than nbyte bytes immediately available for reading. For example, a read()\r | |
1154 | from a file associated with a terminal may return one typed line of data.\r | |
1155 | \r | |
1156 | If fildes does not refer to a directory, the function reads the requested\r | |
d7ce7006 | 1157 | number of bytes from the file at the file's current position and returns\r |
2aa62f2b | 1158 | them in buf. If the read goes beyond the end of the file, the read\r |
d7ce7006 | 1159 | length is truncated to the end of the file. The file's current position is\r |
2aa62f2b | 1160 | increased by the number of bytes returned.\r |
1161 | \r | |
1162 | If fildes refers to a directory, the function reads the directory entry at\r | |
d7ce7006 | 1163 | the file's current position and returns the entry in buf. If buf\r |
2aa62f2b | 1164 | is not large enough to hold the current directory entry, then\r |
1165 | errno is set to EBUFSIZE, EFIerrno is set to EFI_BUFFER_TOO_SMALL, and the\r | |
1166 | current file position is not updated. The size of the buffer needed to read\r | |
1167 | the entry will be returned as a negative number. On success, the current\r | |
1168 | position is updated to the next directory entry. If there are no more\r | |
1169 | directory entries, the read returns a zero-length buffer.\r | |
1170 | EFI_FILE_INFO is the structure returned as the directory entry.\r | |
1171 | \r | |
6c6c850a | 1172 | @param[in] fildes Descriptor of the file to be read.\r |
1173 | @param[out] buf Pointer to location in which to store the read data.\r | |
1174 | @param[in] nbyte Maximum number of bytes to be read.\r | |
1175 | \r | |
2aa62f2b | 1176 | @return Upon successful completion, read() returns a non-negative integer\r |
1177 | indicating the number of bytes actually read. Otherwise, the\r | |
1178 | functions return a negative value and sets errno to indicate the\r | |
1179 | error. If errno is EBUFSIZE, the absolute value of the\r | |
1180 | return value indicates the size of the buffer needed to read\r | |
1181 | the directory entry.\r | |
1182 | **/\r | |
1183 | ssize_t\r | |
1184 | read (int fildes, void *buf, size_t nbyte)\r | |
1185 | {\r | |
53e1e5c6 | 1186 | struct __filedes *filp;\r |
6c6c850a | 1187 | cIIO *IIO;\r |
2aa62f2b | 1188 | ssize_t BufSize;\r |
2aa62f2b | 1189 | \r |
1190 | BufSize = (ssize_t)nbyte;\r | |
6c6c850a | 1191 | if(BufSize > 0) {\r |
1192 | if(ValidateFD( fildes, VALID_OPEN)) {\r | |
1193 | filp = &gMD->fdarray[fildes];\r | |
53e1e5c6 | 1194 | \r |
6c6c850a | 1195 | IIO = filp->devdata;\r |
1196 | if(isatty(fildes) && (IIO != NULL)) {\r | |
1197 | BufSize = IIO->Read(filp, nbyte, buf);\r | |
1198 | }\r | |
1199 | else {\r | |
1200 | BufSize = filp->f_ops->fo_read(filp, &filp->f_offset, nbyte, buf);\r | |
1201 | }\r | |
1202 | }\r | |
1203 | else {\r | |
1204 | errno = EBADF;\r | |
1205 | BufSize = -1;\r | |
1206 | }\r | |
2aa62f2b | 1207 | }\r |
1208 | return BufSize;\r | |
1209 | }\r | |
1210 | \r | |
2aa62f2b | 1211 | /** Write data to a file.\r |
1212 | \r | |
6c6c850a | 1213 | This function writes the specified number of bytes to the file at the current\r |
1214 | file position. The current file position is advanced the actual number of bytes\r | |
450ea6d5 DM |
1215 | written. Partial writes only occur when there has been a data error during\r |
1216 | the write attempt (such as "volume space full"). The file is automatically\r | |
1217 | grown to hold the data if required.\r | |
1218 | \r | |
1219 | Direct writes to opened directories are not supported.\r | |
6c6c850a | 1220 | \r |
1221 | If fildes refers to a terminal device, isatty() returns TRUE, a partial write\r | |
1222 | will occur if a NULL or EOF character is encountered before n characters have\r | |
450ea6d5 DM |
1223 | been written. Characters inserted due to line-end translations or TAB\r |
1224 | expansion will not be counted. Unconvertable characters are translated into\r | |
1225 | the UEFI character BLOCKELEMENT_LIGHT_SHADE.\r | |
6c6c850a | 1226 | \r |
1227 | Since the UEFI console device works on wide characters, the buffer is assumed\r | |
450ea6d5 DM |
1228 | to contain a byte-oriented multi-byte character stream which is then\r |
1229 | translated to wide characters using the mbtowc() functions. The resulting\r | |
1230 | wide character stream is what is actually sent to the UEFI console.\r | |
1231 | \r | |
1232 | Although both text and binary wide-oriented streams are conceptually\r | |
1233 | sequences of wide characters, the external file associated with a\r | |
1234 | wide-oriented stream is a sequence of multibyte characters,\r | |
1235 | generalized as follows:\r | |
1236 | - Multibyte encodings within files may contain embedded null bytes\r | |
1237 | (unlike multibyte encodings valid for use internal to the program).\r | |
1238 | - A file need not begin nor end in the initial shift state.\r | |
6c6c850a | 1239 | \r |
1240 | @param[in] fd Descriptor of file to be written to.\r | |
1241 | @param[in] buf Pointer to data to write to the file.\r | |
1242 | @param[in] nbyte Number of bytes to be written to the file.\r | |
1243 | \r | |
1244 | @retval >=0 Number of bytes actually written to the file.\r | |
1245 | @retval <0 An error occurred. More data is provided by errno.\r | |
2aa62f2b | 1246 | **/\r |
1247 | ssize_t\r | |
53e1e5c6 | 1248 | write (int fd, const void *buf, size_t nbyte)\r |
2aa62f2b | 1249 | {\r |
53e1e5c6 | 1250 | struct __filedes *filp;\r |
6c6c850a | 1251 | cIIO *IIO;\r |
2aa62f2b | 1252 | ssize_t BufSize;\r |
53e1e5c6 | 1253 | \r |
1254 | BufSize = (ssize_t)nbyte;\r | |
2aa62f2b | 1255 | \r |
53e1e5c6 | 1256 | if(ValidateFD( fd, VALID_OPEN)) {\r |
1257 | filp = &gMD->fdarray[fd];\r | |
6c6c850a | 1258 | if ((filp->Oflags & O_ACCMODE) != 0) {\r |
1259 | // File is open for writing\r | |
1260 | IIO = filp->devdata;\r | |
1261 | if(isatty(fd) && (IIO != NULL)) {\r | |
1262 | // Output to an Interactive I/O device\r | |
450ea6d5 | 1263 | // (Terminal device or the slave side of a pseudo-tty)\r |
6c6c850a | 1264 | BufSize = IIO->Write(filp, buf, nbyte);\r |
1265 | }\r | |
1266 | else {\r | |
450ea6d5 | 1267 | // Output to a regular file, socket, pipe, etc.\r |
6c6c850a | 1268 | BufSize = filp->f_ops->fo_write(filp, &filp->f_offset, nbyte, buf);\r |
1269 | }\r | |
2aa62f2b | 1270 | }\r |
1271 | else {\r | |
6c6c850a | 1272 | // File is NOT open for writing\r |
1273 | errno = EINVAL;\r | |
1274 | BufSize = -1;\r | |
1275 | }\r | |
1276 | }\r | |
1277 | else {\r | |
1278 | // fd is not for a valid open file\r | |
53e1e5c6 | 1279 | errno = EBADF;\r |
6c6c850a | 1280 | BufSize = -1;\r |
1281 | }\r | |
53e1e5c6 | 1282 | return BufSize;\r |
1283 | }\r | |
1284 | \r | |
1285 | /** Gets the current working directory.\r | |
1286 | \r | |
7dc13291 | 1287 | The getcwd() function shall place an absolute pathname of the current\r |
0c1992fb | 1288 | working directory in the array pointed to by buf, and return buf.The\r |
1289 | size argument is the size in bytes of the character array pointed to\r | |
1290 | by the buf argument.\r | |
7dc13291 | 1291 | \r |
53e1e5c6 | 1292 | @param[in,out] buf The buffer to fill.\r |
1293 | @param[in] size The number of bytes in buffer.\r | |
1294 | \r | |
0c1992fb | 1295 | @retval NULL The function failed. The value in errno provides\r |
1296 | further information about the cause of the failure.\r | |
1297 | Values for errno are:\r | |
1298 | - EINVAL: buf is NULL or size is zero.\r | |
1299 | - ENOENT: directory does not exist.\r | |
1300 | - ERANGE: buf size is too small to hold CWD\r | |
1301 | \r | |
1302 | @retval buf The function completed successfully.\r | |
53e1e5c6 | 1303 | **/\r |
7dc13291 | 1304 | char\r |
1305 | *getcwd (char *buf, size_t size)\r | |
53e1e5c6 | 1306 | {\r |
1307 | CONST CHAR16 *Cwd;\r | |
1308 | \r | |
1309 | if (size == 0 || buf == NULL) {\r | |
1310 | errno = EINVAL;\r | |
1311 | return NULL;\r | |
2aa62f2b | 1312 | }\r |
53e1e5c6 | 1313 | \r |
1314 | Cwd = ShellGetCurrentDir(NULL);\r | |
1315 | if (Cwd == NULL) {\r | |
0c1992fb | 1316 | errno = ENOENT;\r |
53e1e5c6 | 1317 | return NULL;\r |
2aa62f2b | 1318 | }\r |
53e1e5c6 | 1319 | if (size < ((StrLen (Cwd) + 1) * sizeof (CHAR8))) {\r |
1320 | errno = ERANGE;\r | |
1321 | return (NULL);\r | |
2aa62f2b | 1322 | }\r |
53e1e5c6 | 1323 | return (UnicodeStrToAsciiStr(Cwd, buf));\r |
2aa62f2b | 1324 | }\r |
d7ce7006 | 1325 | \r |
1326 | /** Change the current working directory.\r | |
1327 | \r | |
1328 | The chdir() function shall cause the directory named by the pathname\r | |
1329 | pointed to by the path argument to become the current working directory;\r | |
1330 | that is, the starting point for path searches for pathnames not beginning\r | |
1331 | with '/'.\r | |
1332 | \r | |
1333 | @param[in] path The new path to set.\r | |
1334 | \r | |
0c1992fb | 1335 | @retval 0 Operation completed successfully.\r |
1336 | @retval -1 Function failed. The value in errno provides more\r | |
1337 | information on the cause of failure:\r | |
1338 | - EPERM: Operation not supported with this Shell version.\r | |
1339 | - ENOMEM: Unable to allocate memory.\r | |
1340 | - ENOENT: Target directory does not exist.\r | |
1341 | \r | |
1342 | @todo Add non-NEW-shell CWD changing.\r | |
d7ce7006 | 1343 | **/\r |
1344 | int\r | |
1345 | chdir (const char *path)\r | |
1346 | {\r | |
1347 | CONST CHAR16 *Cwd;\r | |
1348 | EFI_STATUS Status;\r | |
1349 | CHAR16 *UnicodePath;\r | |
1350 | \r | |
0c1992fb | 1351 | /* Old Shell does not support Set Current Dir. */\r |
1352 | if(gEfiShellProtocol != NULL) {\r | |
48831246 | 1353 | Cwd = ShellGetCurrentDir(NULL);\r |
1354 | if (Cwd != NULL) {\r | |
1355 | /* We have shell support */\r | |
1356 | UnicodePath = AllocatePool(((AsciiStrLen (path) + 1) * sizeof (CHAR16)));\r | |
1357 | if (UnicodePath == NULL) {\r | |
1358 | errno = ENOMEM;\r | |
1359 | return -1;\r | |
1360 | }\r | |
1361 | AsciiStrToUnicodeStr(path, UnicodePath);\r | |
1362 | Status = gEfiShellProtocol->SetCurDir(NULL, UnicodePath);\r | |
1363 | FreePool(UnicodePath);\r | |
1364 | if (EFI_ERROR(Status)) {\r | |
0c1992fb | 1365 | errno = ENOENT;\r |
48831246 | 1366 | return -1;\r |
1367 | } else {\r | |
1368 | return 0;\r | |
1369 | }\r | |
d7ce7006 | 1370 | }\r |
1371 | }\r | |
d7ce7006 | 1372 | /* Add here for non-shell */\r |
0c1992fb | 1373 | errno = EPERM;\r |
d7ce7006 | 1374 | return -1;\r |
1375 | }\r | |
1376 | \r | |
6c6c850a | 1377 | /** Get the foreground process group ID associated with a terminal.\r |
1378 | \r | |
1379 | Just returns the Image Handle for the requestor since UEFI does not have\r | |
1380 | a concept of processes or groups.\r | |
1381 | \r | |
1382 | @param[in] x Ignored.\r | |
1383 | \r | |
1384 | @return Returns the Image Handle of the application or driver which\r | |
1385 | called this function.\r | |
1386 | **/\r | |
d7ce7006 | 1387 | pid_t tcgetpgrp (int x)\r |
1388 | {\r | |
1389 | return ((pid_t)(UINTN)(gImageHandle));\r | |
1390 | }\r | |
1391 | \r | |
6c6c850a | 1392 | /** Get the process group ID of the calling process.\r |
1393 | \r | |
1394 | Just returns the Image Handle for the requestor since UEFI does not have\r | |
1395 | a concept of processes or groups.\r | |
1396 | \r | |
1397 | @return Returns the Image Handle of the application or driver which\r | |
1398 | called this function.\r | |
1399 | **/\r | |
d7ce7006 | 1400 | pid_t getpgrp(void)\r |
1401 | {\r | |
1402 | return ((pid_t)(UINTN)(gImageHandle));\r | |
1403 | }\r | |
1404 | \r | |
48831246 | 1405 | /* Internal worker function for utimes.\r |
1406 | This works around an error produced by GCC when the va_* macros\r | |
1407 | are used within a function with a fixed number of arguments.\r | |
1408 | */\r | |
1409 | static\r | |
0c1992fb | 1410 | int\r |
48831246 | 1411 | EFIAPI\r |
1412 | va_Utimes(\r | |
1413 | const char *path,\r | |
1414 | ...\r | |
0c1992fb | 1415 | )\r |
1416 | {\r | |
1417 | struct __filedes *filp;\r | |
1418 | va_list ap;\r | |
1419 | int fd;\r | |
1420 | int retval = -1;\r | |
1421 | \r | |
1422 | va_start(ap, path);\r | |
1423 | fd = open(path, O_RDWR, 0);\r | |
1424 | if(fd >= 0) {\r | |
1425 | filp = &gMD->fdarray[fd];\r | |
1426 | retval = filp->f_ops->fo_ioctl( filp, FIOSETIME, ap);\r | |
1427 | close(fd);\r | |
1428 | }\r | |
1429 | va_end(ap);\r | |
1430 | return retval;\r | |
48831246 | 1431 | }\r |
1432 | \r | |
1433 | /** Set file access and modification times.\r | |
0c1992fb | 1434 | \r |
6c6c850a | 1435 | @param[in] path Path to the file to be modified.\r |
1436 | @param[in] times Pointer to an array of two timeval structures\r | |
48831246 | 1437 | \r |
6c6c850a | 1438 | @retval 0 File times successfully set.\r |
1439 | @retval -1 An error occured. Error type in errno.\r | |
48831246 | 1440 | **/\r |
1441 | int\r | |
1442 | utimes(\r | |
1443 | const char *path,\r | |
1444 | const struct timeval *times\r | |
1445 | )\r | |
1446 | {\r | |
1447 | return va_Utimes(path, times);\r | |
0c1992fb | 1448 | }\r |