Return 1 if FILE is an unwritable non-symlink, 0 if it is writable or some other type of file, -1 and set errno if there is some problem in determining the answer. Set *BUF to the file status. */
| 93 | -1 and set errno if there is some problem in determining the answer. |
| 94 | Set *BUF to the file status. */ |
| 95 | static int |
| 96 | write_protected_non_symlink (int fd_cwd, |
| 97 | char const *file, |
| 98 | struct stat *buf) |
| 99 | { |
| 100 | if (can_write_any_file ()) |
| 101 | return 0; |
| 102 | if (cache_fstatat (fd_cwd, file, buf, AT_SYMLINK_NOFOLLOW) != 0) |
| 103 | return -1; |
| 104 | if (S_ISLNK (buf->st_mode)) |
| 105 | return 0; |
| 106 | /* Here, we know FILE is not a symbolic link. */ |
| 107 | |
| 108 | /* In order to be reentrant -- i.e., to avoid changing the working |
| 109 | directory, and at the same time to be able to deal with alternate |
| 110 | access control mechanisms (ACLs, xattr-style attributes) and |
| 111 | arbitrarily deep trees -- we need a function like eaccessat, i.e., |
| 112 | like Solaris' eaccess, but fd-relative, in the spirit of openat. */ |
| 113 | |
| 114 | /* In the absence of a native eaccessat function, here are some of |
| 115 | the implementation choices [#4 and #5 were suggested by Paul Eggert]: |
| 116 | 1) call openat with O_WRONLY|O_NOCTTY |
| 117 | Disadvantage: may create the file and doesn't work for directory, |
| 118 | may mistakenly report 'unwritable' for EROFS or ACLs even though |
| 119 | perm bits say the file is writable. |
| 120 | |
| 121 | 2) fake eaccessat (save_cwd, fchdir, call euidaccess, restore_cwd) |
| 122 | Disadvantage: changes working directory (not reentrant) and can't |
| 123 | work if save_cwd fails. |
| 124 | |
| 125 | 3) if (euidaccess (full_name, W_OK) == 0) |
| 126 | Disadvantage: doesn't work if full_name is too long. |
| 127 | Inefficient for very deep trees (O(Depth^2)). |
| 128 | |
| 129 | 4) If the full pathname is sufficiently short (say, less than |
| 130 | PATH_MAX or 8192 bytes, whichever is shorter): |
| 131 | use method (3) (i.e., euidaccess (full_name, W_OK)); |
| 132 | Otherwise: vfork, fchdir in the child, run euidaccess in the |
| 133 | child, then the child exits with a status that tells the parent |
| 134 | whether euidaccess succeeded. |
| 135 | |
| 136 | This avoids the O(N**2) algorithm of method (3), and it also avoids |
| 137 | the failure-due-to-too-long-file-names of method (3), but it's fast |
| 138 | in the normal shallow case. It also avoids the lack-of-reentrancy |
| 139 | and the save_cwd problems. |
| 140 | Disadvantage; it uses a process slot for very-long file names, |
| 141 | and would be very slow for hierarchies with many such files. |
| 142 | |
| 143 | 5) If the full file name is sufficiently short (say, less than |
| 144 | PATH_MAX or 8192 bytes, whichever is shorter): |
| 145 | use method (3) (i.e., euidaccess (full_name, W_OK)); |
| 146 | Otherwise: look just at the file bits. Perhaps issue a warning |
| 147 | the first time this occurs. |
| 148 | |
| 149 | This is like (4), except for the "Otherwise" case where it isn't as |
| 150 | "perfect" as (4) but is considerably faster. It conforms to current |
| 151 | POSIX, and is uniformly better than what Solaris and FreeBSD do (they |
| 152 | mess up with long file names). */ |
no test coverage detected