xref: /trunk/main/sal/osl/unx/tempfile.c (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 /*****************************************************************/
29 /* Includes                                                      */
30 /*****************************************************************/
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include "system.h"
38 #include <osl/file.h>
39 #include <osl/thread.h>
40 #include <rtl/ustrbuf.h>
41 #include <osl/diagnose.h>
42 
43 #ifndef _FILE_URL_H_
44 #include "file_url.h"
45 #endif
46 
47 /*****************************************************************/
48 /* osl_getTempFirURL                                             */
49 /*****************************************************************/
50 
51 oslFileError SAL_CALL osl_getTempDirURL( rtl_uString** pustrTempDir )
52 {
53 #ifdef MACOSX
54     const char *pValue = getenv( "TMPDIR" );
55 
56     /* If TMPDIR environment variable is not set, use "/tmp" instead
57        of P_tmpdir because its value is "/var/tmp" and it is not
58        deleted on system start up */
59     if ( !pValue )
60         pValue = "/tmp";
61 #else
62 
63     const char *pValue = getenv( "TEMP" );
64 
65     if ( !pValue )
66 	{
67         pValue = getenv( "TMP" );
68 #if defined(SOLARIS) || defined (LINUX) || defined (FREEBSD)
69 		if ( !pValue )
70 			pValue = P_tmpdir;
71 #endif
72 	}
73 #endif /* MACOSX */
74 
75 	if ( pValue )
76 	{
77 		oslFileError error;
78 		rtl_uString	*ustrTempPath = NULL;
79 
80 		rtl_string2UString( &ustrTempPath, pValue, strlen( pValue ), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
81         OSL_ASSERT(ustrTempPath != NULL);
82 		error = osl_getFileURLFromSystemPath( ustrTempPath, pustrTempDir );
83 		rtl_uString_release( ustrTempPath );
84 
85 		return error;
86 	}
87 	else
88 		return osl_File_E_NOENT;
89 }
90 
91 /******************************************************************
92  * Generates a random unique file name. We're using the scheme
93  * from the standard c-lib function mkstemp to generate a more
94  * or less random unique file name
95  *
96  * @param rand_name
97  *        receives the random name
98  ******************************************************************/
99 
100 static const char LETTERS[]        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
101 static const int  COUNT_OF_LETTERS = sizeof(LETTERS)/sizeof(LETTERS[0]) - 1;
102 
103 #define RAND_NAME_LENGTH 6
104 
105 static void osl_gen_random_name_impl_(rtl_uString** rand_name)
106 {
107 	static uint64_t value;
108 
109 	char     buffer[RAND_NAME_LENGTH];
110 	struct   timeval tv;
111 	uint64_t v;
112 	int      i;
113 
114 	gettimeofday(&tv, NULL);
115 
116 	value += ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
117 
118 	v = value;
119 
120 	for (i = 0; i < RAND_NAME_LENGTH; i++)
121 	{
122 		buffer[i] = LETTERS[v % COUNT_OF_LETTERS];
123 		v        /= COUNT_OF_LETTERS;
124 	}
125 
126 	rtl_string2UString(
127 			rand_name,
128 			buffer,
129 			RAND_NAME_LENGTH,
130 			RTL_TEXTENCODING_ASCII_US,
131 			OSTRING_TO_OUSTRING_CVTFLAGS);
132     OSL_ASSERT(*rand_name != NULL);
133 }
134 
135 /*****************************************************************
136  * Helper function
137  * Either use the directory provided or the result of
138  * osl_getTempDirUrl and return it as system path and file url
139  ****************************************************************/
140 
141 static oslFileError osl_setup_base_directory_impl_(
142 	rtl_uString*  pustrDirectoryURL,
143 	rtl_uString** ppustr_base_dir)
144 {
145 	rtl_uString* dir_url = 0;
146 	rtl_uString* dir     = 0;
147     oslFileError error   = osl_File_E_None;
148 
149 	if (pustrDirectoryURL)
150 		rtl_uString_assign(&dir_url, pustrDirectoryURL);
151 	else
152 		error = osl_getTempDirURL(&dir_url);
153 
154     if (osl_File_E_None == error)
155 	{
156         error = osl_getSystemPathFromFileURL_Ex(dir_url, &dir, FURL_DENY_RELATIVE);
157 		rtl_uString_release(dir_url);
158 	}
159 
160 	if (osl_File_E_None == error)
161 	{
162 		rtl_uString_assign(ppustr_base_dir, dir);
163 		rtl_uString_release(dir);
164 	}
165 
166     return error;
167 }
168 
169 /*****************************************************************
170  * osl_setup_createTempFile_impl
171  * validate input parameter, setup variables
172  ****************************************************************/
173 
174  static oslFileError osl_setup_createTempFile_impl_(
175  	rtl_uString*   pustrDirectoryURL,
176 	oslFileHandle* pHandle,
177 	rtl_uString**  ppustrTempFileURL,
178 	rtl_uString**  ppustr_base_dir,
179 	sal_Bool*	   b_delete_on_close)
180  {
181  	oslFileError osl_error;
182 
183 	OSL_PRECOND(((0 != pHandle) || (0 != ppustrTempFileURL)), "Invalid parameter!");
184 
185 	if ((0 == pHandle) && (0 == ppustrTempFileURL))
186 	{
187 		osl_error = osl_File_E_INVAL;
188 	}
189 	else
190 	{
191 		osl_error = osl_setup_base_directory_impl_(
192 			pustrDirectoryURL, ppustr_base_dir);
193 
194 		*b_delete_on_close = (0 == ppustrTempFileURL);
195 	}
196 
197 	return osl_error;
198  }
199 
200 /*****************************************************************
201  * Create a unique file in the specified directory and return
202  * it's name
203  ****************************************************************/
204 
205 static oslFileError osl_create_temp_file_impl_(
206 	const rtl_uString* pustr_base_directory,
207 	oslFileHandle* file_handle,
208 	rtl_uString** ppustr_temp_file_name)
209 {
210 	rtl_uString*        rand_name        = 0;
211 	sal_uInt32          len_base_dir     = 0;
212 	rtl_uString*        tmp_file_path    = 0;
213 	rtl_uString*        tmp_file_url     = 0;
214 	sal_Int32           capacity         = 0;
215 	oslFileError        osl_error        = osl_File_E_None;
216 	sal_Int32           offset_file_name;
217 	const sal_Unicode*  puchr;
218 
219 	OSL_PRECOND(pustr_base_directory, "Invalid Parameter");
220 	OSL_PRECOND(file_handle, "Invalid Parameter");
221 	OSL_PRECOND(ppustr_temp_file_name, "Invalid Parameter");
222 
223 	len_base_dir = rtl_uString_getLength(pustr_base_directory);
224 
225 	rtl_uStringbuffer_newFromStr_WithLength(
226 		&tmp_file_path,
227 		rtl_uString_getStr((rtl_uString*)pustr_base_directory),
228 		len_base_dir);
229 
230 	rtl_uStringbuffer_ensureCapacity(
231 		&tmp_file_path,
232 		&capacity,
233 		(len_base_dir + 1 + RAND_NAME_LENGTH));
234 
235 	offset_file_name = len_base_dir;
236 
237 	puchr = rtl_uString_getStr(tmp_file_path);
238 
239 	/* ensure that the last character is a '/' */
240 
241 	if ((sal_Unicode)'/' != puchr[len_base_dir - 1])
242 	{
243 		rtl_uStringbuffer_insert_ascii(
244 			&tmp_file_path,
245 			&capacity,
246 			len_base_dir,
247 			"/",
248 			1);
249 
250 		offset_file_name++;
251 	}
252 
253 	while(1) /* try until success */
254 	{
255 		osl_gen_random_name_impl_(&rand_name);
256 
257 		rtl_uStringbuffer_insert(
258 			&tmp_file_path,
259 			&capacity,
260 			offset_file_name,
261 			rtl_uString_getStr(rand_name),
262 			rtl_uString_getLength(rand_name));
263 
264 		osl_error = osl_getFileURLFromSystemPath(
265 		    tmp_file_path, &tmp_file_url);
266 
267 		if (osl_File_E_None == osl_error)
268 		{
269 		    /* RW permission for the user only! */
270 		    mode_t old_mode = umask(077);
271 
272 		    osl_error = osl_openFile(
273 			    tmp_file_url,
274 			    file_handle,
275 			    osl_File_OpenFlag_Read |
276 			    osl_File_OpenFlag_Write |
277 			    osl_File_OpenFlag_Create);
278 
279 			umask(old_mode);
280 		}
281 
282 		/* in case of error osl_File_E_EXIST we simply try again else we give up */
283 
284 		if ((osl_File_E_None == osl_error) || (osl_error != osl_File_E_EXIST))
285 		{
286 			if (rand_name)
287 				rtl_uString_release(rand_name);
288 
289 			if (tmp_file_url)
290 				rtl_uString_release(tmp_file_url);
291 
292 			break;
293 		}
294 	} /* while(1) */
295 
296 	if (osl_File_E_None == osl_error)
297 		rtl_uString_assign(ppustr_temp_file_name, tmp_file_path);
298 
299 	if (tmp_file_path)
300 		rtl_uString_release(tmp_file_path);
301 
302 	return osl_error;
303 }
304 
305 /*****************************************************************
306  * osl_createTempFile
307  *****************************************************************/
308 
309 oslFileError SAL_CALL osl_createTempFile(
310 	rtl_uString*   pustrDirectoryURL,
311 	oslFileHandle* pHandle,
312 	rtl_uString**  ppustrTempFileURL)
313 {
314 	rtl_uString*  base_directory     = 0;
315 	rtl_uString*  temp_file_name     = 0;
316 	oslFileHandle temp_file_handle;
317 	sal_Bool      b_delete_on_close;
318 	oslFileError  osl_error;
319 
320 	osl_error = osl_setup_createTempFile_impl_(
321 		pustrDirectoryURL,
322 		pHandle,
323 		ppustrTempFileURL,
324 		&base_directory,
325 		&b_delete_on_close);
326 
327 	if (osl_File_E_None != osl_error)
328 		return osl_error;
329 
330 	osl_error = osl_create_temp_file_impl_(
331 	    base_directory, &temp_file_handle, &temp_file_name);
332 
333 	if (osl_File_E_None == osl_error)
334 	{
335 		rtl_uString* temp_file_url = 0;
336 
337 		/* assuming this works */
338 		osl_getFileURLFromSystemPath(temp_file_name, &temp_file_url);
339 
340 		if (b_delete_on_close)
341 		{
342 			osl_error = osl_removeFile(temp_file_url);
343 
344 			if (osl_File_E_None == osl_error)
345 				*pHandle = temp_file_handle;
346 			else
347 				osl_closeFile(temp_file_handle);
348 		}
349 		else
350 		{
351 			if (pHandle)
352 				*pHandle = temp_file_handle;
353 			else
354 				osl_closeFile(temp_file_handle);
355 
356 			rtl_uString_assign(ppustrTempFileURL, temp_file_url);
357         }
358 
359 		if (temp_file_url)
360 			rtl_uString_release(temp_file_url);
361 
362         if (temp_file_name)
363             rtl_uString_release(temp_file_name);
364 	}
365 
366     if (base_directory)
367     	rtl_uString_release(base_directory);
368 
369     return osl_error;
370 }
371