mktemp.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. mktemp.c
  5. Abstract:
  6. This module implements the mktemp (temporary file and directory creation)
  7. utility.
  8. Author:
  9. Evan Green 11-Oct-2013
  10. Environment:
  11. POSIX
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/lib/types.h>
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <getopt.h>
  21. #include <libgen.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <unistd.h>
  25. #include "swlib.h"
  26. //
  27. // ---------------------------------------------------------------- Definitions
  28. //
  29. #define MKTEMP_VERSION_MAJOR 1
  30. #define MKTEMP_VERSION_MINOR 0
  31. #define MKTEMP_USAGE \
  32. "usage: mktemp [-duq] [--suffix=SUFFIX] [--tmpdir[=DIR]] [-p DIR] [-t] " \
  33. "template\n " \
  34. "The mktemp utility creates a temporary file or directory safely and \n" \
  35. "prints its name. If no template is supplied, tmp.XXXXXXXXXX is used, \n" \
  36. "and --tmpdir is implied. Valid options are:\n" \
  37. " -d, --directory -- Create a directory, not a file.\n" \
  38. " -u, --dry-run -- Do not create anything, just print a name.\n" \
  39. " -q, --quiet -- Suppress messages about file/directory creation.\n" \
  40. " --suffix=SUFFIX -- Append the given suffix to the template.\n" \
  41. " --tmpdir=DIR -- Prepend the given directory to the template. If not \n" \
  42. " specified, prepend the value of the TMPDIR environment " \
  43. "variable.\n" \
  44. " -p DIR -- Use the given directory as a prefix.\n" \
  45. " -t -- Interpret the template relative to a directory: TMPDIR if set, \n"\
  46. " or the directory specified by -p, or /tmp.\n" \
  47. " --help -- Display this help text and exit.\n" \
  48. " --version -- Display the application version and exit.\n"
  49. #define MKTEMP_OPTIONS_STRING "duqp:t"
  50. //
  51. // Define the name of the environment variable mktemp looks at to get the
  52. // temporary directory prefix in some scenarios.
  53. //
  54. #define MKTEMP_DIRECTORY_VARIABLE "TMPDIR"
  55. //
  56. // Define the default template to use if none is provided.
  57. //
  58. #define MKTEMP_DEFAULT_TEMPLATE "tmp.XXXXXXXXXX"
  59. //
  60. // Define the permissions on temporary files.
  61. //
  62. #define TEMPORARY_FILE_PERMISSIONS (S_IRUSR | S_IWUSR)
  63. //
  64. // Define the permissions on a temporary directory.
  65. //
  66. #define TEMPORARY_DIRECTORY_PERMISSIONS (S_IRUSR | S_IWUSR | S_IXUSR)
  67. //
  68. // Define the minimum number of trailing X characters to enforce.
  69. //
  70. #define MKTEMP_MINIMUM_REPLACE_COUNT 3
  71. //
  72. // Define the number of times mktemp will try before giving up.
  73. //
  74. #define MKTEMP_TRY_COUNT 1000000
  75. //
  76. // Define mktemp option flags.
  77. //
  78. //
  79. // Set this flag to create a directory instead of a file.
  80. //
  81. #define MKTEMP_OPTION_DIRECTORY 0x00000001
  82. //
  83. // Set this flag to only perform a dry run, not actually create a
  84. // file/directory.
  85. //
  86. #define MKTEMP_OPTION_DRY_RUN 0x00000002
  87. //
  88. // Set this option to not print stuff.
  89. //
  90. #define MKTEMP_OPTION_QUIET 0x00000004
  91. //
  92. // Set this option to use the old directory order (the -t option).
  93. //
  94. #define MKTEMP_OPTION_OLD_DIRECTORY_ORDER 0x00000008
  95. //
  96. // ------------------------------------------------------ Data Type Definitions
  97. //
  98. //
  99. // ----------------------------------------------- Internal Function Prototypes
  100. //
  101. VOID
  102. MktempReplaceTemplate (
  103. PSTR String,
  104. UINTN ReplaceCount
  105. );
  106. //
  107. // -------------------------------------------------------------------- Globals
  108. //
  109. struct option MktempLongOptions[] = {
  110. {"directory", no_argument, 0, 'd'},
  111. {"dry-run", no_argument, 0, 'u'},
  112. {"quiet", no_argument, 0, 'q'},
  113. {"suffix", required_argument, 0, 's'},
  114. {"tmpdir", optional_argument, 0, 'T'},
  115. {"help", no_argument, 0, 'h'},
  116. {"version", no_argument, 0, 'V'},
  117. {NULL, 0, 0, 0},
  118. };
  119. //
  120. // ------------------------------------------------------------------ Functions
  121. //
  122. INT
  123. MktempMain (
  124. INT ArgumentCount,
  125. CHAR **Arguments
  126. )
  127. /*++
  128. Routine Description:
  129. This routine is the main entry point for the mktemp utility.
  130. Arguments:
  131. ArgumentCount - Supplies the number of command line arguments the program
  132. was invoked with.
  133. Arguments - Supplies a tokenized array of command line arguments.
  134. Return Value:
  135. Returns an integer exit code. 0 for success, nonzero otherwise.
  136. --*/
  137. {
  138. PSTR AppendedPath;
  139. BOOL AppendedPathAllocated;
  140. ULONG AppendedPathSize;
  141. ULONG ArgumentIndex;
  142. INT Descriptor;
  143. size_t Length;
  144. INT Option;
  145. ULONG Options;
  146. PSTR Prefix;
  147. PSTR PrefixedPath;
  148. BOOL PrefixedPathAllocated;
  149. ULONG PrefixedPathSize;
  150. ULONG PrefixSize;
  151. size_t ReplaceCount;
  152. struct stat Stat;
  153. int Status;
  154. PSTR Suffix;
  155. ULONG SuffixSize;
  156. PSTR Template;
  157. PSTR TemplateCopy;
  158. ULONG TemplateSize;
  159. ULONG Try;
  160. PSTR Variable;
  161. AppendedPathAllocated = FALSE;
  162. Prefix = NULL;
  163. PrefixedPath = NULL;
  164. PrefixedPathAllocated = FALSE;
  165. Options = 0;
  166. Suffix = NULL;
  167. Template = NULL;
  168. TemplateCopy = NULL;
  169. srand(time(NULL) ^ getpid());
  170. //
  171. // Process the control arguments.
  172. //
  173. while (TRUE) {
  174. Option = getopt_long(ArgumentCount,
  175. Arguments,
  176. MKTEMP_OPTIONS_STRING,
  177. MktempLongOptions,
  178. NULL);
  179. if (Option == -1) {
  180. break;
  181. }
  182. if ((Option == '?') || (Option == ':')) {
  183. Status = 1;
  184. goto MainEnd;
  185. }
  186. switch (Option) {
  187. case 'd':
  188. Options |= MKTEMP_OPTION_DIRECTORY;
  189. break;
  190. case 'u':
  191. Options |= MKTEMP_OPTION_DRY_RUN;
  192. break;
  193. case 'q':
  194. Options |= MKTEMP_OPTION_QUIET;
  195. break;
  196. case 'p':
  197. Prefix = optarg;
  198. break;
  199. case 't':
  200. Options |= MKTEMP_OPTION_OLD_DIRECTORY_ORDER;
  201. break;
  202. case 's':
  203. Suffix = optarg;
  204. break;
  205. case 'T':
  206. Options &= ~MKTEMP_OPTION_OLD_DIRECTORY_ORDER;
  207. Prefix = optarg;
  208. if (Prefix == NULL) {
  209. Prefix = getenv(MKTEMP_DIRECTORY_VARIABLE);
  210. }
  211. break;
  212. case 'V':
  213. SwPrintVersion(MKTEMP_VERSION_MAJOR, MKTEMP_VERSION_MINOR);
  214. return 1;
  215. case 'h':
  216. printf(MKTEMP_USAGE);
  217. return 1;
  218. default:
  219. assert(FALSE);
  220. Status = 1;
  221. goto MainEnd;
  222. }
  223. }
  224. ArgumentIndex = optind;
  225. if (ArgumentIndex > ArgumentCount) {
  226. ArgumentIndex = ArgumentCount;
  227. }
  228. Template = NULL;
  229. if (ArgumentCount - ArgumentIndex != 0) {
  230. Template = Arguments[ArgumentIndex];
  231. ArgumentIndex += 1;
  232. //
  233. // If there was no template, use a default one, and get the prefix from the
  234. // environment if one is not already set.
  235. //
  236. } else {
  237. Template = MKTEMP_DEFAULT_TEMPLATE;
  238. Options |= MKTEMP_OPTION_OLD_DIRECTORY_ORDER;
  239. }
  240. //
  241. // If the old style directory order is in effect, then prefer the contents
  242. // of the variable TMPDIR, followed by the argument from -p, followed by
  243. // /tmp.
  244. //
  245. if ((Options & MKTEMP_OPTION_OLD_DIRECTORY_ORDER) != 0) {
  246. Variable = getenv(MKTEMP_DIRECTORY_VARIABLE);
  247. if (Variable != NULL) {
  248. Prefix = Variable;
  249. }
  250. if (Prefix == NULL) {
  251. Prefix = "/tmp";
  252. }
  253. }
  254. if (ArgumentCount - ArgumentIndex > 1) {
  255. SwPrintError(0, NULL, "Expected no more than one operand");
  256. Status = 1;
  257. goto MainEnd;
  258. }
  259. //
  260. // Ensure there are enough X characters to replace.
  261. //
  262. Length = strlen(Template);
  263. ReplaceCount = 0;
  264. while ((Length - ReplaceCount != 0) &&
  265. (Template[Length - 1 - ReplaceCount] == 'X')) {
  266. ReplaceCount += 1;
  267. }
  268. if (ReplaceCount < MKTEMP_MINIMUM_REPLACE_COUNT) {
  269. SwPrintError(0, Template, "Too few Xs in template");
  270. Status = 1;
  271. goto MainEnd;
  272. }
  273. TemplateSize = Length + 1;
  274. PrefixSize = 0;
  275. if (Prefix != NULL) {
  276. PrefixSize = strlen(Prefix) + 1;
  277. }
  278. SuffixSize = 0;
  279. if (Suffix != NULL) {
  280. SuffixSize = strlen(Suffix) + 1;
  281. }
  282. Status = -1;
  283. TemplateCopy = strdup(Template);
  284. if (TemplateCopy == NULL) {
  285. Status = ENOMEM;
  286. goto MainEnd;
  287. }
  288. for (Try = 0; Try < MKTEMP_TRY_COUNT; Try += 1) {
  289. MktempReplaceTemplate(TemplateCopy + Length - ReplaceCount,
  290. ReplaceCount);
  291. //
  292. // Append the prefix if there is one.
  293. //
  294. if (Prefix != NULL) {
  295. Status = SwAppendPath(Prefix,
  296. PrefixSize,
  297. TemplateCopy,
  298. TemplateSize,
  299. &PrefixedPath,
  300. &PrefixedPathSize);
  301. if (Status == FALSE) {
  302. Status = ENOMEM;
  303. SwPrintError(Status, NULL, "Failed to create string");
  304. goto MainEnd;
  305. }
  306. PrefixedPathAllocated = TRUE;
  307. } else {
  308. PrefixedPath = TemplateCopy;
  309. PrefixedPathSize = TemplateSize;
  310. }
  311. //
  312. // Append the suffix if there is one.
  313. //
  314. if (Suffix != NULL) {
  315. Status = SwAppendPath(PrefixedPath,
  316. PrefixedPathSize,
  317. Suffix,
  318. SuffixSize,
  319. &AppendedPath,
  320. &AppendedPathSize);
  321. if (Status == FALSE) {
  322. Status = ENOMEM;
  323. SwPrintError(Status, NULL, "Failed to create string");
  324. goto MainEnd;
  325. }
  326. PrefixedPathAllocated = TRUE;
  327. } else {
  328. AppendedPath = PrefixedPath;
  329. AppendedPathSize = PrefixedPathSize;
  330. }
  331. //
  332. // Whew, now that the path is created, try to create it.
  333. //
  334. if ((Options & MKTEMP_OPTION_DRY_RUN) != 0) {
  335. Status = SwStat(AppendedPath, FALSE, &Stat);
  336. if ((Status != 0) && (Status == ENOENT)) {
  337. Status = 0;
  338. break;
  339. }
  340. if (Status != 0) {
  341. if ((Options & MKTEMP_OPTION_QUIET) == 0) {
  342. SwPrintError(Status, AppendedPath, "Unable to stat");
  343. }
  344. goto MainEnd;
  345. }
  346. } else if ((Options & MKTEMP_OPTION_DIRECTORY) != 0) {
  347. Status = SwMakeDirectory(AppendedPath,
  348. TEMPORARY_DIRECTORY_PERMISSIONS);
  349. if (Status == 0) {
  350. break;
  351. }
  352. if (errno != EEXIST) {
  353. if ((Options & MKTEMP_OPTION_QUIET) == 0) {
  354. SwPrintError(errno,
  355. AppendedPath,
  356. "Unable to create directory");
  357. }
  358. goto MainEnd;
  359. }
  360. } else {
  361. Descriptor = open(AppendedPath,
  362. O_RDWR | O_CREAT | O_EXCL,
  363. TEMPORARY_FILE_PERMISSIONS);
  364. if (Descriptor >= 0) {
  365. close(Descriptor);
  366. Status = 0;
  367. break;
  368. }
  369. if ((errno != EEXIST) && (errno != EISDIR)) {
  370. if ((Options & MKTEMP_OPTION_QUIET) == 0) {
  371. SwPrintError(errno, AppendedPath, "Unable to create file");
  372. }
  373. goto MainEnd;
  374. }
  375. }
  376. //
  377. // Clean up.
  378. //
  379. if (PrefixedPathAllocated != FALSE) {
  380. free(PrefixedPath);
  381. PrefixedPath = NULL;
  382. PrefixedPathAllocated = FALSE;
  383. }
  384. if (AppendedPathAllocated != FALSE) {
  385. free(AppendedPath);
  386. AppendedPath = NULL;
  387. AppendedPathAllocated = FALSE;
  388. }
  389. assert(TemplateCopy != NULL);
  390. free(TemplateCopy);
  391. TemplateCopy = NULL;
  392. }
  393. if (Try == MKTEMP_TRY_COUNT) {
  394. SwPrintError(0, NULL, "Tried %d times and failed", MKTEMP_TRY_COUNT);
  395. goto MainEnd;
  396. }
  397. //
  398. // Print out what was found!
  399. //
  400. assert(AppendedPath != NULL);
  401. if ((Options & MKTEMP_OPTION_QUIET) == 0) {
  402. printf("%s\n", AppendedPath);
  403. }
  404. Status = 0;
  405. MainEnd:
  406. if (PrefixedPathAllocated != FALSE) {
  407. free(PrefixedPath);
  408. PrefixedPath = NULL;
  409. PrefixedPathAllocated = FALSE;
  410. }
  411. if (AppendedPathAllocated != FALSE) {
  412. free(AppendedPath);
  413. AppendedPath = NULL;
  414. AppendedPathAllocated = FALSE;
  415. }
  416. free(TemplateCopy);
  417. TemplateCopy = NULL;
  418. return Status;
  419. }
  420. //
  421. // --------------------------------------------------------- Internal Functions
  422. //
  423. VOID
  424. MktempReplaceTemplate (
  425. PSTR String,
  426. UINTN ReplaceCount
  427. )
  428. /*++
  429. Routine Description:
  430. This routine creates random ASCII characters in the range of 0-9 and A-Z.
  431. Arguments:
  432. String - Supplies a pointer to the template.
  433. ReplaceCount - Supplies the number of characters to replace.
  434. Return Value:
  435. None.
  436. --*/
  437. {
  438. ULONG RandomIndex;
  439. INT Value;
  440. for (RandomIndex = 0; RandomIndex < ReplaceCount; RandomIndex += 1) {
  441. //
  442. // Create a random value using letters and numbers. Avoid relying
  443. // on case sensitivity just in case. For reference,
  444. // 36^5 is 60.4 million.
  445. //
  446. Value = rand() % 36;
  447. if (Value >= 10) {
  448. Value += 'A' - 10;
  449. } else {
  450. Value += '0';
  451. }
  452. String[RandomIndex] = Value;
  453. }
  454. return;
  455. }