134
svn_stream_set_data_available(svn_stream_t *stream,
135
svn_stream_data_available_fn_t data_available_fn)
137
stream->data_available_fn = data_available_fn;
134
141
svn_stream__set_is_buffered(svn_stream_t *stream,
135
142
svn_stream__is_buffered_fn_t is_buffered_fn)
137
144
stream->is_buffered_fn = is_buffered_fn;
147
/* Standard implementation for svn_stream_read_full() based on
148
multiple svn_stream_read2() calls (in separate function to make
149
it more likely for svn_stream_read_full to be inlined) */
151
full_read_fallback(svn_stream_t *stream, char *buffer, apr_size_t *len)
153
apr_size_t remaining = *len;
154
while (remaining > 0)
156
apr_size_t length = remaining;
157
SVN_ERR(svn_stream_read2(stream, buffer, &length));
173
svn_stream_supports_partial_read(svn_stream_t *stream)
175
return stream->read_fn != NULL;
141
svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len)
179
svn_stream_read2(svn_stream_t *stream, char *buffer, apr_size_t *len)
143
SVN_ERR_ASSERT(stream->read_fn != NULL);
181
if (stream->read_fn == NULL)
182
return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
144
184
return svn_error_trace(stream->read_fn(stream->baton, buffer, len));
188
svn_stream_read_full(svn_stream_t *stream, char *buffer, apr_size_t *len)
190
if (stream->read_full_fn == NULL)
191
return svn_error_trace(full_read_fallback(stream, buffer, len));
193
return svn_error_trace(stream->read_full_fn(stream->baton, buffer, len));
149
197
svn_stream_skip(svn_stream_t *stream, apr_size_t len)
151
199
if (stream->skip_fn == NULL)
152
200
return svn_error_trace(
153
skip_default_handler(stream->baton, len, stream->read_fn));
201
skip_default_handler(stream->baton, len, stream->read_full_fn));
155
203
return svn_error_trace(stream->skip_fn(stream->baton, len));
1279
1430
baton->pool = pool;
1281
1432
s = svn_stream_create(baton, pool);
1282
svn_stream_set_read(s, read_handler_checksum);
1433
svn_stream_set_read2(s, read_handler_checksum, read_full_handler_checksum);
1283
1434
svn_stream_set_write(s, write_handler_checksum);
1435
svn_stream_set_data_available(s, data_available_handler_checksum);
1284
1436
svn_stream_set_close(s, close_handler_checksum);
1288
struct md5_stream_baton
1290
const unsigned char **read_digest;
1291
const unsigned char **write_digest;
1292
svn_checksum_t *read_checksum;
1293
svn_checksum_t *write_checksum;
1294
svn_stream_t *proxy;
1298
static svn_error_t *
1299
read_handler_md5(void *baton, char *buffer, apr_size_t *len)
1301
struct md5_stream_baton *btn = baton;
1302
return svn_error_trace(svn_stream_read(btn->proxy, buffer, len));
1305
static svn_error_t *
1306
skip_handler_md5(void *baton, apr_size_t len)
1308
struct md5_stream_baton *btn = baton;
1309
return svn_error_trace(svn_stream_skip(btn->proxy, len));
1312
static svn_error_t *
1313
write_handler_md5(void *baton, const char *buffer, apr_size_t *len)
1315
struct md5_stream_baton *btn = baton;
1316
return svn_error_trace(svn_stream_write(btn->proxy, buffer, len));
1319
static svn_error_t *
1320
close_handler_md5(void *baton)
1322
struct md5_stream_baton *btn = baton;
1324
SVN_ERR(svn_stream_close(btn->proxy));
1326
if (btn->read_digest)
1328
= apr_pmemdup(btn->pool, btn->read_checksum->digest,
1329
APR_MD5_DIGESTSIZE);
1331
if (btn->write_digest)
1333
= apr_pmemdup(btn->pool, btn->write_checksum->digest,
1334
APR_MD5_DIGESTSIZE);
1336
return SVN_NO_ERROR;
1341
svn_stream_checksummed(svn_stream_t *stream,
1342
const unsigned char **read_digest,
1343
const unsigned char **write_digest,
1344
svn_boolean_t read_all,
1348
struct md5_stream_baton *baton;
1350
if (! read_digest && ! write_digest)
1353
baton = apr_palloc(pool, sizeof(*baton));
1354
baton->read_digest = read_digest;
1355
baton->write_digest = write_digest;
1358
/* Set BATON->proxy to a stream that will fill in BATON->read_checksum
1359
* and BATON->write_checksum (if we want them) when it is closed. */
1361
= svn_stream_checksummed2(stream,
1362
read_digest ? &baton->read_checksum : NULL,
1363
write_digest ? &baton->write_checksum : NULL,
1367
/* Create a stream that will forward its read/write/close operations to
1368
* BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we
1369
* want them) after it closes BATON->proxy. */
1370
s = svn_stream_create(baton, pool);
1371
svn_stream_set_read(s, read_handler_md5);
1372
svn_stream_set_skip(s, skip_handler_md5);
1373
svn_stream_set_write(s, write_handler_md5);
1374
svn_stream_set_close(s, close_handler_md5);
1381
1440
/* Miscellaneous stream functions. */
1443
svn_stringbuf_from_stream(svn_stringbuf_t **str,
1444
svn_stream_t *stream,
1445
apr_size_t len_hint,
1446
apr_pool_t *result_pool)
1448
#define MIN_READ_SIZE 64
1450
apr_size_t to_read = 0;
1451
svn_stringbuf_t *text
1452
= svn_stringbuf_create_ensure(len_hint ? len_hint : MIN_READ_SIZE,
1457
to_read = text->blocksize - 1 - text->len;
1458
SVN_ERR(svn_stream_read_full(stream, text->data + text->len, &to_read));
1459
text->len += to_read;
1461
if (to_read && text->blocksize < text->len + MIN_READ_SIZE)
1462
svn_stringbuf_ensure(text, text->blocksize * 2);
1466
text->data[text->len] = '\0';
1469
return SVN_NO_ERROR;
1382
1472
struct stringbuf_stream_baton
1384
1474
svn_stringbuf_t *str;
1823
1960
lob->open_on_close = open_on_close;
1825
1962
stream = svn_stream_create(lob, result_pool);
1826
svn_stream_set_read(stream, read_handler_lazyopen);
1963
svn_stream_set_read2(stream, read_handler_lazyopen,
1964
read_full_handler_lazyopen);
1827
1965
svn_stream_set_skip(stream, skip_handler_lazyopen);
1828
1966
svn_stream_set_write(stream, write_handler_lazyopen);
1829
1967
svn_stream_set_close(stream, close_handler_lazyopen);
1830
1968
svn_stream_set_mark(stream, mark_handler_lazyopen);
1831
1969
svn_stream_set_seek(stream, seek_handler_lazyopen);
1970
svn_stream_set_data_available(stream, data_available_handler_lazyopen);
1832
1971
svn_stream__set_is_buffered(stream, is_buffered_lazyopen);
1976
/* Baton for install streams */
1977
struct install_baton_t
1979
struct baton_apr baton_apr;
1980
const char *tmp_path;
1985
#if _WIN32_WINNT < 0x600 /* Does the SDK assume Windows Vista+? */
1986
typedef struct _FILE_RENAME_INFO {
1987
BOOL ReplaceIfExists;
1988
HANDLE RootDirectory;
1989
DWORD FileNameLength;
1991
} FILE_RENAME_INFO, *PFILE_RENAME_INFO;
1993
typedef struct _FILE_DISPOSITION_INFO {
1995
} FILE_DISPOSITION_INFO, *PFILE_DISPOSITION_INFO;
1997
#define FileRenameInfo 3
1998
#define FileDispositionInfo 4
2000
typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile,
2001
int FileInformationClass,
2002
LPVOID lpFileInformation,
2003
DWORD dwBufferSize);
2005
static volatile SetFileInformationByHandle_t SetFileInformationByHandle_p = 0;
2006
#define SetFileInformationByHandle (*SetFileInformationByHandle_p)
2008
static volatile svn_atomic_t SetFileInformationByHandle_a = 0;
2011
static svn_error_t *
2012
find_SetFileInformationByHandle(void *baton, apr_pool_t *scratch_pool)
2014
HMODULE kernel32 = GetModuleHandle("Kernel32.dll");
2018
SetFileInformationByHandle_p =
2019
(SetFileInformationByHandle_t)
2020
GetProcAddress(kernel32, "SetFileInformationByHandle");
2023
return SVN_NO_ERROR;
2025
#endif /* WIN32 < Vista */
2027
/* Create and open a tempfile in DIRECTORY. Return its handle and path */
2028
static svn_error_t *
2029
create_tempfile(HANDLE *hFile,
2030
const char **file_path,
2031
const char *directory,
2032
apr_pool_t *result_pool,
2033
apr_pool_t *scratch_pool)
2035
const char *unique_name;
2036
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2037
static svn_atomic_t tempname_counter;
2038
int baseNr = (GetTickCount() << 11) + 13 * svn_atomic_inc(&tempname_counter)
2039
+ GetCurrentProcessId();
2043
/* Shares common idea with io.c's temp_file_create */
2047
apr_uint32_t unique_nr;
2050
/* Generate a number that should be unique for this application and
2051
usually for the entire computer to reduce the number of cycles
2052
through this loop. (A bit of calculation is much cheaper than
2054
unique_nr = baseNr + 7 * i++;
2057
svn_pool_clear(iterpool);
2058
unique_name = svn_dirent_join(directory,
2059
apr_psprintf(iterpool, "svn-%X",
2063
SVN_ERR(svn_io__utf8_to_unicode_longpath(&w_name, unique_name,
2066
/* Create a completely not-sharable file to avoid indexers, and other
2067
filesystem watchers locking the file while we are still writing.
2069
We need DELETE privileges to move the file. */
2070
h = CreateFileW(w_name, GENERIC_WRITE | DELETE, 0 /* share */,
2071
NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
2073
if (h == INVALID_HANDLE_VALUE)
2075
apr_status_t status = apr_get_os_error();
2077
return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
2078
svn_error_wrap_apr(status, NULL),
2079
_("Unable to make name in '%s'"),
2080
svn_dirent_local_style(directory, scratch_pool));
2082
if (!APR_STATUS_IS_EEXIST(status) && !APR_STATUS_IS_EACCES(status))
2083
return svn_error_wrap_apr(status, NULL);
2086
while (h == INVALID_HANDLE_VALUE);
2089
*file_path = apr_pstrdup(result_pool, unique_name);
2090
svn_pool_destroy(iterpool);
2092
return SVN_NO_ERROR;
2095
/* Implements svn_close_fn_t */
2096
static svn_error_t *
2097
install_close(void *baton)
2099
struct install_baton_t *ib = baton;
2101
/* Flush the data cached in APR, but don't close the file yet */
2102
SVN_ERR(svn_io_file_flush(ib->baton_apr.file, ib->baton_apr.pool));
2104
return SVN_NO_ERROR;
2110
svn_stream__create_for_install(svn_stream_t **install_stream,
2111
const char *tmp_abspath,
2112
apr_pool_t *result_pool,
2113
apr_pool_t *scratch_pool)
2116
struct install_baton_t *ib;
2117
const char *tmp_path;
2121
apr_status_t status;
2123
SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath));
2125
SVN_ERR(create_tempfile(&hInstall, &tmp_path, tmp_abspath,
2126
scratch_pool, scratch_pool));
2128
/* Wrap as a standard APR file to allow sharing implementation.
2130
But do note that some file functions (such as retrieving the name)
2131
don't work on this wrapper. */
2132
/* ### Buffered, or not? */
2133
status = apr_os_file_put(&file, &hInstall,
2134
APR_WRITE | APR_BINARY | APR_BUFFERED,
2139
CloseHandle(hInstall);
2140
return svn_error_wrap_apr(status, NULL);
2143
tmp_path = svn_dirent_internal_style(tmp_path, result_pool);
2146
SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath));
2148
SVN_ERR(svn_io_open_unique_file3(&file, &tmp_path, tmp_abspath,
2149
svn_io_file_del_none,
2150
result_pool, scratch_pool));
2152
*install_stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
2154
ib = apr_pcalloc(result_pool, sizeof(*ib));
2155
ib->baton_apr = *(struct baton_apr*)(*install_stream)->baton;
2157
assert((void*)&ib->baton_apr == (void*)ib); /* baton pointer is the same */
2159
(*install_stream)->baton = ib;
2161
ib->tmp_path = tmp_path;
2164
/* Don't close the file on stream close; flush instead */
2165
svn_stream_set_close(*install_stream, install_close);
2167
/* ### Install pool cleanup handler for tempfile? */
2170
return SVN_NO_ERROR;
2174
svn_stream__install_stream(svn_stream_t *install_stream,
2175
const char *final_abspath,
2176
svn_boolean_t make_parents,
2177
apr_pool_t *scratch_pool)
2179
struct install_baton_t *ib = install_stream->baton;
2182
SVN_ERR_ASSERT(svn_dirent_is_absolute(final_abspath));
2185
#if _WIN32_WINNT < 0x600
2186
SVN_ERR(svn_atomic__init_once(&SetFileInformationByHandle_a,
2187
find_SetFileInformationByHandle,
2188
NULL, scratch_pool));
2190
if (!SetFileInformationByHandle_p)
2191
SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2193
#endif /* WIN32 < Windows Vista */
2195
WCHAR *w_final_abspath;
2198
FILE_RENAME_INFO *rename_info;
2201
apr_os_file_get(&hFile, ib->baton_apr.file);
2203
SVN_ERR(svn_io__utf8_to_unicode_longpath(&w_final_abspath,
2204
svn_dirent_local_style(
2208
path_len = wcslen(w_final_abspath);
2209
rename_size = sizeof(*rename_info) + sizeof(WCHAR) * path_len;
2211
/* The rename info struct doesn't need hacks for long paths,
2212
so no ugly escaping calls here */
2213
rename_info = apr_pcalloc(scratch_pool, rename_size);
2214
rename_info->ReplaceIfExists = TRUE;
2215
rename_info->FileNameLength = path_len;
2216
memcpy(rename_info->FileName, w_final_abspath, path_len * sizeof(WCHAR));
2218
if (!SetFileInformationByHandle(hFile, FileRenameInfo, rename_info,
2221
svn_boolean_t retry = FALSE;
2222
err = svn_error_wrap_apr(apr_get_os_error(), NULL);
2224
/* ### rhuijben: I wouldn't be surprised if we later find out that we
2225
have to fall back to close+rename on some specific
2226
error values here, to support some non standard NAS
2227
and filesystem scenarios. */
2229
if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err))
2233
err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath,
2238
return svn_error_trace(svn_error_compose_create(err, err2));
2240
svn_error_clear(err);
2245
else if (err && (APR_STATUS_IS_EACCES(err->apr_err)
2246
|| APR_STATUS_IS_EEXIST(err->apr_err)))
2248
svn_error_clear(err);
2252
/* Set the destination file writable because Windows will not allow
2253
us to rename when final_abspath is read-only. */
2254
SVN_ERR(svn_io_set_file_read_write(final_abspath, TRUE,
2260
if (!SetFileInformationByHandle(hFile, FileRenameInfo,
2261
rename_info, rename_size))
2263
err = svn_error_wrap_apr(
2265
_("Can't move '%s' to '%s'"),
2266
svn_dirent_local_style(ib->tmp_path,
2268
svn_dirent_local_style(final_abspath,
2276
return svn_error_compose_create(err,
2277
svn_io_file_close(ib->baton_apr.file,
2282
err = svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool);
2284
/* A missing directory is too common to not cover here. */
2285
if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err))
2289
err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath,
2294
/* Creating directory didn't work: Return all errors */
2295
return svn_error_trace(svn_error_compose_create(err, err2));
2297
/* We could create a directory: retry install */
2298
svn_error_clear(err);
2300
SVN_ERR(svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool));
2305
return SVN_NO_ERROR;
2309
svn_stream__install_get_info(apr_finfo_t *finfo,
2310
svn_stream_t *install_stream,
2312
apr_pool_t *scratch_pool)
2314
struct install_baton_t *ib = install_stream->baton;
2317
/* On WIN32 the file is still open, so we can obtain the information
2318
from the handle without race conditions */
2319
apr_status_t status;
2321
status = apr_file_info_get(finfo, wanted, ib->baton_apr.file);
2324
return svn_error_wrap_apr(status, NULL);
2326
SVN_ERR(svn_io_stat(finfo, ib->tmp_path, wanted, scratch_pool));
2329
return SVN_NO_ERROR;
2333
svn_stream__install_delete(svn_stream_t *install_stream,
2334
apr_pool_t *scratch_pool)
2336
struct install_baton_t *ib = install_stream->baton;
2341
#if _WIN32_WINNT < 0x600
2343
SVN_ERR(svn_atomic__init_once(&SetFileInformationByHandle_a,
2344
find_SetFileInformationByHandle,
2345
NULL, scratch_pool));
2347
if (!SetFileInformationByHandle_p)
2350
#endif /* WIN32 < Windows Vista */
2352
FILE_DISPOSITION_INFO disposition_info;
2355
apr_os_file_get(&hFile, ib->baton_apr.file);
2357
disposition_info.DeleteFile = TRUE;
2359
/* Mark the file as delete on close to avoid having to reopen
2360
the file as part of the delete handling. */
2361
done = SetFileInformationByHandle(hFile, FileDispositionInfo,
2363
sizeof(disposition_info));
2366
SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2369
return SVN_NO_ERROR; /* File is already gone */
2372
return svn_error_trace(svn_io_remove_file2(ib->tmp_path, FALSE,