monotone

monotone Mtn Source Tree

Root/src/mkstemp.cc

1// Copyright (C) 2000 Red Hat, Inc.
2// 2004 Graydon Hoare <graydon@pobox.com>
3// 2009 Zack Weinberg <zackw@panix.com>
4//
5// This program is made available under the GNU GPL version 2.0 or
6// greater. See the accompanying file COPYING for details.
7//
8// This program is distributed WITHOUT ANY WARRANTY; without even the
9// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10// PURPOSE.
11
12// this code was originally based on part of gfileutils.cc in glib.
13//
14// this is a portable implementation of mkstemp, which is not available on
15// all systems and not always correctly implemented even when available.
16//
17// as with other mkstemp implementations, caller is expected to put the
18// string "XXXXXX" somewhere in the template, which will be replaced with a
19// random string. it does not need to be at the end. unlike the usual
20// mkstemp, the return value is just a boolean success/failure condition,
21// not a file descriptor; however, the file *has* been created. only 100
22// filenames are tried before we give up.
23//
24// we use only uppercase letters and digits in the random string, to avoid
25// all problems with case (in)sensitivity and magic characters (filesystem
26// or shell). the letters I, L, O, X are not used; I, L, and O are often
27// easily confused with the digits 1 or 0, and excluding X ensures that all
28// six of the placeholder characters will be modified when the function
29// returns, which makes it easier to test. this also arranges for there to
30// be 32 possible characters in the random string, which means there are
31// exactly 2^30 possible random strings.
32//
33// security notes: because we use open() correctly, the worst thing an
34// attacker can do to us is predict all 100 candidate filenames and cause
35// the function to fail. it is thus desirable for the attacker not to be
36// able to deduce the state of the RNG from the output string. however, the
37// function is not used very often and the runtime cost of a fully seeded,
38// cryptographically secure PRNG is very high. we're not about to drop the
39// mersenne twister in here, either, but we don't want something so shabby
40// as a 32-bit LCG, which is easily vulnerable to attack as used here (for
41// any output string there would be only four possible internal states).
42//
43// a reasonable choice is Pierre L'Ecuyer's "maximally equidistributed
44// combined LFSR (Tausworthe) generator" as described at
45// http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme2.ps -- this has
46// 128 bits of state and is nice and short.
47//
48// we don't use the system rand() because we don't know if it has been
49// seeded, we don't know if anyone else in the application is using it,
50// and it can be extremely poor quality (RANDU, anyone?)
51
52#include "base.hh"
53#include "file_io.hh"
54#include "numeric_vocab.hh"
55
56#include <errno.h>
57#include <string.h>
58#include <time.h>
59
60#include <sys/types.h>
61#include <sys/stat.h>
62#include <fcntl.h>
63#include <unistd.h>
64
65#ifdef _MSC_VER // bleh, is this really necessary?
66 #undef open
67 #define open(p, f, m) _open(p, f, m)
68 #undef close
69 #define close(f) _close(f)
70 #undef O_RDWR
71 #define O_RDWR _O_RDWR
72 #undef O_CREAT
73 #define O_CREAT _O_CREAT
74 #undef O_EXCL
75 #define O_EXCL _O_EXCL
76 #undef O_BINARY
77 #define O_BINARY _O_BINARY
78#endif
79
80#ifndef O_BINARY
81 #define O_BINARY 0
82#endif
83
84using std::string;
85
86// RNG implementation: this code is taken from figure 1 of the paper cited
87// above, with revision for clarity.
88
89static u32 z1, z2, z3, z4;
90
91static u32
92lfsr113()
93{
94 z1 = ((z1 & 0xfffffffeu) << 18) ^ (((z1 << 6) ^ z1) >> 13);
95 z2 = ((z2 & 0xfffffff8u) << 2) ^ (((z2 << 2) ^ z2) >> 27);
96 z3 = ((z3 & 0xfffffff0u) << 7) ^ (((z3 << 13) ^ z3) >> 21);
97 z4 = ((z4 & 0xffffff80u) << 13) ^ (((z4 << 3) ^ z4) >> 12);
98 return z1 ^ z2 ^ z3 ^ z4;
99}
100
101// RNG implementation: i made this up based on the advice in the paper cited
102// above: "before calling lfsr113 for the first time, the variables z1, z2,
103// z3, and z4 must be initialized to any (random) integers larger than 1, 7,
104// 15, and 127, respectively." the current time is the traditional source
105// of true indeterminacy for this purpose. note that we perturb the values
106// already there rather than replacing them -- this is so successive calls
107// to monotone_mkstemp() within the system time granularity do not produce
108// the same sequence. the constants were chosen using a different PRNG.
109
110static void
111seed_lfsr113()
112{
113 u32 b = time(0);
114 z1 += b ^ 2421089565u;
115 z2 += b ^ 3453830001u;
116 z3 += b ^ 1437919543u;
117 z4 += b ^ 1406684125u;
118
119 if (z1 < 1) z1 += 1;
120 if (z2 < 7) z2 += 7;
121 if (z3 < 15) z3 += 15;
122 if (z4 < 127) z4 += 127;
123}
124
125bool
126monotone_mkstemp(string & tmpl)
127{
128 static const char letters[] = "0123456789ABCDEFGHJKMNPQRSTUVWYZ";
129 static const int NLETTERS = sizeof (letters) - 1;
130
131 typedef string::size_type position;
132
133 position len = tmpl.length();
134 if (len < 6)
135 return false;
136
137 position xes = tmpl.find("XXXXXX");
138 if (xes == string::npos)
139 return false;
140
141 std::vector<char> buf(len + 1);
142 memcpy(&buf[0], tmpl.data(), len);
143 buf[len] = 0;
144
145 seed_lfsr113();
146
147 // if we can't find a name in 100 tries there's probably a problem
148 // requiring user intervention.
149 for (int count = 0; count < 100; ++count)
150 {
151 u32 x = lfsr113();
152 for (int i = 0; i < 6; i++)
153 {
154 buf[xes + i] = letters[x % NLETTERS];
155 x /= NLETTERS;
156 }
157
158 int fd = open(&buf[0], O_RDWR|O_CREAT|O_EXCL|O_BINARY, 0600);
159 if (fd >= 0)
160 {
161 close(fd);
162 tmpl.replace(xes, 6, &buf[xes], 6);
163 return true;
164 }
165 else if (errno != EEXIST)
166 break;
167 }
168 return false;
169}
170
171// Local Variables:
172// mode: C++
173// fill-column: 76
174// c-file-style: "gnu"
175// indent-tabs-mode: nil
176// End:
177// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status