You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FS_POSIX_Java7.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*
  2. * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg@dewire.com>
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.util;
  44. import java.io.BufferedReader;
  45. import java.io.File;
  46. import java.io.IOException;
  47. import java.io.InputStreamReader;
  48. import java.nio.charset.Charset;
  49. import java.nio.file.Files;
  50. import java.nio.file.Path;
  51. import java.nio.file.attribute.PosixFilePermission;
  52. import java.util.Set;
  53. import org.eclipse.jgit.lib.Constants;
  54. import org.eclipse.jgit.lib.Repository;
  55. /**
  56. * FS implementation for Java7 on unix like systems
  57. */
  58. public class FS_POSIX_Java7 extends FS_POSIX {
  59. /*
  60. * True if the current user "umask" allows to set execute bit for "others".
  61. * Can be null if "umask" is not supported (or returns unexpected values) by
  62. * current user shell.
  63. *
  64. * Bug 424395: with the umask of 0002 (user: rwx group: rwx others: rx) egit
  65. * checked out files as rwx,rwx,r (execution not allowed for "others"). To
  66. * fix this and properly set "executable" permission bit for "others", we
  67. * must consider the user umask on checkout
  68. */
  69. private static final Boolean EXECUTE_FOR_OTHERS;
  70. /*
  71. * True if the current user "umask" allows to set execute bit for "group".
  72. * Can be null if "umask" is not supported (or returns unexpected values) by
  73. * current user shell.
  74. */
  75. private static final Boolean EXECUTE_FOR_GROUP;
  76. static {
  77. String umask = readUmask();
  78. // umask return value consists of 3 or 4 digits, like "002" or "0002"
  79. if (umask != null && umask.length() > 0 && umask.matches("\\d{3,4}")) { //$NON-NLS-1$
  80. EXECUTE_FOR_OTHERS = isGranted(PosixFilePermission.OTHERS_EXECUTE,
  81. umask);
  82. EXECUTE_FOR_GROUP = isGranted(PosixFilePermission.GROUP_EXECUTE,
  83. umask);
  84. } else {
  85. EXECUTE_FOR_OTHERS = null;
  86. EXECUTE_FOR_GROUP = null;
  87. }
  88. }
  89. FS_POSIX_Java7(FS_POSIX_Java7 src) {
  90. super(src);
  91. }
  92. FS_POSIX_Java7() {
  93. // empty
  94. }
  95. @Override
  96. public FS newInstance() {
  97. return new FS_POSIX_Java7(this);
  98. }
  99. @Override
  100. public boolean supportsExecute() {
  101. return true;
  102. }
  103. @Override
  104. public boolean canExecute(File f) {
  105. return FileUtil.canExecute(f);
  106. }
  107. @Override
  108. public boolean setExecute(File f, boolean canExecute) {
  109. if (!isFile(f))
  110. return false;
  111. // only if the execute has to be set, and we know the umask
  112. if (canExecute && EXECUTE_FOR_OTHERS != null) {
  113. try {
  114. Path path = f.toPath();
  115. Set<PosixFilePermission> pset = Files
  116. .getPosixFilePermissions(path);
  117. // user is always allowed to set execute
  118. pset.add(PosixFilePermission.OWNER_EXECUTE);
  119. if (EXECUTE_FOR_GROUP.booleanValue())
  120. pset.add(PosixFilePermission.GROUP_EXECUTE);
  121. if (EXECUTE_FOR_OTHERS.booleanValue())
  122. pset.add(PosixFilePermission.OTHERS_EXECUTE);
  123. Files.setPosixFilePermissions(path, pset);
  124. return true;
  125. } catch (IOException e) {
  126. // The interface doesn't allow to throw IOException
  127. final boolean debug = Boolean.parseBoolean(SystemReader
  128. .getInstance().getProperty("jgit.fs.debug")); //$NON-NLS-1$
  129. if (debug)
  130. System.err.println(e);
  131. return false;
  132. }
  133. }
  134. // if umask is not working for some reason: fall back to default (buggy)
  135. // implementation which does not consider umask: see bug 424395
  136. return f.setExecutable(canExecute);
  137. }
  138. /**
  139. * Derives requested permission from given octal umask value as defined e.g.
  140. * in <a href="http://linux.die.net/man/2/umask">http://linux.die.net/man/2/
  141. * umask</a>.
  142. * <p>
  143. * The umask expected here must consist of 3 or 4 digits. Last three digits
  144. * are significant here because they represent file permissions granted to
  145. * the "owner", "group" and "others" (in this order).
  146. * <p>
  147. * Each single digit from the umask represents 3 bits of the mask standing
  148. * for "<b>r</b>ead, <b>w</b>rite, e<b>x</b>ecute" permissions (in this
  149. * order).
  150. * <p>
  151. * The possible umask values table:
  152. *
  153. * <pre>
  154. * Value : Bits:Abbr.: Permission
  155. * 0 : 000 :rwx : read, write and execute
  156. * 1 : 001 :rw : read and write
  157. * 2 : 010 :rx : read and execute
  158. * 3 : 011 :r : read only
  159. * 4 : 100 :wx : write and execute
  160. * 5 : 101 :w : write only
  161. * 6 : 110 :x : execute only
  162. * 7 : 111 : : no permissions
  163. * </pre>
  164. * <p>
  165. * Note, that umask value is used to "mask" the requested permissions on
  166. * file creation by combining the requested permission bit with the
  167. * <b>negated</b> value of the umask bit.
  168. * <p>
  169. * Simply speaking, if a bit is <b>not</b> set in the umask, then the
  170. * appropriate right <b>will</b> be granted <b>if</b> requested. If a bit is
  171. * set in the umask value, then the appropriate permission will be not
  172. * granted.
  173. * <p>
  174. * Example:
  175. * <li>umask 023 ("000 010 011" or rwx rx r) combined with the request to
  176. * create an executable file with full set of permissions for everyone (777)
  177. * results in the file with permissions 754 (rwx rx r).
  178. * <li>umask 002 ("000 000 010" or rwx rwx rx) combined with the request to
  179. * create an executable file with full set of permissions for everyone (777)
  180. * results in the file with permissions 775 (rwx rwx rx).
  181. * <li>umask 002 ("000 000 010" or rwx rwx rx) combined with the request to
  182. * create a file without executable rights for everyone (666) results in the
  183. * file with permissions 664 (rw rw r).
  184. *
  185. * @param p
  186. * non null permission
  187. * @param umask
  188. * octal umask value represented by at least three digits. The
  189. * digits (read from the end to beginning of the umask) represent
  190. * permissions for "others", "group" and "owner".
  191. *
  192. * @return true if the requested permission is set according to given umask
  193. */
  194. private static Boolean isGranted(PosixFilePermission p, String umask) {
  195. char val;
  196. switch (p) {
  197. case OTHERS_EXECUTE:
  198. // Read last digit, because umask is ordered as: User/Group/Others.
  199. val = umask.charAt(umask.length() - 1);
  200. return isExecuteGranted(val);
  201. case GROUP_EXECUTE:
  202. val = umask.charAt(umask.length() - 2);
  203. return isExecuteGranted(val);
  204. default:
  205. throw new UnsupportedOperationException(
  206. "isGranted() for " + p + " is not implemented!"); //$NON-NLS-1$ //$NON-NLS-2$
  207. }
  208. }
  209. /**
  210. * @param c
  211. * character representing octal permission value from the table
  212. * in {@link #isGranted(PosixFilePermission, String)}
  213. * @return true if the "execute" permission is granted according to given
  214. * character
  215. */
  216. private static Boolean isExecuteGranted(char c) {
  217. if (c == '0' || c == '2' || c == '4' || c == '6')
  218. return Boolean.TRUE;
  219. return Boolean.FALSE;
  220. }
  221. private static String readUmask() {
  222. Process p;
  223. try {
  224. p = Runtime.getRuntime().exec(
  225. new String[] { "sh", "-c", "umask" }, null, null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
  226. try (BufferedReader lineRead = new BufferedReader(
  227. new InputStreamReader(p.getInputStream(), Charset
  228. .defaultCharset().name()))) {
  229. p.waitFor();
  230. return lineRead.readLine();
  231. }
  232. } catch (Exception e) {
  233. return null;
  234. }
  235. }
  236. @Override
  237. public boolean retryFailedLockFileCommit() {
  238. return false;
  239. }
  240. @Override
  241. public boolean supportsSymlinks() {
  242. return true;
  243. }
  244. @Override
  245. public boolean isSymLink(File path) throws IOException {
  246. return FileUtil.isSymlink(path);
  247. }
  248. @Override
  249. public long lastModified(File path) throws IOException {
  250. return FileUtil.lastModified(path);
  251. }
  252. @Override
  253. public void setLastModified(File path, long time) throws IOException {
  254. FileUtil.setLastModified(path, time);
  255. }
  256. @Override
  257. public void delete(File path) throws IOException {
  258. FileUtil.delete(path);
  259. }
  260. @Override
  261. public long length(File f) throws IOException {
  262. return FileUtil.getLength(f);
  263. }
  264. @Override
  265. public boolean exists(File path) {
  266. return FileUtil.exists(path);
  267. }
  268. @Override
  269. public boolean isDirectory(File path) {
  270. return FileUtil.isDirectory(path);
  271. }
  272. @Override
  273. public boolean isFile(File path) {
  274. return FileUtil.isFile(path);
  275. }
  276. @Override
  277. public boolean isHidden(File path) throws IOException {
  278. return FileUtil.isHidden(path);
  279. }
  280. @Override
  281. public void setHidden(File path, boolean hidden) throws IOException {
  282. // no action on POSIX
  283. }
  284. @Override
  285. public String readSymLink(File path) throws IOException {
  286. return FileUtil.readSymlink(path);
  287. }
  288. @Override
  289. public void createSymLink(File path, String target) throws IOException {
  290. FileUtil.createSymLink(path, target);
  291. }
  292. /**
  293. * @since 3.3
  294. */
  295. @Override
  296. public Attributes getAttributes(File path) {
  297. return FileUtil.getFileAttributesPosix(this, path);
  298. }
  299. /**
  300. * @since 3.3
  301. */
  302. @Override
  303. public File normalize(File file) {
  304. return FileUtil.normalize(file);
  305. }
  306. /**
  307. * @since 3.3
  308. */
  309. @Override
  310. public String normalize(String name) {
  311. return FileUtil.normalize(name);
  312. }
  313. /**
  314. * @since 3.7
  315. */
  316. @Override
  317. public File findHook(Repository repository, Hook hook) {
  318. final File gitdir = repository.getDirectory();
  319. final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
  320. .resolve(hook.getName());
  321. if (Files.isExecutable(hookPath))
  322. return hookPath.toFile();
  323. return null;
  324. }
  325. }