1 /* Low-level I/O routines for BFDs.
3 Copyright (C) 1990-2024 Free Software Foundation, Inc.
5 Written by Cygnus Support.
7 This file is part of BFD, the Binary File Descriptor library.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
22 MA 02110-1301, USA. */
35 #define S_IXUSR 0100 /* Execute by owner. */
38 #define S_IXGRP 0010 /* Execute by group. */
41 #define S_IXOTH 0001 /* Execute by others. */
49 _bfd_real_ftell (FILE *file
)
51 #if defined (HAVE_FTELLO64)
52 return ftello64 (file
);
53 #elif defined (HAVE_FTELLO)
61 _bfd_real_fseek (FILE *file
, file_ptr offset
, int whence
)
63 #if defined (HAVE_FSEEKO64)
64 return fseeko64 (file
, offset
, whence
);
65 #elif defined (HAVE_FSEEKO)
66 return fseeko (file
, offset
, whence
);
68 return fseek (file
, offset
, whence
);
72 /* Mark FILE as close-on-exec. Return FILE. FILE may be NULL, in
73 which case nothing is done. */
75 close_on_exec (FILE *file
)
77 #if defined (HAVE_FILENO) && defined (F_GETFD)
80 int fd
= fileno (file
);
81 int old
= fcntl (fd
, F_GETFD
, 0);
83 fcntl (fd
, F_SETFD
, old
| FD_CLOEXEC
);
90 _bfd_real_fopen (const char *filename
, const char *modes
)
95 /* On VMS, fopen allows file attributes as optional arguments.
96 We need to use them but we'd better to use the common prototype.
97 In fopen-vms.h, they are separated from the mode with a comma.
99 vms_attr
= strchr (modes
, ',');
100 if (vms_attr
!= NULL
)
102 /* Attributes found. Split. */
103 size_t modes_len
= strlen (modes
) + 1;
104 char attrs
[modes_len
+ 1];
108 memcpy (attrs
, modes
, modes_len
);
110 for (i
= 0; i
< 2; i
++)
112 at
[i
+ 1] = strchr (at
[i
], ',');
113 BFD_ASSERT (at
[i
+ 1] != NULL
);
114 *(at
[i
+ 1]++) = 0; /* Replace ',' with a nul, and skip it. */
116 return close_on_exec (fopen (filename
, at
[0], at
[1], at
[2]));
119 #elif defined (_WIN32)
120 /* PR 25713: Handle extra long path names possibly containing '..' and '.'. */
121 wchar_t ** lpFilePart
= {NULL
};
122 const wchar_t prefixDOS
[] = L
"\\\\?\\";
123 const wchar_t prefixUNC
[] = L
"\\\\?\\UNC\\";
124 const wchar_t prefixNone
[] = L
"";
125 const size_t partPathLen
= strlen (filename
) + 1;
126 const wchar_t * prefix
;
127 size_t sizeof_prefix
;
128 bool strip_network_prefix
= false;
130 /* PR 31527: In order to not hit limits in the maximum file path, all paths
131 need converting to Universal Naming Convention (UNC) syntax. The following
132 forms may be provided to this function and are converted accordingly.
134 1. UNC paths (start with \\?\), these are unconverted;
135 2. Network paths (start with \\ or // but not \\?\), these are given the
136 \\?UNC\ prefix, and have the incoming \\ or // removed;
137 3. DOS drive paths (a letter followed by a colon), these are given the
139 4. Paths relative to CWD, the current working directory is tested for the
140 above conditions, and otherwise are assumed to be DOS paths.
142 For more information see:
143 https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
146 if (startswith (filename
, "\\\\?\\"))
149 sizeof_prefix
= sizeof (prefixNone
);
151 else if (startswith (filename
, "\\\\") || startswith (filename
, "//"))
154 sizeof_prefix
= sizeof (prefixUNC
);
155 strip_network_prefix
= true;
157 else if (strlen (filename
) > 2 && filename
[1] == ':')
160 sizeof_prefix
= sizeof (prefixDOS
);
164 /* The partial path is relative to the current working directory, use this
165 to determine the prefix.
166 1) Get the length: Calling with lpBuffer set to null returns the length.
167 2) Resolve the path. */
168 size_t pwdWSize
= GetCurrentDirectoryW (0, NULL
);
169 wchar_t * pwdPath
= calloc (pwdWSize
, sizeof(wchar_t));
170 GetCurrentDirectoryW (pwdWSize
, pwdPath
);
171 if (wcsncmp (pwdPath
, L
"\\\\?\\", 6) == 0)
174 sizeof_prefix
= sizeof (prefixNone
);
176 else if (wcsncmp (pwdPath
, L
"\\\\", 2) == 0
177 || wcsncmp (pwdPath
, L
"//", 2) == 0)
180 sizeof_prefix
= sizeof (prefixUNC
);
181 strip_network_prefix
= true;
186 sizeof_prefix
= sizeof (prefixDOS
);
192 #if !HAVE_DECL____LC_CODEPAGE_FUNC
193 /* This prototype was added to locale.h in version 9.0 of MinGW-w64. */
194 _CRTIMP
unsigned int __cdecl
___lc_codepage_func (void);
196 const unsigned int cp
= ___lc_codepage_func ();
198 const unsigned int cp
= CP_UTF8
;
201 /* Converting the partial path from ascii to unicode.
202 1) Get the length: Calling with lpWideCharStr set to null returns the length.
203 2) Convert the string: Calling with cbMultiByte set to -1 includes the terminating null. */
204 size_t partPathWSize
= MultiByteToWideChar (cp
, 0, filename
, -1, NULL
, 0);
205 wchar_t * partPath
= calloc (partPathWSize
, sizeof(wchar_t));
208 MultiByteToWideChar (cp
, 0, filename
, -1, partPath
, partPathWSize
);
210 /* Convert any UNIX style path separators into the DOS i.e. backslash separator. */
211 for (ix
= 0; ix
< partPathLen
; ix
++)
212 if (IS_UNIX_DIR_SEPARATOR(filename
[ix
]))
215 /* Getting the full path from the provided partial path.
217 2) Resolve the path. */
218 long fullPathWSize
= GetFullPathNameW (partPath
, 0, NULL
, lpFilePart
);
219 wchar_t * fullPath
= calloc (fullPathWSize
+ sizeof_prefix
+ 1, sizeof(wchar_t));
221 wcscpy (fullPath
, prefix
);
223 int prefixLen
= sizeof_prefix
/ sizeof(wchar_t);
225 /* Do not add a prefix to the null device. */
226 if (stricmp (filename
, "nul") == 0)
229 wchar_t * fullPathOffset
= fullPath
+ prefixLen
- 1;
231 GetFullPathNameW (partPath
, fullPathWSize
, fullPathOffset
, lpFilePart
);
233 if (strip_network_prefix
)
235 /* Remove begining of the beginning two backslash characters (\\). */
236 wchar_t *_fullPath
= calloc (fullPathWSize
+ sizeof_prefix
+ 1, sizeof(wchar_t));
238 GetFullPathNameW (fullPath
, fullPathWSize
+ sizeof_prefix
+ 1, _fullPath
, lpFilePart
);
240 fullPath
= _fullPath
;
245 /* It is non-standard for modes to exceed 16 characters. */
248 MultiByteToWideChar (cp
, 0, modes
, -1, modesW
, sizeof(modesW
));
250 FILE * file
= _wfopen (fullPath
, modesW
);
253 return close_on_exec (file
);
255 #elif defined (HAVE_FOPEN64)
256 return close_on_exec (fopen64 (filename
, modes
));
259 return close_on_exec (fopen (filename
, modes
));
269 The <<struct bfd_iovec>> contains the internal file I/O class.
270 Each <<BFD>> has an instance of this class and all file I/O is
271 routed through it (it is assumed that the instance implements
272 all methods listed below).
276 . {* To avoid problems with macros, a "b" rather than "f"
277 . prefix is prepended to each method name. *}
278 . {* Attempt to read/write NBYTES on ABFD's IOSTREAM storing/fetching
279 . bytes starting at PTR. Return the number of bytes actually
280 . transfered (a read past end-of-file returns less than NBYTES),
281 . or -1 (setting <<bfd_error>>) if an error occurs. *}
282 . file_ptr (*bread) (struct bfd *abfd, void *ptr, file_ptr nbytes);
283 . file_ptr (*bwrite) (struct bfd *abfd, const void *ptr,
285 . {* Return the current IOSTREAM file offset, or -1 (setting <<bfd_error>>
286 . if an error occurs. *}
287 . file_ptr (*btell) (struct bfd *abfd);
288 . {* For the following, on successful completion a value of 0 is returned.
289 . Otherwise, a value of -1 is returned (and <<bfd_error>> is set). *}
290 . int (*bseek) (struct bfd *abfd, file_ptr offset, int whence);
291 . int (*bclose) (struct bfd *abfd);
292 . int (*bflush) (struct bfd *abfd);
293 . int (*bstat) (struct bfd *abfd, struct stat *sb);
294 . {* Mmap a part of the files. ADDR, LEN, PROT, FLAGS and OFFSET are the usual
295 . mmap parameter, except that LEN and OFFSET do not need to be page
296 . aligned. Returns (void *)-1 on failure, mmapped address on success.
297 . Also write in MAP_ADDR the address of the page aligned buffer and in
298 . MAP_LEN the size mapped (a page multiple). Use unmap with MAP_ADDR and
299 . MAP_LEN to unmap. *}
300 . void *(*bmmap) (struct bfd *abfd, void *addr, size_t len,
301 . int prot, int flags, file_ptr offset,
302 . void **map_addr, size_t *map_len);
305 .extern const struct bfd_iovec _bfd_memory_iovec;
315 bfd_size_type bfd_read (void *, bfd_size_type, bfd *)
316 ATTRIBUTE_WARN_UNUSED_RESULT;
319 Attempt to read SIZE bytes from ABFD's iostream to PTR.
320 Return the amount read.
324 bfd_read (void *ptr
, bfd_size_type size
, bfd
*abfd
)
327 bfd
*element_bfd
= abfd
;
328 ufile_ptr offset
= 0;
330 while (abfd
->my_archive
!= NULL
331 && !bfd_is_thin_archive (abfd
->my_archive
))
333 offset
+= abfd
->origin
;
334 abfd
= abfd
->my_archive
;
336 offset
+= abfd
->origin
;
338 /* If this is a non-thin archive element, don't read past the end of
340 if (element_bfd
->arelt_data
!= NULL
341 && element_bfd
->my_archive
!= NULL
342 && !bfd_is_thin_archive (element_bfd
->my_archive
))
344 bfd_size_type maxbytes
= arelt_size (element_bfd
);
346 if (abfd
->where
< offset
|| abfd
->where
- offset
>= maxbytes
)
348 bfd_set_error (bfd_error_invalid_operation
);
351 if (abfd
->where
- offset
+ size
> maxbytes
)
352 size
= maxbytes
- (abfd
->where
- offset
);
355 if (abfd
->iovec
== NULL
)
357 bfd_set_error (bfd_error_invalid_operation
);
361 if (abfd
->last_io
== bfd_io_write
)
363 abfd
->last_io
= bfd_io_force
;
364 if (bfd_seek (abfd
, 0, SEEK_CUR
) != 0)
367 abfd
->last_io
= bfd_io_read
;
369 nread
= abfd
->iovec
->bread (abfd
, ptr
, size
);
371 abfd
->where
+= nread
;
381 bfd_size_type bfd_write (const void *, bfd_size_type, bfd *)
382 ATTRIBUTE_WARN_UNUSED_RESULT;
385 Attempt to write SIZE bytes to ABFD's iostream from PTR.
386 Return the amount written.
390 bfd_write (const void *ptr
, bfd_size_type size
, bfd
*abfd
)
394 while (abfd
->my_archive
!= NULL
395 && !bfd_is_thin_archive (abfd
->my_archive
))
396 abfd
= abfd
->my_archive
;
398 if (abfd
->iovec
== NULL
)
400 bfd_set_error (bfd_error_invalid_operation
);
404 if (abfd
->last_io
== bfd_io_read
)
406 abfd
->last_io
= bfd_io_force
;
407 if (bfd_seek (abfd
, 0, SEEK_CUR
) != 0)
410 abfd
->last_io
= bfd_io_write
;
412 nwrote
= abfd
->iovec
->bwrite (abfd
, ptr
, size
);
414 abfd
->where
+= nwrote
;
415 if ((bfd_size_type
) nwrote
!= size
)
420 bfd_set_error (bfd_error_system_call
);
430 file_ptr bfd_tell (bfd *) ATTRIBUTE_WARN_UNUSED_RESULT;
433 Return ABFD's iostream file position.
439 ufile_ptr offset
= 0;
442 while (abfd
->my_archive
!= NULL
443 && !bfd_is_thin_archive (abfd
->my_archive
))
445 offset
+= abfd
->origin
;
446 abfd
= abfd
->my_archive
;
448 offset
+= abfd
->origin
;
450 if (abfd
->iovec
== NULL
)
453 ptr
= abfd
->iovec
->btell (abfd
);
463 int bfd_flush (bfd *);
466 Flush ABFD's iostream pending IO.
470 bfd_flush (bfd
*abfd
)
472 while (abfd
->my_archive
!= NULL
473 && !bfd_is_thin_archive (abfd
->my_archive
))
474 abfd
= abfd
->my_archive
;
476 if (abfd
->iovec
== NULL
)
479 return abfd
->iovec
->bflush (abfd
);
487 int bfd_stat (bfd *, struct stat *) ATTRIBUTE_WARN_UNUSED_RESULT;
490 Call fstat on ABFD's iostream. Return 0 on success, and a
491 negative value on failure.
495 bfd_stat (bfd
*abfd
, struct stat
*statbuf
)
499 while (abfd
->my_archive
!= NULL
500 && !bfd_is_thin_archive (abfd
->my_archive
))
501 abfd
= abfd
->my_archive
;
503 if (abfd
->iovec
== NULL
)
505 bfd_set_error (bfd_error_invalid_operation
);
509 result
= abfd
->iovec
->bstat (abfd
, statbuf
);
511 bfd_set_error (bfd_error_system_call
);
520 int bfd_seek (bfd *, file_ptr, int) ATTRIBUTE_WARN_UNUSED_RESULT;
523 Call fseek on ABFD's iostream. Return 0 on success, and a
524 negative value on failure.
528 bfd_seek (bfd
*abfd
, file_ptr position
, int direction
)
531 ufile_ptr offset
= 0;
533 while (abfd
->my_archive
!= NULL
534 && !bfd_is_thin_archive (abfd
->my_archive
))
536 offset
+= abfd
->origin
;
537 abfd
= abfd
->my_archive
;
539 offset
+= abfd
->origin
;
541 if (abfd
->iovec
== NULL
)
543 bfd_set_error (bfd_error_invalid_operation
);
547 /* For the time being, a BFD may not seek to it's end. The problem
548 is that we don't easily have a way to recognize the end of an
549 element in an archive. */
550 BFD_ASSERT (direction
== SEEK_SET
|| direction
== SEEK_CUR
);
552 if (direction
!= SEEK_CUR
)
555 if (((direction
== SEEK_CUR
&& position
== 0)
556 || (direction
== SEEK_SET
&& (ufile_ptr
) position
== abfd
->where
))
557 && abfd
->last_io
!= bfd_io_force
)
560 abfd
->last_io
= bfd_io_seek
;
562 result
= abfd
->iovec
->bseek (abfd
, position
, direction
);
565 /* An EINVAL error probably means that the file offset was
568 bfd_set_error (bfd_error_file_truncated
);
570 bfd_set_error (bfd_error_system_call
);
574 /* Adjust `where' field. */
575 if (direction
== SEEK_CUR
)
576 abfd
->where
+= position
;
578 abfd
->where
= position
;
589 long bfd_get_mtime (bfd *abfd);
592 Return the file modification time (as read from the file system, or
593 from the archive header for archive members).
598 bfd_get_mtime (bfd
*abfd
)
605 if (bfd_stat (abfd
, &buf
) != 0)
608 abfd
->mtime
= buf
.st_mtime
; /* Save value in case anyone wants it */
617 ufile_ptr bfd_get_size (bfd *abfd);
620 Return the file size (as read from file system) for the file
621 associated with BFD @var{abfd}.
623 The initial motivation for, and use of, this routine is not
624 so we can get the exact size of the object the BFD applies to, since
625 that might not be generally possible (archive members for example).
626 It would be ideal if someone could eventually modify
627 it so that such results were guaranteed.
629 Instead, we want to ask questions like "is this NNN byte sized
630 object I'm about to try read from file offset YYY reasonable?"
631 As as example of where we might do this, some object formats
632 use string tables for which the first <<sizeof (long)>> bytes of the
633 table contain the size of the table itself, including the size bytes.
634 If an application tries to read what it thinks is one of these
635 string tables, without some way to validate the size, and for
636 some reason the size is wrong (byte swapping error, wrong location
637 for the string table, etc.), the only clue is likely to be a read
638 error when it tries to read the table, or a "virtual memory
639 exhausted" error when it tries to allocate 15 bazillon bytes
640 of space for the 15 bazillon byte table it is about to read.
641 This function at least allows us to answer the question, "is the
644 A return value of zero indicates the file size is unknown.
648 bfd_get_size (bfd
*abfd
)
650 /* A size of 0 means we haven't yet called bfd_stat. A size of 1
651 means we have a cached value of 0, ie. unknown. */
652 if (abfd
->size
<= 1 || bfd_write_p (abfd
))
656 if (abfd
->size
== 1 && !bfd_write_p (abfd
))
659 if (bfd_stat (abfd
, &buf
) != 0
661 || buf
.st_size
- (ufile_ptr
) buf
.st_size
!= 0)
666 abfd
->size
= buf
.st_size
;
676 ufile_ptr bfd_get_file_size (bfd *abfd);
679 Return the file size (as read from file system) for the file
680 associated with BFD @var{abfd}. It supports both normal files
681 and archive elements.
686 bfd_get_file_size (bfd
*abfd
)
688 ufile_ptr file_size
, archive_size
= (ufile_ptr
) -1;
689 unsigned int compression_p2
= 0;
691 if (abfd
->my_archive
!= NULL
692 && !bfd_is_thin_archive (abfd
->my_archive
))
694 struct areltdata
*adata
= (struct areltdata
*) abfd
->arelt_data
;
697 archive_size
= adata
->parsed_size
;
698 /* If the archive is compressed, assume an element won't
699 expand more than eight times file size. */
700 if (adata
->arch_header
!= NULL
701 && memcmp (((struct ar_hdr
*) adata
->arch_header
)->ar_fmag
,
704 abfd
= abfd
->my_archive
;
708 file_size
= bfd_get_size (abfd
) << compression_p2
;
709 if (archive_size
< file_size
)
719 void *bfd_mmap (bfd *abfd, void *addr, size_t len,
720 int prot, int flags, file_ptr offset,
721 void **map_addr, size_t *map_len)
722 ATTRIBUTE_WARN_UNUSED_RESULT;
725 Return mmap()ed region of the file, if possible and implemented.
726 LEN and OFFSET do not need to be page aligned. The page aligned
727 address and length are written to MAP_ADDR and MAP_LEN.
732 bfd_mmap (bfd
*abfd
, void *addr
, size_t len
,
733 int prot
, int flags
, file_ptr offset
,
734 void **map_addr
, size_t *map_len
)
736 while (abfd
->my_archive
!= NULL
737 && !bfd_is_thin_archive (abfd
->my_archive
))
739 offset
+= abfd
->origin
;
740 abfd
= abfd
->my_archive
;
742 offset
+= abfd
->origin
;
744 if (abfd
->iovec
== NULL
)
746 bfd_set_error (bfd_error_invalid_operation
);
750 return abfd
->iovec
->bmmap (abfd
, addr
, len
, prot
, flags
, offset
,
754 /* Memory file I/O operations. */
757 memory_bread (bfd
*abfd
, void *ptr
, file_ptr size
)
759 struct bfd_in_memory
*bim
;
762 bim
= (struct bfd_in_memory
*) abfd
->iostream
;
764 if (abfd
->where
+ get
> bim
->size
)
766 if (bim
->size
< (bfd_size_type
) abfd
->where
)
769 get
= bim
->size
- abfd
->where
;
770 bfd_set_error (bfd_error_file_truncated
);
772 memcpy (ptr
, bim
->buffer
+ abfd
->where
, (size_t) get
);
777 memory_bwrite (bfd
*abfd
, const void *ptr
, file_ptr size
)
779 struct bfd_in_memory
*bim
= (struct bfd_in_memory
*) abfd
->iostream
;
781 if (abfd
->where
+ size
> bim
->size
)
783 bfd_size_type newsize
, oldsize
;
785 oldsize
= (bim
->size
+ 127) & ~(bfd_size_type
) 127;
786 bim
->size
= abfd
->where
+ size
;
787 /* Round up to cut down on memory fragmentation */
788 newsize
= (bim
->size
+ 127) & ~(bfd_size_type
) 127;
789 if (newsize
> oldsize
)
791 bim
->buffer
= (bfd_byte
*) bfd_realloc_or_free (bim
->buffer
, newsize
);
792 if (bim
->buffer
== NULL
)
797 if (newsize
> bim
->size
)
798 memset (bim
->buffer
+ bim
->size
, 0, newsize
- bim
->size
);
801 memcpy (bim
->buffer
+ abfd
->where
, ptr
, (size_t) size
);
806 memory_btell (bfd
*abfd
)
812 memory_bseek (bfd
*abfd
, file_ptr position
, int direction
)
815 struct bfd_in_memory
*bim
;
817 bim
= (struct bfd_in_memory
*) abfd
->iostream
;
819 if (direction
== SEEK_SET
)
822 nwhere
= abfd
->where
+ position
;
831 if ((bfd_size_type
)nwhere
> bim
->size
)
833 if (abfd
->direction
== write_direction
834 || abfd
->direction
== both_direction
)
836 bfd_size_type newsize
, oldsize
;
838 oldsize
= (bim
->size
+ 127) & ~(bfd_size_type
) 127;
840 /* Round up to cut down on memory fragmentation */
841 newsize
= (bim
->size
+ 127) & ~(bfd_size_type
) 127;
842 if (newsize
> oldsize
)
844 bim
->buffer
= (bfd_byte
*) bfd_realloc_or_free (bim
->buffer
, newsize
);
845 if (bim
->buffer
== NULL
)
851 memset (bim
->buffer
+ oldsize
, 0, newsize
- oldsize
);
856 abfd
->where
= bim
->size
;
858 bfd_set_error (bfd_error_file_truncated
);
866 memory_bclose (struct bfd
*abfd
)
868 struct bfd_in_memory
*bim
= (struct bfd_in_memory
*) abfd
->iostream
;
872 abfd
->iostream
= NULL
;
878 memory_bflush (bfd
*abfd ATTRIBUTE_UNUSED
)
884 memory_bstat (bfd
*abfd
, struct stat
*statbuf
)
886 struct bfd_in_memory
*bim
= (struct bfd_in_memory
*) abfd
->iostream
;
888 memset (statbuf
, 0, sizeof (*statbuf
));
889 statbuf
->st_size
= bim
->size
;
895 memory_bmmap (bfd
*abfd ATTRIBUTE_UNUSED
, void *addr ATTRIBUTE_UNUSED
,
896 size_t len ATTRIBUTE_UNUSED
, int prot ATTRIBUTE_UNUSED
,
897 int flags ATTRIBUTE_UNUSED
, file_ptr offset ATTRIBUTE_UNUSED
,
898 void **map_addr ATTRIBUTE_UNUSED
,
899 size_t *map_len ATTRIBUTE_UNUSED
)
904 const struct bfd_iovec _bfd_memory_iovec
=
906 &memory_bread
, &memory_bwrite
, &memory_btell
, &memory_bseek
,
907 &memory_bclose
, &memory_bflush
, &memory_bstat
, &memory_bmmap
915 time_t bfd_get_current_time (time_t now);
918 Returns the current time.
920 If the environment variable SOURCE_DATE_EPOCH is defined
921 then this is parsed and its value is returned. Otherwise
922 if the paramter NOW is non-zero, then that is returned.
923 Otherwise the result of the system call "time(NULL)" is
928 bfd_get_current_time (time_t now
)
930 char *source_date_epoch
;
931 unsigned long long epoch
;
933 /* FIXME: We could probably cache this lookup,
934 and the parsing of its value below. */
935 source_date_epoch
= getenv ("SOURCE_DATE_EPOCH");
937 if (source_date_epoch
== NULL
)
944 epoch
= strtoull (source_date_epoch
, NULL
, 0);
946 /* If epoch == 0 then something is wrong with SOURCE_DATE_EPOCH,
947 but we do not have an easy way to report it. Since the presence
948 of the environment variable implies that the user wants
949 deterministic behaviour we just accept the 0 value. */
951 return (time_t) epoch
;