PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/hadoop-common-project/hadoop-common/src/main/winutils/chmod.c

http://github.com/apache/hadoop-common
C | 892 lines | 783 code | 38 blank | 71 comment | 29 complexity | 2efe7b5fb11ea5211ecacb6ed4bb65f4 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with this
  4. * work for additional information regarding copyright ownership. The ASF
  5. * licenses this file to you under the Apache License, Version 2.0 (the
  6. * "License"); you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. * License for the specific language governing permissions and limitations under
  15. * the License.
  16. */
  17. #include "winutils.h"
  18. #include <errno.h>
  19. enum CHMOD_WHO
  20. {
  21. CHMOD_WHO_NONE = 0,
  22. CHMOD_WHO_OTHER = 07,
  23. CHMOD_WHO_GROUP = 070,
  24. CHMOD_WHO_USER = 0700,
  25. CHMOD_WHO_ALL = CHMOD_WHO_OTHER | CHMOD_WHO_GROUP | CHMOD_WHO_USER
  26. };
  27. enum CHMOD_OP
  28. {
  29. CHMOD_OP_INVALID,
  30. CHMOD_OP_PLUS,
  31. CHMOD_OP_MINUS,
  32. CHMOD_OP_EQUAL,
  33. };
  34. enum CHMOD_PERM
  35. {
  36. CHMOD_PERM_NA = 00,
  37. CHMOD_PERM_R = 01,
  38. CHMOD_PERM_W = 02,
  39. CHMOD_PERM_X = 04,
  40. CHMOD_PERM_LX = 010,
  41. };
  42. /*
  43. * We use the following struct to build a linked list of mode change actions.
  44. * The mode is described by the following grammar:
  45. * mode ::= clause [, clause ...]
  46. * clause ::= [who ...] [action ...]
  47. * action ::= op [perm ...] | op [ref]
  48. * who ::= a | u | g | o
  49. * op ::= + | - | =
  50. * perm ::= r | w | x | X
  51. * ref ::= u | g | o
  52. */
  53. typedef struct _MODE_CHANGE_ACTION
  54. {
  55. USHORT who;
  56. USHORT op;
  57. USHORT perm;
  58. USHORT ref;
  59. struct _MODE_CHANGE_ACTION *next_action;
  60. } MODE_CHANGE_ACTION, *PMODE_CHANGE_ACTION;
  61. const MODE_CHANGE_ACTION INIT_MODE_CHANGE_ACTION = {
  62. CHMOD_WHO_NONE, CHMOD_OP_INVALID, CHMOD_PERM_NA, CHMOD_WHO_NONE, NULL
  63. };
  64. static BOOL ParseOctalMode(LPCWSTR tsMask, INT *uMask);
  65. static BOOL ParseMode(LPCWSTR modeString, PMODE_CHANGE_ACTION *actions);
  66. static BOOL FreeActions(PMODE_CHANGE_ACTION actions);
  67. static BOOL ParseCommandLineArguments(
  68. __in int argc,
  69. __in_ecount(argc) wchar_t *argv[],
  70. __out BOOL *rec,
  71. __out_opt INT *mask,
  72. __out_opt PMODE_CHANGE_ACTION *actions,
  73. __out LPCWSTR *path);
  74. static BOOL ChangeFileModeByActions(__in LPCWSTR path,
  75. MODE_CHANGE_ACTION const *actions);
  76. static BOOL ChangeFileMode(__in LPCWSTR path, __in_opt INT mode,
  77. __in_opt MODE_CHANGE_ACTION const *actions);
  78. static BOOL ChangeFileModeRecursively(__in LPCWSTR path, __in_opt INT mode,
  79. __in_opt MODE_CHANGE_ACTION const *actions);
  80. //----------------------------------------------------------------------------
  81. // Function: Chmod
  82. //
  83. // Description:
  84. // The main method for chmod command
  85. //
  86. // Returns:
  87. // 0: on success
  88. //
  89. // Notes:
  90. //
  91. int Chmod(__in int argc, __in_ecount(argc) wchar_t *argv[])
  92. {
  93. LPWSTR pathName = NULL;
  94. LPWSTR longPathName = NULL;
  95. BOOL recursive = FALSE;
  96. PMODE_CHANGE_ACTION actions = NULL;
  97. INT unixAccessMask = 0;
  98. DWORD dwRtnCode = 0;
  99. int ret = EXIT_FAILURE;
  100. // Parsing chmod arguments
  101. //
  102. if (!ParseCommandLineArguments(argc, argv,
  103. &recursive, &unixAccessMask, &actions, &pathName))
  104. {
  105. fwprintf(stderr, L"Incorrect command line arguments.\n\n");
  106. ChmodUsage(argv[0]);
  107. return EXIT_FAILURE;
  108. }
  109. // Convert the path the the long path
  110. //
  111. dwRtnCode = ConvertToLongPath(pathName, &longPathName);
  112. if (dwRtnCode != ERROR_SUCCESS)
  113. {
  114. ReportErrorCode(L"ConvertToLongPath", dwRtnCode);
  115. goto ChmodEnd;
  116. }
  117. if (!recursive)
  118. {
  119. if (ChangeFileMode(longPathName, unixAccessMask, actions))
  120. {
  121. ret = EXIT_SUCCESS;
  122. }
  123. }
  124. else
  125. {
  126. if (ChangeFileModeRecursively(longPathName, unixAccessMask, actions))
  127. {
  128. ret = EXIT_SUCCESS;
  129. }
  130. }
  131. ChmodEnd:
  132. FreeActions(actions);
  133. LocalFree(longPathName);
  134. return ret;
  135. }
  136. //----------------------------------------------------------------------------
  137. // Function: ChangeFileMode
  138. //
  139. // Description:
  140. // Wrapper function for change file mode. Choose either change by action or by
  141. // access mask.
  142. //
  143. // Returns:
  144. // TRUE: on success
  145. // FALSE: otherwise
  146. //
  147. // Notes:
  148. //
  149. static BOOL ChangeFileMode(__in LPCWSTR path, __in_opt INT unixAccessMask,
  150. __in_opt MODE_CHANGE_ACTION const *actions)
  151. {
  152. if (actions != NULL)
  153. return ChangeFileModeByActions(path, actions);
  154. else
  155. {
  156. DWORD dwRtnCode = ChangeFileModeByMask(path, unixAccessMask);
  157. if (dwRtnCode != ERROR_SUCCESS)
  158. {
  159. ReportErrorCode(L"ChangeFileModeByMask", dwRtnCode);
  160. return FALSE;
  161. }
  162. return TRUE;
  163. }
  164. }
  165. //----------------------------------------------------------------------------
  166. // Function: ChangeFileModeRecursively
  167. //
  168. // Description:
  169. // Travel the directory recursively to change the permissions.
  170. //
  171. // Returns:
  172. // TRUE: on success
  173. // FALSE: otherwise
  174. //
  175. // Notes:
  176. // The recursion works in the following way:
  177. // - If the path is not a directory, change its mode and return.
  178. // Symbolic links and junction points are not considered as directories.
  179. // - Otherwise, call the method on all its children, then change its mode.
  180. //
  181. static BOOL ChangeFileModeRecursively(__in LPCWSTR path, __in_opt INT mode,
  182. __in_opt MODE_CHANGE_ACTION const *actions)
  183. {
  184. BOOL isDir = FALSE;
  185. BOOL isSymlink = FALSE;
  186. LPWSTR dir = NULL;
  187. size_t pathSize = 0;
  188. size_t dirSize = 0;
  189. HANDLE hFind = INVALID_HANDLE_VALUE;
  190. WIN32_FIND_DATA ffd;
  191. DWORD dwRtnCode = ERROR_SUCCESS;
  192. BOOL ret = FALSE;
  193. if ((dwRtnCode = DirectoryCheck(path, &isDir)) != ERROR_SUCCESS)
  194. {
  195. ReportErrorCode(L"IsDirectory", dwRtnCode);
  196. return FALSE;
  197. }
  198. if ((dwRtnCode = SymbolicLinkCheck(path, &isSymlink)) != ERROR_SUCCESS)
  199. {
  200. ReportErrorCode(L"IsSymbolicLink", dwRtnCode);
  201. return FALSE;
  202. }
  203. if (isSymlink || !isDir)
  204. {
  205. if (ChangeFileMode(path, mode, actions))
  206. return TRUE;
  207. else
  208. return FALSE;
  209. }
  210. if (FAILED(StringCchLengthW(path, STRSAFE_MAX_CCH - 3, &pathSize)))
  211. {
  212. return FALSE;
  213. }
  214. dirSize = pathSize + 3;
  215. dir = (LPWSTR)LocalAlloc(LPTR, dirSize * sizeof(WCHAR));
  216. if (dir == NULL)
  217. {
  218. ReportErrorCode(L"LocalAlloc", GetLastError());
  219. goto ChangeFileModeRecursivelyEnd;
  220. }
  221. if (FAILED(StringCchCopyW(dir, dirSize, path)) ||
  222. FAILED(StringCchCatW(dir, dirSize, L"\\*")))
  223. {
  224. goto ChangeFileModeRecursivelyEnd;
  225. }
  226. hFind = FindFirstFile(dir, &ffd);
  227. if (hFind == INVALID_HANDLE_VALUE)
  228. {
  229. ReportErrorCode(L"FindFirstFile", GetLastError());
  230. goto ChangeFileModeRecursivelyEnd;
  231. }
  232. do
  233. {
  234. LPWSTR filename = NULL;
  235. LPWSTR longFilename = NULL;
  236. size_t filenameSize = 0;
  237. if (wcscmp(ffd.cFileName, L".") == 0 ||
  238. wcscmp(ffd.cFileName, L"..") == 0)
  239. continue;
  240. filenameSize = pathSize + wcslen(ffd.cFileName) + 2;
  241. filename = (LPWSTR)LocalAlloc(LPTR, filenameSize * sizeof(WCHAR));
  242. if (filename == NULL)
  243. {
  244. ReportErrorCode(L"LocalAlloc", GetLastError());
  245. goto ChangeFileModeRecursivelyEnd;
  246. }
  247. if (FAILED(StringCchCopyW(filename, filenameSize, path)) ||
  248. FAILED(StringCchCatW(filename, filenameSize, L"\\")) ||
  249. FAILED(StringCchCatW(filename, filenameSize, ffd.cFileName)))
  250. {
  251. LocalFree(filename);
  252. goto ChangeFileModeRecursivelyEnd;
  253. }
  254. // The child fileanme is not prepended with long path prefix.
  255. // Convert the filename to long path format.
  256. //
  257. dwRtnCode = ConvertToLongPath(filename, &longFilename);
  258. LocalFree(filename);
  259. if (dwRtnCode != ERROR_SUCCESS)
  260. {
  261. ReportErrorCode(L"ConvertToLongPath", dwRtnCode);
  262. LocalFree(longFilename);
  263. goto ChangeFileModeRecursivelyEnd;
  264. }
  265. if(!ChangeFileModeRecursively(longFilename, mode, actions))
  266. {
  267. LocalFree(longFilename);
  268. goto ChangeFileModeRecursivelyEnd;
  269. }
  270. LocalFree(longFilename);
  271. } while (FindNextFileW(hFind, &ffd));
  272. if (!ChangeFileMode(path, mode, actions))
  273. {
  274. goto ChangeFileModeRecursivelyEnd;
  275. }
  276. ret = TRUE;
  277. ChangeFileModeRecursivelyEnd:
  278. LocalFree(dir);
  279. return ret;
  280. }
  281. //----------------------------------------------------------------------------
  282. // Function: ParseCommandLineArguments
  283. //
  284. // Description:
  285. // Parse command line arguments for chmod.
  286. //
  287. // Returns:
  288. // TRUE: on success
  289. // FALSE: otherwise
  290. //
  291. // Notes:
  292. // 1. Recursive is only set on directories
  293. // 2. 'actions' is NULL if the mode is octal
  294. //
  295. static BOOL ParseCommandLineArguments(
  296. __in int argc,
  297. __in_ecount(argc) wchar_t *argv[],
  298. __out BOOL *rec,
  299. __out_opt INT *mask,
  300. __out_opt PMODE_CHANGE_ACTION *actions,
  301. __out LPCWSTR *path)
  302. {
  303. LPCWSTR maskString;
  304. BY_HANDLE_FILE_INFORMATION fileInfo;
  305. DWORD dwRtnCode = ERROR_SUCCESS;
  306. assert(path != NULL);
  307. if (argc != 3 && argc != 4)
  308. return FALSE;
  309. *rec = FALSE;
  310. if (argc == 4)
  311. {
  312. maskString = argv[2];
  313. *path = argv[3];
  314. if (wcscmp(argv[1], L"-R") == 0)
  315. {
  316. // Check if the given path name is a file or directory
  317. // Only set recursive flag if the given path is a directory
  318. //
  319. dwRtnCode = GetFileInformationByName(*path, FALSE, &fileInfo);
  320. if (dwRtnCode != ERROR_SUCCESS)
  321. {
  322. ReportErrorCode(L"GetFileInformationByName", dwRtnCode);
  323. return FALSE;
  324. }
  325. if (IsDirFileInfo(&fileInfo))
  326. {
  327. *rec = TRUE;
  328. }
  329. }
  330. else
  331. return FALSE;
  332. }
  333. else
  334. {
  335. maskString = argv[1];
  336. *path = argv[2];
  337. }
  338. if (ParseOctalMode(maskString, mask))
  339. {
  340. return TRUE;
  341. }
  342. else if (ParseMode(maskString, actions))
  343. {
  344. return TRUE;
  345. }
  346. return FALSE;
  347. }
  348. //----------------------------------------------------------------------------
  349. // Function: FreeActions
  350. //
  351. // Description:
  352. // Free a linked list of mode change actions given the head node.
  353. //
  354. // Returns:
  355. // TRUE: on success
  356. // FALSE: otherwise
  357. //
  358. // Notes:
  359. // none
  360. //
  361. static BOOL FreeActions(PMODE_CHANGE_ACTION actions)
  362. {
  363. PMODE_CHANGE_ACTION curr = NULL;
  364. PMODE_CHANGE_ACTION next = NULL;
  365. // Nothing to free if NULL is passed in
  366. //
  367. if (actions == NULL)
  368. {
  369. return TRUE;
  370. }
  371. curr = actions;
  372. while (curr != NULL)
  373. {
  374. next = curr->next_action;
  375. LocalFree(curr);
  376. curr = next;
  377. }
  378. actions = NULL;
  379. return TRUE;
  380. }
  381. //----------------------------------------------------------------------------
  382. // Function: ComputeNewMode
  383. //
  384. // Description:
  385. // Compute a new mode based on the old mode and a mode change action.
  386. //
  387. // Returns:
  388. // The newly computed mode
  389. //
  390. // Notes:
  391. // Apply 'rwx' permission mask or reference permission mode according to the
  392. // '+', '-', or '=' operator.
  393. //
  394. static INT ComputeNewMode(__in INT oldMode,
  395. __in USHORT who, __in USHORT op,
  396. __in USHORT perm, __in USHORT ref)
  397. {
  398. static const INT readMask = 0444;
  399. static const INT writeMask = 0222;
  400. static const INT exeMask = 0111;
  401. INT mask = 0;
  402. INT mode = 0;
  403. // Operations are exclusive, and cannot be invalid
  404. //
  405. assert(op == CHMOD_OP_EQUAL || op == CHMOD_OP_PLUS || op == CHMOD_OP_MINUS);
  406. // Nothing needs to be changed if there is not permission or reference
  407. //
  408. if(perm == CHMOD_PERM_NA && ref == CHMOD_WHO_NONE)
  409. {
  410. return oldMode;
  411. }
  412. // We should have only permissions or a reference target, not both.
  413. //
  414. assert((perm != CHMOD_PERM_NA && ref == CHMOD_WHO_NONE) ||
  415. (perm == CHMOD_PERM_NA && ref != CHMOD_WHO_NONE));
  416. if (perm != CHMOD_PERM_NA)
  417. {
  418. if ((perm & CHMOD_PERM_R) == CHMOD_PERM_R)
  419. mask |= readMask;
  420. if ((perm & CHMOD_PERM_W) == CHMOD_PERM_W)
  421. mask |= writeMask;
  422. if ((perm & CHMOD_PERM_X) == CHMOD_PERM_X)
  423. mask |= exeMask;
  424. if (((perm & CHMOD_PERM_LX) == CHMOD_PERM_LX))
  425. {
  426. // It applies execute permissions to directories regardless of their
  427. // current permissions and applies execute permissions to a file which
  428. // already has at least 1 execute permission bit already set (either user,
  429. // group or other). It is only really useful when used with '+' and
  430. // usually in combination with the -R option for giving group or other
  431. // access to a big directory tree without setting execute permission on
  432. // normal files (such as text files), which would normally happen if you
  433. // just used "chmod -R a+rx .", whereas with 'X' you can do
  434. // "chmod -R a+rX ." instead (Source: Wikipedia)
  435. //
  436. if ((oldMode & UX_DIRECTORY) == UX_DIRECTORY || (oldMode & exeMask))
  437. mask |= exeMask;
  438. }
  439. }
  440. else if (ref != CHMOD_WHO_NONE)
  441. {
  442. mask |= oldMode & ref;
  443. switch(ref)
  444. {
  445. case CHMOD_WHO_GROUP:
  446. mask |= mask >> 3;
  447. mask |= mask << 3;
  448. break;
  449. case CHMOD_WHO_OTHER:
  450. mask |= mask << 3;
  451. mask |= mask << 6;
  452. break;
  453. case CHMOD_WHO_USER:
  454. mask |= mask >> 3;
  455. mask |= mask >> 6;
  456. break;
  457. default:
  458. // Reference modes can only be U/G/O and are exclusive
  459. assert(FALSE);
  460. }
  461. }
  462. mask &= who;
  463. if (op == CHMOD_OP_EQUAL)
  464. {
  465. mode = (oldMode & (~who)) | mask;
  466. }
  467. else if (op == CHMOD_OP_MINUS)
  468. {
  469. mode = oldMode & (~mask);
  470. }
  471. else if (op == CHMOD_OP_PLUS)
  472. {
  473. mode = oldMode | mask;
  474. }
  475. return mode;
  476. }
  477. //----------------------------------------------------------------------------
  478. // Function: ConvertActionsToMask
  479. //
  480. // Description:
  481. // Convert a linked list of mode change actions to the Unix permission mask
  482. // given the head node.
  483. //
  484. // Returns:
  485. // TRUE: on success
  486. // FALSE: otherwise
  487. //
  488. // Notes:
  489. // none
  490. //
  491. static BOOL ConvertActionsToMask(__in LPCWSTR path,
  492. __in MODE_CHANGE_ACTION const *actions, __out PINT puMask)
  493. {
  494. MODE_CHANGE_ACTION const *curr = NULL;
  495. DWORD dwErrorCode = ERROR_SUCCESS;
  496. INT mode = 0;
  497. dwErrorCode = FindFileOwnerAndPermission(path, FALSE, NULL, NULL, &mode);
  498. if (dwErrorCode != ERROR_SUCCESS)
  499. {
  500. ReportErrorCode(L"FindFileOwnerAndPermission", dwErrorCode);
  501. return FALSE;
  502. }
  503. *puMask = mode;
  504. // Nothing to change if NULL is passed in
  505. //
  506. if (actions == NULL)
  507. {
  508. return TRUE;
  509. }
  510. for (curr = actions; curr != NULL; curr = curr->next_action)
  511. {
  512. mode = ComputeNewMode(mode, curr->who, curr->op, curr->perm, curr->ref);
  513. }
  514. *puMask = mode;
  515. return TRUE;
  516. }
  517. //----------------------------------------------------------------------------
  518. // Function: ChangeFileModeByActions
  519. //
  520. // Description:
  521. // Change a file mode through a list of actions.
  522. //
  523. // Returns:
  524. // TRUE: on success
  525. // FALSE: otherwise
  526. //
  527. // Notes:
  528. // none
  529. //
  530. static BOOL ChangeFileModeByActions(__in LPCWSTR path,
  531. MODE_CHANGE_ACTION const *actions)
  532. {
  533. INT mask = 0;
  534. if (ConvertActionsToMask(path, actions, &mask))
  535. {
  536. DWORD dwRtnCode = ChangeFileModeByMask(path, mask);
  537. if (dwRtnCode != ERROR_SUCCESS)
  538. {
  539. ReportErrorCode(L"ChangeFileModeByMask", dwRtnCode);
  540. return FALSE;
  541. }
  542. return TRUE;
  543. }
  544. else
  545. return FALSE;
  546. }
  547. //----------------------------------------------------------------------------
  548. // Function: ParseMode
  549. //
  550. // Description:
  551. // Convert a mode string into a linked list of actions
  552. //
  553. // Returns:
  554. // TRUE: on success
  555. // FALSE: otherwise
  556. //
  557. // Notes:
  558. // Take a state machine approach to parse the mode. Each mode change action
  559. // will be a node in the output linked list. The state machine has five state,
  560. // and each will only transit to the next; the end state can transit back to
  561. // the first state, and thus form a circle. In each state, if we see a
  562. // a character not belongs to the state, we will move to next state. WHO, PERM,
  563. // and REF states are optional; OP and END states are required; and errors
  564. // will only be reported at the latter two states.
  565. //
  566. static BOOL ParseMode(LPCWSTR modeString, PMODE_CHANGE_ACTION *pActions)
  567. {
  568. enum __PARSE_MODE_ACTION_STATE
  569. {
  570. PARSE_MODE_ACTION_WHO_STATE,
  571. PARSE_MODE_ACTION_OP_STATE,
  572. PARSE_MODE_ACTION_PERM_STATE,
  573. PARSE_MODE_ACTION_REF_STATE,
  574. PARSE_MODE_ACTION_END_STATE
  575. } state = PARSE_MODE_ACTION_WHO_STATE;
  576. MODE_CHANGE_ACTION action = INIT_MODE_CHANGE_ACTION;
  577. PMODE_CHANGE_ACTION actionsEnd = NULL;
  578. PMODE_CHANGE_ACTION actionsLast = NULL;
  579. USHORT lastWho;
  580. WCHAR c = 0;
  581. size_t len = 0;
  582. size_t i = 0;
  583. assert(modeString != NULL && pActions != NULL);
  584. if (FAILED(StringCchLengthW(modeString, STRSAFE_MAX_CCH, &len)))
  585. {
  586. return FALSE;
  587. }
  588. actionsEnd = *pActions;
  589. while(i <= len)
  590. {
  591. c = modeString[i];
  592. if (state == PARSE_MODE_ACTION_WHO_STATE)
  593. {
  594. switch (c)
  595. {
  596. case L'a':
  597. action.who |= CHMOD_WHO_ALL;
  598. i++;
  599. break;
  600. case L'u':
  601. action.who |= CHMOD_WHO_USER;
  602. i++;
  603. break;
  604. case L'g':
  605. action.who |= CHMOD_WHO_GROUP;
  606. i++;
  607. break;
  608. case L'o':
  609. action.who |= CHMOD_WHO_OTHER;
  610. i++;
  611. break;
  612. default:
  613. state = PARSE_MODE_ACTION_OP_STATE;
  614. } // WHO switch
  615. }
  616. else if (state == PARSE_MODE_ACTION_OP_STATE)
  617. {
  618. switch (c)
  619. {
  620. case L'+':
  621. action.op = CHMOD_OP_PLUS;
  622. break;
  623. case L'-':
  624. action.op = CHMOD_OP_MINUS;
  625. break;
  626. case L'=':
  627. action.op = CHMOD_OP_EQUAL;
  628. break;
  629. default:
  630. fwprintf(stderr, L"Invalid mode: '%s'\n", modeString);
  631. FreeActions(*pActions);
  632. return FALSE;
  633. } // OP switch
  634. i++;
  635. state = PARSE_MODE_ACTION_PERM_STATE;
  636. }
  637. else if (state == PARSE_MODE_ACTION_PERM_STATE)
  638. {
  639. switch (c)
  640. {
  641. case L'r':
  642. action.perm |= CHMOD_PERM_R;
  643. i++;
  644. break;
  645. case L'w':
  646. action.perm |= CHMOD_PERM_W;
  647. i++;
  648. break;
  649. case L'x':
  650. action.perm |= CHMOD_PERM_X;
  651. i++;
  652. break;
  653. case L'X':
  654. action.perm |= CHMOD_PERM_LX;
  655. i++;
  656. break;
  657. default:
  658. state = PARSE_MODE_ACTION_REF_STATE;
  659. } // PERM switch
  660. }
  661. else if (state == PARSE_MODE_ACTION_REF_STATE)
  662. {
  663. switch (c)
  664. {
  665. case L'u':
  666. action.ref = CHMOD_WHO_USER;
  667. i++;
  668. break;
  669. case L'g':
  670. action.ref = CHMOD_WHO_GROUP;
  671. i++;
  672. break;
  673. case L'o':
  674. action.ref = CHMOD_WHO_OTHER;
  675. i++;
  676. break;
  677. default:
  678. state = PARSE_MODE_ACTION_END_STATE;
  679. } // REF switch
  680. }
  681. else if (state == PARSE_MODE_ACTION_END_STATE)
  682. {
  683. switch (c)
  684. {
  685. case NULL:
  686. __fallthrough;
  687. case L',':
  688. i++;
  689. __fallthrough;
  690. case L'+':
  691. __fallthrough;
  692. case L'-':
  693. __fallthrough;
  694. case L'=':
  695. state = PARSE_MODE_ACTION_WHO_STATE;
  696. // Append the current action to the end of the linked list
  697. //
  698. assert(actionsEnd == NULL);
  699. // Allocate memory
  700. actionsEnd = (PMODE_CHANGE_ACTION) LocalAlloc(LPTR,
  701. sizeof(MODE_CHANGE_ACTION));
  702. if (actionsEnd == NULL)
  703. {
  704. ReportErrorCode(L"LocalAlloc", GetLastError());
  705. FreeActions(*pActions);
  706. return FALSE;
  707. }
  708. if (action.who == CHMOD_WHO_NONE) action.who = CHMOD_WHO_ALL;
  709. // Copy the action to the new node
  710. *actionsEnd = action;
  711. // Append to the last node in the linked list
  712. if (actionsLast != NULL) actionsLast->next_action = actionsEnd;
  713. // pActions should point to the head of the linked list
  714. if (*pActions == NULL) *pActions = actionsEnd;
  715. // Update the two pointers to point to the last node and the tail
  716. actionsLast = actionsEnd;
  717. actionsEnd = actionsLast->next_action;
  718. // Reset action
  719. //
  720. lastWho = action.who;
  721. action = INIT_MODE_CHANGE_ACTION;
  722. if (c != L',')
  723. {
  724. action.who = lastWho;
  725. }
  726. break;
  727. default:
  728. fwprintf(stderr, L"Invalid mode: '%s'\n", modeString);
  729. FreeActions(*pActions);
  730. return FALSE;
  731. } // END switch
  732. }
  733. } // while
  734. return TRUE;
  735. }
  736. //----------------------------------------------------------------------------
  737. // Function: ParseOctalMode
  738. //
  739. // Description:
  740. // Convert the 3 or 4 digits Unix mask string into the binary representation
  741. // of the Unix access mask, i.e. 9 bits each an indicator of the permission
  742. // of 'rwxrwxrwx', i.e. user's, group's, and owner's read, write, and
  743. // execute/search permissions.
  744. //
  745. // Returns:
  746. // TRUE: on success
  747. // FALSE: otherwise
  748. //
  749. // Notes:
  750. // none
  751. //
  752. static BOOL ParseOctalMode(LPCWSTR tsMask, INT *uMask)
  753. {
  754. size_t tsMaskLen = 0;
  755. DWORD i;
  756. LONG l;
  757. WCHAR *end;
  758. if (uMask == NULL)
  759. return FALSE;
  760. if (FAILED(StringCchLengthW(tsMask, STRSAFE_MAX_CCH, &tsMaskLen)))
  761. return FALSE;
  762. if (tsMaskLen == 0 || tsMaskLen > 4)
  763. {
  764. return FALSE;
  765. }
  766. for (i = 0; i < tsMaskLen; i++)
  767. {
  768. if (!(tsMask[tsMaskLen - i - 1] >= L'0' &&
  769. tsMask[tsMaskLen - i - 1] <= L'7'))
  770. return FALSE;
  771. }
  772. errno = 0;
  773. if (tsMaskLen == 4)
  774. // Windows does not have any equivalent of setuid/setgid and sticky bit.
  775. // So the first bit is omitted for the 4 digit octal mode case.
  776. //
  777. l = wcstol(tsMask + 1, &end, 8);
  778. else
  779. l = wcstol(tsMask, &end, 8);
  780. if (errno || l > 0x0777 || l < 0 || *end != 0)
  781. {
  782. return FALSE;
  783. }
  784. *uMask = (INT) l;
  785. return TRUE;
  786. }
  787. void ChmodUsage(LPCWSTR program)
  788. {
  789. fwprintf(stdout, L"\
  790. Usage: %s [OPTION] OCTAL-MODE [FILE]\n\
  791. or: %s [OPTION] MODE [FILE]\n\
  792. Change the mode of the FILE to MODE.\n\
  793. \n\
  794. -R: change files and directories recursively\n\
  795. \n\
  796. Each MODE is of the form '[ugoa]*([-+=]([rwxX]*|[ugo]))+'.\n",
  797. program, program);
  798. }