tmpfile.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. /*++
  2. Copyright (c) 2013 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. tmpfile.c
  9. Abstract:
  10. This module implements support for creating temporary files.
  11. Author:
  12. Evan Green 12-Aug-2013
  13. Environment:
  14. User Mode C Library
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include "libcp.h"
  20. #include <errno.h>
  21. #include <fcntl.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <sys/stat.h>
  26. //
  27. // ---------------------------------------------------------------- Definitions
  28. //
  29. #define TEMPORARY_FILE_RANDOM_CHARACTERS 5
  30. #define TEMPORARY_FILE_LONG_RANDOM_CHARACTERS 8
  31. //
  32. // Define the number of times the mktemp functions will try to create a unique
  33. // file name before giving up.
  34. //
  35. #define MKTEMP_TRY_COUNT MAX_ULONG
  36. //
  37. // Define the permissions on temporary files.
  38. //
  39. #define TEMPORARY_FILE_PERMISSIONS (S_IRUSR | S_IWUSR)
  40. //
  41. // Define the permissions on a temporary directory.
  42. //
  43. #define TEMPORARY_DIRECTORY_PERMISSIONS (S_IRUSR | S_IWUSR | S_IXUSR)
  44. //
  45. // Define the default temporary file name prefix.
  46. //
  47. #define TEMPORARY_FILE_PREFIX "tmp"
  48. //
  49. // ------------------------------------------------------ Data Type Definitions
  50. //
  51. //
  52. // ----------------------------------------------- Internal Function Prototypes
  53. //
  54. VOID
  55. ClpCreateRandomString (
  56. PSTR String,
  57. ULONG CharacterCount
  58. );
  59. //
  60. // -------------------------------------------------------------------- Globals
  61. //
  62. //
  63. // Store the global temporary name buffer.
  64. //
  65. char *ClTemporaryNameBuffer;
  66. //
  67. // Store the temporary name seed.
  68. //
  69. unsigned ClTemporaryNameSeed;
  70. //
  71. // ------------------------------------------------------------------ Functions
  72. //
  73. LIBC_API
  74. char *
  75. tmpnam (
  76. char *Buffer
  77. )
  78. /*++
  79. Routine Description:
  80. This routine generates a string that is a valid filename and is not the
  81. name of an existing file. This routine returns a different name each time
  82. it is called. Note that between the time the name is returned and when an
  83. application goes to create the file, the file may already be created.
  84. Applications may find the tmpfile function more robust and useful.
  85. Arguments:
  86. Buffer - Supplies an optional pointer to a buffer where the name will be
  87. returned. This buffer is assumed to be at least L_tmpnam bytes large.
  88. If this buffer is not supplied, then a pointer to a global buffer will
  89. be returned. Subsequent calls to this routine will overwrite the
  90. contents of that returned buffer.
  91. Return Value:
  92. Returns a pointer to a string containing the name of a temporary file. This
  93. returns the buffer if it is supplied, or a pointer to a global buffer
  94. otherwise.
  95. --*/
  96. {
  97. int OriginalError;
  98. char RandomBuffer[TEMPORARY_FILE_RANDOM_CHARACTERS + 1];
  99. int Result;
  100. struct stat Stat;
  101. ULONG Try;
  102. if (Buffer == NULL) {
  103. if (ClTemporaryNameBuffer == NULL) {
  104. ClTemporaryNameBuffer = malloc(L_tmpnam);
  105. if (ClTemporaryNameBuffer == NULL) {
  106. return NULL;
  107. }
  108. }
  109. Buffer = ClTemporaryNameBuffer;
  110. }
  111. //
  112. // Loop creating random names as long as they exist.
  113. //
  114. for (Try = 0; Try < MKTEMP_TRY_COUNT; Try += 1) {
  115. ClpCreateRandomString(RandomBuffer, sizeof(RandomBuffer) - 1);
  116. RandomBuffer[sizeof(RandomBuffer) - 1] = '\0';
  117. snprintf(Buffer, L_tmpnam, TEMPORARY_FILE_PREFIX "%s", RandomBuffer);
  118. OriginalError = errno;
  119. Result = stat(Buffer, &Stat);
  120. errno = OriginalError;
  121. if (Result != 0) {
  122. break;
  123. }
  124. }
  125. return Buffer;
  126. }
  127. LIBC_API
  128. char *
  129. tempnam (
  130. const char *Directory,
  131. const char *Prefix
  132. )
  133. /*++
  134. Routine Description:
  135. This routine generates path name that may be used for a temporary file.
  136. Arguments:
  137. Directory - Supplies an optional pointer to a string containing the name of
  138. the directory in which the temporary file is to be created. If the
  139. directory is not supplied or is not an appropriate directory, then the
  140. path prefix defined as P_tmpdir in stdio.h shall be used.
  141. Prefix - Supplies a pointer to a string containing up to a five character
  142. prefix on the temporary file name.
  143. Return Value:
  144. Returns a pointer to a string containing the name of a temporary file. The
  145. caller must call free when finished with this buffer to reclaim the memory
  146. allocated by this routine.
  147. NULL on failure, and errno will be set to contain more information.
  148. --*/
  149. {
  150. PSTR Buffer;
  151. size_t Length;
  152. int OriginalError;
  153. size_t RandomPartOffset;
  154. int Result;
  155. struct stat Stat;
  156. ULONG Try;
  157. //
  158. // Figure out the directory to use.
  159. //
  160. OriginalError = errno;
  161. if (Directory == NULL) {
  162. Directory = P_tmpdir;
  163. } else {
  164. Result = stat(Directory, &Stat);
  165. if ((Result != 0) || (!S_ISDIR(Stat.st_mode))) {
  166. Directory = P_tmpdir;
  167. }
  168. }
  169. //
  170. // Figure out the length of the final string, and allocate a buffer for it.
  171. // The form will be <directory>/<prefix><Random>.
  172. //
  173. if (Prefix == NULL) {
  174. Prefix = TEMPORARY_FILE_PREFIX;
  175. }
  176. RandomPartOffset = strlen(Directory) + 1 + strlen(Prefix);
  177. Length = RandomPartOffset + TEMPORARY_FILE_LONG_RANDOM_CHARACTERS + 1;
  178. Buffer = malloc(Length);
  179. if (Buffer == NULL) {
  180. return NULL;
  181. }
  182. for (Try = 0; Try < MKTEMP_TRY_COUNT; Try += 1) {
  183. snprintf(Buffer, Length, "%s/%s", Directory, Prefix);
  184. ClpCreateRandomString(Buffer + RandomPartOffset,
  185. TEMPORARY_FILE_LONG_RANDOM_CHARACTERS);
  186. Buffer[Length - 1] = '\0';
  187. Result = stat(Buffer, &Stat);
  188. if (Result != 0) {
  189. break;
  190. }
  191. }
  192. //
  193. // Restore the errno variable.
  194. //
  195. errno = OriginalError;
  196. return Buffer;
  197. }
  198. LIBC_API
  199. FILE *
  200. tmpfile (
  201. void
  202. )
  203. /*++
  204. Routine Description:
  205. This routine creates a file and opens a corresponding stream. The file
  206. shall be automatically deleted when all references to the file are closed.
  207. The file is opened as in fopen for update ("w+").
  208. Arguments:
  209. None.
  210. Return Value:
  211. Returns an open file stream on success.
  212. NULL if a temporary file could not be created.
  213. --*/
  214. {
  215. PSTR Buffer;
  216. int Descriptor;
  217. PSTR Directory;
  218. FILE *File;
  219. size_t Length;
  220. int OriginalError;
  221. PSTR Prefix;
  222. size_t RandomPartOffset;
  223. ULONG Try;
  224. File = NULL;
  225. OriginalError = errno;
  226. Directory = P_tmpdir;
  227. Prefix = TEMPORARY_FILE_PREFIX;
  228. //
  229. // Allocate a buffer to hold the temporary file name.
  230. //
  231. RandomPartOffset = strlen(Directory) + 1 + strlen(Prefix);
  232. Length = RandomPartOffset + TEMPORARY_FILE_LONG_RANDOM_CHARACTERS + 1;
  233. Buffer = malloc(Length);
  234. if (Buffer == NULL) {
  235. return NULL;
  236. }
  237. //
  238. // Loop creating random names and trying to exclusively create them.
  239. //
  240. for (Try = 0; Try < MKTEMP_TRY_COUNT; Try += 1) {
  241. snprintf(Buffer, Length, "%s/%s", Directory, Prefix);
  242. ClpCreateRandomString(Buffer + RandomPartOffset,
  243. TEMPORARY_FILE_LONG_RANDOM_CHARACTERS);
  244. Buffer[Length - 1] = '\0';
  245. //
  246. // Try to exclusively create the file. If that works, open a stream too.
  247. //
  248. Descriptor = open(Buffer, O_CREAT | O_EXCL, TEMPORARY_FILE_PERMISSIONS);
  249. if (Descriptor >= 0) {
  250. File = fdopen(Descriptor, "w+");
  251. if (File != NULL) {
  252. //
  253. // Unlink the file so that it is deleted whenever the file is
  254. // closed.
  255. //
  256. unlink(Buffer);
  257. break;
  258. }
  259. close(Descriptor);
  260. //
  261. // The file opened but not the stream. Stop, as something is going
  262. // on here like a low memory condition.
  263. //
  264. break;
  265. }
  266. //
  267. // Also stop if the error is anything other than some standard errors.
  268. //
  269. if ((errno != EEXIST) && (errno != EPERM) && (errno != EACCES)) {
  270. break;
  271. }
  272. }
  273. free(Buffer);
  274. //
  275. // Restore the errno variable.
  276. //
  277. errno = OriginalError;
  278. return File;
  279. }
  280. LIBC_API
  281. char *
  282. mktemp (
  283. char *Template
  284. )
  285. /*++
  286. Routine Description:
  287. This routine creates replaces the contents of the given string by a unique
  288. filename.
  289. Arguments:
  290. Template - Supplies a pointer to a template string that will be modified
  291. in place. The string must end in six X characters. Each X character
  292. will be replaced by a random valid filename character.
  293. Return Value:
  294. Returns a pointer to the template string.
  295. --*/
  296. {
  297. size_t Length;
  298. int OriginalError;
  299. int Result;
  300. struct stat Stat;
  301. ULONG Try;
  302. //
  303. // Ensure the string ends in six X characters.
  304. //
  305. Length = strlen(Template);
  306. if ((Length < 6) || (strcmp(Template + Length - 6, "XXXXXX") != 0)) {
  307. errno = EINVAL;
  308. return NULL;
  309. }
  310. OriginalError = errno;
  311. errno = 0;
  312. for (Try = 0; Try < MKTEMP_TRY_COUNT; Try += 1) {
  313. ClpCreateRandomString(Template + Length - 6, 6);
  314. Result = stat(Template, &Stat);
  315. if (Result != 0) {
  316. break;
  317. }
  318. }
  319. //
  320. // If the error is "no such file", that's a good thing, it means the
  321. // string is available.
  322. //
  323. if (errno == ENOENT) {
  324. errno = OriginalError;
  325. //
  326. // Anything else is a failure.
  327. //
  328. } else {
  329. Template = NULL;
  330. }
  331. return Template;
  332. }
  333. LIBC_API
  334. char *
  335. mkdtemp (
  336. char *Template
  337. )
  338. /*++
  339. Routine Description:
  340. This routine creates replaces the contents of the given string by a unique
  341. directory name, and attempts to create that directory.
  342. Arguments:
  343. Template - Supplies a pointer to a template string that will be modified
  344. in place. The string must end in six X characters. Each X character
  345. will be replaced by a random valid filename character.
  346. Return Value:
  347. Returns a pointer to the template string.
  348. --*/
  349. {
  350. size_t Length;
  351. int OriginalError;
  352. int Result;
  353. ULONG Try;
  354. //
  355. // Ensure the string ends in six X characters.
  356. //
  357. Length = strlen(Template);
  358. if ((Length < 6) || (strcmp(Template + Length - 6, "XXXXXX") != 0)) {
  359. errno = EINVAL;
  360. return NULL;
  361. }
  362. OriginalError = errno;
  363. errno = 0;
  364. Result = -1;
  365. for (Try = 0; Try < MKTEMP_TRY_COUNT; Try += 1) {
  366. ClpCreateRandomString(Template + Length - 6, 6);
  367. Result = mkdir(Template, TEMPORARY_DIRECTORY_PERMISSIONS);
  368. if (Result == 0) {
  369. break;
  370. }
  371. if (errno != EEXIST) {
  372. break;
  373. }
  374. }
  375. if (Result == 0) {
  376. errno = OriginalError;
  377. } else {
  378. Template = NULL;
  379. }
  380. return Template;
  381. }
  382. LIBC_API
  383. int
  384. mkstemp (
  385. char *Template
  386. )
  387. /*++
  388. Routine Description:
  389. This routine creates replaces the contents of the given string by a unique
  390. filename, and returns an open file descriptor to that file.
  391. Arguments:
  392. Template - Supplies a pointer to a template string that will be modified
  393. in place. The string must end in six X characters. Each X character
  394. will be replaced by a random valid filename character.
  395. Return Value:
  396. Returns the open file descriptor to the newly created file on success.
  397. -1 on failure, and errno will be set to contain more information.
  398. --*/
  399. {
  400. size_t Length;
  401. int OriginalError;
  402. int Result;
  403. ULONG Try;
  404. //
  405. // Ensure the string ends in six X characters.
  406. //
  407. Length = strlen(Template);
  408. if ((Length < 6) || (strcmp(Template + Length - 6, "XXXXXX") != 0)) {
  409. errno = EINVAL;
  410. return -1;
  411. }
  412. Result = -1;
  413. OriginalError = errno;
  414. errno = 0;
  415. for (Try = 0; Try < MKTEMP_TRY_COUNT; Try += 1) {
  416. ClpCreateRandomString(Template + Length - 6, 6);
  417. Result = open(Template,
  418. O_RDWR | O_CREAT | O_EXCL,
  419. TEMPORARY_FILE_PERMISSIONS);
  420. if (Result >= 0) {
  421. break;
  422. }
  423. if ((errno != EEXIST) && (errno != EISDIR)) {
  424. break;
  425. }
  426. }
  427. if (Result >= 0) {
  428. errno = OriginalError;
  429. }
  430. return Result;
  431. }
  432. //
  433. // --------------------------------------------------------- Internal Functions
  434. //
  435. VOID
  436. ClpCreateRandomString (
  437. PSTR String,
  438. ULONG CharacterCount
  439. )
  440. /*++
  441. Routine Description:
  442. This routine creates random ASCII characters in the range of 0-9 and A-Z.
  443. Arguments:
  444. String - Supplies a pointer where the random characters will be returned.
  445. This buffer will NOT be null terminated by this function.
  446. CharacterCount - Supplies the number of random characters to generate.
  447. Return Value:
  448. None.
  449. --*/
  450. {
  451. ULONG RandomIndex;
  452. INT Value;
  453. if (ClTemporaryNameSeed == 0) {
  454. ClTemporaryNameSeed = time(NULL) ^ getpid();
  455. }
  456. for (RandomIndex = 0; RandomIndex < CharacterCount; RandomIndex += 1) {
  457. //
  458. // Create a random value using letters and numbers. Avoid relying
  459. // on case sensitivity just in case. For reference,
  460. // 36^5 is 60.4 million.
  461. //
  462. Value = rand_r(&ClTemporaryNameSeed) % 36;
  463. if (Value >= 10) {
  464. Value += 'A' - 10;
  465. } else {
  466. Value += '0';
  467. }
  468. String[RandomIndex] = Value;
  469. }
  470. return;
  471. }