chmod.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. chmod.c
  5. Abstract:
  6. This module implements support for the chmod utility.
  7. Author:
  8. Evan Green 16-Jul-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/lib/types.h>
  16. #include <errno.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include "swlib.h"
  20. //
  21. // ---------------------------------------------------------------- Definitions
  22. //
  23. //
  24. // Set this option to recursively change permissions for any directory.
  25. //
  26. #define CHMOD_OPTION_RECURSIVE 0x00000001
  27. //
  28. // Set this option to print out a message for each file changed.
  29. //
  30. #define CHMOD_OPTION_VERBOSE 0x00000002
  31. //
  32. // Set this option to suppress most error messages.
  33. //
  34. #define CHMOD_OPTION_QUIET 0x00000004
  35. //
  36. // ------------------------------------------------------ Data Type Definitions
  37. //
  38. //
  39. // ----------------------------------------------- Internal Function Prototypes
  40. //
  41. INT
  42. ChmodChangePermissions (
  43. ULONG Options,
  44. PSTR ModeString,
  45. PSTR Argument
  46. );
  47. //
  48. // -------------------------------------------------------------------- Globals
  49. //
  50. //
  51. // ------------------------------------------------------------------ Functions
  52. //
  53. INT
  54. ChmodMain (
  55. INT ArgumentCount,
  56. CHAR **Arguments
  57. )
  58. /*++
  59. Routine Description:
  60. This routine implements the main entry point for the chmod utility.
  61. Arguments:
  62. ArgumentCount - Supplies the number of arguments on the command line.
  63. Arguments - Supplies an array of pointers to strings representing the
  64. arguments.
  65. Return Value:
  66. 0 on success.
  67. Non-zero on failure.
  68. --*/
  69. {
  70. PSTR Argument;
  71. ULONG ArgumentIndex;
  72. BOOL DidSomething;
  73. PSTR ModeString;
  74. ULONG Options;
  75. mode_t OriginalUmask;
  76. INT Result;
  77. INT ReturnValue;
  78. BOOL SkipControlArguments;
  79. ModeString = NULL;
  80. Options = 0;
  81. ReturnValue = 0;
  82. OriginalUmask = umask(0);
  83. //
  84. // Loop through all the options.
  85. //
  86. SkipControlArguments = TRUE;
  87. for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) {
  88. Argument = Arguments[ArgumentIndex];
  89. if (SkipControlArguments != FALSE) {
  90. //
  91. // Stop if a -- is found.
  92. //
  93. if (strcmp(Argument, "--") == 0) {
  94. SkipControlArguments = FALSE;
  95. continue;
  96. }
  97. if (Argument[0] == '-') {
  98. //
  99. // Parse the argument, unless they're actually permissions.
  100. //
  101. if ((Argument[1] != 'r') && (Argument[1] != 'w') &&
  102. (Argument[1] != 'x') && (Argument[1] != 's') &&
  103. (Argument[1] != 'X') && (Argument[1] != 't') &&
  104. (Argument[1] != 'u') && (Argument[1] != 'g') &&
  105. (Argument[1] != 'o') && (Argument[1] != 'a')) {
  106. Argument += 1;
  107. while (*Argument != '\0') {
  108. switch (*Argument) {
  109. case 'R':
  110. Options |= CHMOD_OPTION_RECURSIVE;
  111. break;
  112. case 'v':
  113. Options |= CHMOD_OPTION_VERBOSE;
  114. Options &= ~CHMOD_OPTION_QUIET;
  115. break;
  116. case 'f':
  117. Options |= CHMOD_OPTION_QUIET;
  118. Options &= ~CHMOD_OPTION_VERBOSE;
  119. break;
  120. default:
  121. SwPrintError(0,
  122. NULL,
  123. "Unknown option %c",
  124. *Argument);
  125. Result = EINVAL;
  126. goto MainEnd;
  127. }
  128. Argument += 1;
  129. }
  130. continue;
  131. }
  132. }
  133. }
  134. //
  135. // This is a regular argument.
  136. //
  137. if (ModeString == NULL) {
  138. ModeString = Argument;
  139. }
  140. }
  141. if (ModeString == NULL) {
  142. SwPrintError(0, NULL, "Expecting mode argument");
  143. ReturnValue = EINVAL;
  144. goto MainEnd;
  145. }
  146. //
  147. // Now that the options have been figured out, loop through again to
  148. // actually change permissions.
  149. //
  150. DidSomething = FALSE;
  151. SkipControlArguments = TRUE;
  152. for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) {
  153. Argument = Arguments[ArgumentIndex];
  154. if (SkipControlArguments != FALSE) {
  155. //
  156. // If a -- is found, all other arguments are real parameters.
  157. //
  158. if (strcmp(Argument, "--") == 0) {
  159. SkipControlArguments = FALSE;
  160. continue;
  161. }
  162. if (Argument[0] == '-') {
  163. continue;
  164. }
  165. }
  166. if (Argument == ModeString) {
  167. continue;
  168. }
  169. DidSomething = TRUE;
  170. Result = ChmodChangePermissions(Options, ModeString, Argument);
  171. if (Result != 0) {
  172. ReturnValue = Result;
  173. }
  174. }
  175. if (DidSomething == FALSE) {
  176. SwPrintError(0, NULL, "Argument expected");
  177. Result = 1;
  178. goto MainEnd;
  179. }
  180. MainEnd:
  181. umask(OriginalUmask);
  182. return ReturnValue;
  183. }
  184. //
  185. // --------------------------------------------------------- Internal Functions
  186. //
  187. INT
  188. ChmodChangePermissions (
  189. ULONG Options,
  190. PSTR ModeString,
  191. PSTR Argument
  192. )
  193. /*++
  194. Routine Description:
  195. This routine changes the mode bits for the given file entry.
  196. Arguments:
  197. Options - Supplies the invocation options. See CHMOD_OPTION_* definitions.
  198. ModeString - Supplies the string of mode bits to change.
  199. Argument - Supplies the path of the file to change.
  200. Return Value:
  201. 0 on success.
  202. Non-zero on failure.
  203. --*/
  204. {
  205. PSTR AppendedPath;
  206. ULONG AppendedPathSize;
  207. DIR *Directory;
  208. struct dirent Entry;
  209. struct dirent *EntryPointer;
  210. BOOL IsDirectory;
  211. mode_t NewMode;
  212. mode_t OriginalMode;
  213. CHAR PrintMode[10];
  214. PSTR QuotedPath;
  215. INT Result;
  216. struct stat Stat;
  217. INT StringIndex;
  218. PSTR Verb;
  219. Directory = NULL;
  220. //
  221. // Get the file information.
  222. //
  223. Result = SwStat(Argument, FALSE, &Stat);
  224. if (Result != 0) {
  225. Result = errno;
  226. if ((Options & CHMOD_OPTION_QUIET) == 0) {
  227. SwPrintError(Result, Argument, "Cannot stat");
  228. }
  229. goto ChangePermissionsEnd;
  230. }
  231. //
  232. // Skip symbolic links.
  233. //
  234. if (S_ISLNK(Stat.st_mode)) {
  235. if ((Options & CHMOD_OPTION_VERBOSE) != 0) {
  236. QuotedPath = SwQuoteArgument(Argument);
  237. printf("Neither symbolic link '%s' nor referent has been "
  238. "changed.\n",
  239. QuotedPath);
  240. if (QuotedPath != Argument) {
  241. free(QuotedPath);
  242. }
  243. }
  244. goto ChangePermissionsEnd;
  245. }
  246. OriginalMode = Stat.st_mode;
  247. NewMode = OriginalMode;
  248. IsDirectory = FALSE;
  249. if (S_ISDIR(Stat.st_mode)) {
  250. IsDirectory = TRUE;
  251. }
  252. Result = SwParseFilePermissionsString(ModeString, IsDirectory, &NewMode);
  253. if (Result == FALSE) {
  254. SwPrintError(0, ModeString, "Invalid mode");
  255. Result = EINVAL;
  256. goto ChangePermissionsEnd;
  257. }
  258. //
  259. // Attempt to change the mode of this file or directory.
  260. //
  261. Result = chmod(Argument, NewMode);
  262. if (Result != 0) {
  263. Result = errno;
  264. if ((Options & CHMOD_OPTION_QUIET) == 0) {
  265. SwPrintError(Result, Argument, "Could not change mode of");
  266. }
  267. goto ChangePermissionsEnd;
  268. }
  269. //
  270. // Print this out if verbose.
  271. //
  272. if ((Options & CHMOD_OPTION_VERBOSE) != 0) {
  273. for (StringIndex = 0; StringIndex < 9; StringIndex += 1) {
  274. PrintMode[StringIndex] = '-';
  275. }
  276. if ((NewMode & S_IRUSR) != 0) {
  277. PrintMode[0] = 'r';
  278. }
  279. if ((NewMode & S_IWUSR) != 0) {
  280. PrintMode[1] = 'w';
  281. }
  282. if ((NewMode & S_IXUSR) != 0) {
  283. PrintMode[2] = 'x';
  284. if ((NewMode & S_ISUID) != 0) {
  285. PrintMode[2] = 's';
  286. }
  287. } else {
  288. if ((NewMode & S_ISUID) != 0) {
  289. PrintMode[2] = 'S';
  290. }
  291. }
  292. if ((NewMode & S_IRGRP) != 0) {
  293. PrintMode[3] = 'r';
  294. }
  295. if ((NewMode & S_IWGRP) != 0) {
  296. PrintMode[4] = 'w';
  297. }
  298. if ((NewMode & S_IXGRP) != 0) {
  299. PrintMode[5] = 'x';
  300. if ((NewMode & S_ISGID) != 0) {
  301. PrintMode[5] = 's';
  302. }
  303. } else {
  304. if ((NewMode & S_ISGID) != 0) {
  305. PrintMode[5] = 'S';
  306. }
  307. }
  308. if ((NewMode & S_IROTH) != 0) {
  309. PrintMode[6] = 'r';
  310. }
  311. if ((NewMode & S_IWOTH) != 0) {
  312. PrintMode[7] = 'w';
  313. }
  314. if ((NewMode & S_IXOTH) != 0) {
  315. PrintMode[8] = 'x';
  316. if ((NewMode & S_ISVTX) != 0) {
  317. PrintMode[8] = 't';
  318. }
  319. } else {
  320. if ((NewMode & S_ISVTX) != 0) {
  321. PrintMode[8] = 'T';
  322. }
  323. }
  324. PrintMode[9] = '\0';
  325. if (NewMode != OriginalMode) {
  326. Verb = "changed to";
  327. } else {
  328. Verb = "retained as";
  329. }
  330. QuotedPath = SwQuoteArgument(Argument);
  331. printf("mode of '%s' %s 0%03o (%s)\n",
  332. QuotedPath,
  333. Verb,
  334. NewMode & (S_IRWXU | S_IRWXG | S_IRWXO),
  335. PrintMode);
  336. if (QuotedPath != Argument) {
  337. free(QuotedPath);
  338. }
  339. }
  340. //
  341. // If the options are not recursive or this is not a directory, then that's
  342. // all there is to do.
  343. //
  344. if (((Options & CHMOD_OPTION_RECURSIVE) == 0) || (IsDirectory == FALSE)) {
  345. goto ChangePermissionsEnd;
  346. }
  347. Directory = opendir(Argument);
  348. if (Directory == NULL) {
  349. Result = errno;
  350. if ((Options & CHMOD_OPTION_QUIET) == 0) {
  351. SwPrintError(Result, Argument, "Cannot open directory");
  352. }
  353. goto ChangePermissionsEnd;
  354. }
  355. //
  356. // Loop through all entries in the directory.
  357. //
  358. while (TRUE) {
  359. Result = SwReadDirectory(Directory, &Entry, &EntryPointer);
  360. if (Result != 0) {
  361. if ((Options & CHMOD_OPTION_QUIET) == 0) {
  362. SwPrintError(Result, Argument, "Unable to read directory");
  363. }
  364. goto ChangePermissionsEnd;
  365. }
  366. if (EntryPointer == NULL) {
  367. break;
  368. }
  369. if ((strcmp(Entry.d_name, ".") == 0) ||
  370. (strcmp(Entry.d_name, "..") == 0)) {
  371. continue;
  372. }
  373. Result = SwAppendPath(Argument,
  374. strlen(Argument) + 1,
  375. Entry.d_name,
  376. strlen(Entry.d_name) + 1,
  377. &AppendedPath,
  378. &AppendedPathSize);
  379. if (Result == FALSE) {
  380. Result = ENOMEM;
  381. goto ChangePermissionsEnd;
  382. }
  383. Result = ChmodChangePermissions(Options, ModeString, AppendedPath);
  384. free(AppendedPath);
  385. if (Result != 0) {
  386. goto ChangePermissionsEnd;
  387. }
  388. }
  389. ChangePermissionsEnd:
  390. if (Directory != NULL) {
  391. closedir(Directory);
  392. }
  393. return Result;
  394. }