+/* List of directories whose statuses we need to extract after we've
+ finished extracting their subsidiary files. If you consider each
+ contiguous subsequence of elements of the form [D]?[^D]*, where [D]
+ represents an element where AFTER_LINKS is nonzero and [^D]
+ represents an element where AFTER_LINKS is zero, then the head
+ of the subsequence has the longest name, and each non-head element
+ in the prefix is an ancestor (in the directory hierarchy) of the
+ preceding element. */
+
+struct delayed_set_stat
+ {
+ struct delayed_set_stat *next;
+ dev_t dev;
+ ino_t ino;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ struct timespec atime;
+ struct timespec mtime;
+ size_t file_name_len;
+ mode_t invert_permissions;
+ enum permstatus permstatus;
+ bool after_links;
+ char file_name[1];
+ };
+
+static struct delayed_set_stat *delayed_set_stat_head;
+
+/* List of links whose creation we have delayed. */
+struct delayed_link
+ {
+ /* The next delayed link in the list. */
+ struct delayed_link *next;
+
+ /* The device, inode number and last-modified time of the placeholder. */
+ dev_t dev;
+ ino_t ino;
+ struct timespec mtime;
+
+ /* True if the link is symbolic. */
+ bool is_symlink;
+
+ /* The desired owner and group of the link, if it is a symlink. */
+ uid_t uid;
+ gid_t gid;
+
+ /* A list of sources for this link. The sources are all to be
+ hard-linked together. */
+ struct string_list *sources;
+
+ /* The desired target of the desired link. */
+ char target[1];
+ };
+
+static struct delayed_link *delayed_link_head;
+
+struct string_list
+ {
+ struct string_list *next;
+ char string[1];
+ };
+
+/* Set up to extract files. */
+void
+extr_init (void)
+{
+ we_are_root = geteuid () == 0;
+ same_permissions_option += we_are_root;
+ same_owner_option += we_are_root;
+
+ /* Option -p clears the kernel umask, so it does not affect proper
+ restoration of file permissions. New intermediate directories will
+ comply with umask at start of program. */
+
+ newdir_umask = umask (0);
+ if (0 < same_permissions_option)
+ current_umask = 0;
+ else
+ {
+ umask (newdir_umask); /* restore the kernel umask */
+ current_umask = newdir_umask;
+ }
+}
+
+/* If restoring permissions, restore the mode for FILE_NAME from
+ information given in *STAT_INFO (where *CUR_INFO gives
+ the current status if CUR_INFO is nonzero); otherwise invert the
+ INVERT_PERMISSIONS bits from the file's current permissions.
+ PERMSTATUS specifies the status of the file's permissions.
+ TYPEFLAG specifies the type of the file. */
+static void
+set_mode (char const *file_name,
+ struct stat const *stat_info,
+ struct stat const *cur_info,
+ mode_t invert_permissions, enum permstatus permstatus,
+ char typeflag)
+{
+ mode_t mode;
+
+ if (0 < same_permissions_option
+ && permstatus != INTERDIR_PERMSTATUS)
+ {
+ mode = stat_info->st_mode;
+
+ /* If we created the file and it has a usual mode, then its mode
+ is normally set correctly already. But on many hosts, some
+ directories inherit the setgid bits from their parents, so we
+ we must set directories' modes explicitly. */
+ if (permstatus == ARCHIVED_PERMSTATUS
+ && ! (mode & ~ MODE_RWX)
+ && typeflag != DIRTYPE
+ && typeflag != GNUTYPE_DUMPDIR)
+ return;
+ }
+ else if (! invert_permissions)
+ return;
+ else
+ {
+ /* We must inspect a directory's current permissions, since the
+ directory may have inherited its setgid bit from its parent.
+
+ INVERT_PERMISSIONS happens to be nonzero only for directories
+ that we created, so there's no point optimizing this code for
+ other cases. */
+ struct stat st;
+ if (! cur_info)
+ {
+ if (stat (file_name, &st) != 0)
+ {
+ stat_error (file_name);
+ return;
+ }
+ cur_info = &st;
+ }
+ mode = cur_info->st_mode ^ invert_permissions;
+ }