PageRenderTime 75ms CodeModel.GetById 31ms app.highlight 38ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llvfs/lldir.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 727 lines | 578 code | 100 blank | 49 comment | 50 complexity | 2371c4a77552af1a790b1b806abed236 MD5 | raw file
  1/** 
  2 * @file lldir.cpp
  3 * @brief implementation of directory utilities base class
  4 *
  5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28
 29#if !LL_WINDOWS
 30#include <sys/stat.h>
 31#include <sys/types.h>
 32#include <errno.h>
 33#else
 34#include <direct.h>
 35#endif
 36
 37#include "lldir.h"
 38
 39#include "llerror.h"
 40#include "lltimer.h"	// ms_sleep()
 41#include "lluuid.h"
 42
 43#include "lldiriterator.h"
 44
 45#if LL_WINDOWS
 46#include "lldir_win32.h"
 47LLDir_Win32 gDirUtil;
 48#elif LL_DARWIN
 49#include "lldir_mac.h"
 50LLDir_Mac gDirUtil;
 51#elif LL_SOLARIS
 52#include "lldir_solaris.h"
 53LLDir_Solaris gDirUtil;
 54#else
 55#include "lldir_linux.h"
 56LLDir_Linux gDirUtil;
 57#endif
 58
 59LLDir *gDirUtilp = (LLDir *)&gDirUtil;
 60
 61LLDir::LLDir()
 62:	mAppName(""),
 63	mExecutablePathAndName(""),
 64	mExecutableFilename(""),
 65	mExecutableDir(""),
 66	mAppRODataDir(""),
 67	mOSUserDir(""),
 68	mOSUserAppDir(""),
 69	mLindenUserDir(""),
 70	mOSCacheDir(""),
 71	mCAFile(""),
 72	mTempDir(""),
 73	mDirDelimiter("/") // fallback to forward slash if not overridden
 74{
 75}
 76
 77LLDir::~LLDir()
 78{
 79}
 80
 81
 82S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
 83{
 84	S32 count = 0;
 85	std::string filename; 
 86	std::string fullpath;
 87	S32 result;
 88
 89	LLDirIterator iter(dirname, mask);
 90	while (iter.next(filename))
 91	{
 92		fullpath = dirname;
 93		fullpath += getDirDelimiter();
 94		fullpath += filename;
 95
 96		if(LLFile::isdir(fullpath))
 97		{
 98			// skipping directory traversal filenames
 99			count++;
100			continue;
101		}
102
103		S32 retry_count = 0;
104		while (retry_count < 5)
105		{
106			if (0 != LLFile::remove(fullpath))
107			{
108				retry_count++;
109				result = errno;
110				llwarns << "Problem removing " << fullpath << " - errorcode: "
111						<< result << " attempt " << retry_count << llendl;
112
113				if(retry_count >= 5)
114				{
115					llwarns << "Failed to remove " << fullpath << llendl ;
116					return count ;
117				}
118
119				ms_sleep(100);
120			}
121			else
122			{
123				if (retry_count)
124				{
125					llwarns << "Successfully removed " << fullpath << llendl;
126				}
127				break;
128			}			
129		}
130		count++;
131	}
132	return count;
133}
134
135const std::string LLDir::findFile(const std::string &filename, 
136						   const std::string& searchPath1, 
137						   const std::string& searchPath2, 
138						   const std::string& searchPath3) const
139{
140	std::vector<std::string> search_paths;
141	search_paths.push_back(searchPath1);
142	search_paths.push_back(searchPath2);
143	search_paths.push_back(searchPath3);
144	return findFile(filename, search_paths);
145}
146
147const std::string LLDir::findFile(const std::string& filename, const std::vector<std::string> search_paths) const
148{
149	std::vector<std::string>::const_iterator search_path_iter;
150	for (search_path_iter = search_paths.begin();
151		search_path_iter != search_paths.end();
152		++search_path_iter)
153	{
154		if (!search_path_iter->empty())
155		{
156			std::string filename_and_path = (*search_path_iter);
157			if (!filename.empty())
158			{
159				filename_and_path += getDirDelimiter() + filename;
160			}
161			if (fileExists(filename_and_path))
162			{
163				return filename_and_path;
164			}
165		}
166	}
167	return "";
168}
169
170
171const std::string &LLDir::getExecutablePathAndName() const
172{
173	return mExecutablePathAndName;
174}
175
176const std::string &LLDir::getExecutableFilename() const
177{
178	return mExecutableFilename;
179}
180
181const std::string &LLDir::getExecutableDir() const
182{
183	return mExecutableDir;
184}
185
186const std::string &LLDir::getWorkingDir() const
187{
188	return mWorkingDir;
189}
190
191const std::string &LLDir::getAppName() const
192{
193	return mAppName;
194}
195
196const std::string &LLDir::getAppRODataDir() const
197{
198	return mAppRODataDir;
199}
200
201const std::string &LLDir::getOSUserDir() const
202{
203	return mOSUserDir;
204}
205
206const std::string &LLDir::getOSUserAppDir() const
207{
208	return mOSUserAppDir;
209}
210
211const std::string &LLDir::getLindenUserDir() const
212{
213	if (mLindenUserDir.empty())
214	{
215		lldebugs << "getLindenUserDir() called early, we don't have the user name yet - returning empty string to caller" << llendl;
216	}
217
218	return mLindenUserDir;
219}
220
221const std::string &LLDir::getChatLogsDir() const
222{
223	return mChatLogsDir;
224}
225
226const std::string &LLDir::getPerAccountChatLogsDir() const
227{
228	return mPerAccountChatLogsDir;
229}
230
231const std::string &LLDir::getTempDir() const
232{
233	return mTempDir;
234}
235
236const std::string  LLDir::getCacheDir(bool get_default) const
237{
238	if (mCacheDir.empty() || get_default)
239	{
240		if (!mDefaultCacheDir.empty())
241		{	// Set at startup - can't set here due to const API
242			return mDefaultCacheDir;
243		}
244		
245		std::string res = buildSLOSCacheDir();
246		return res;
247	}
248	else
249	{
250		return mCacheDir;
251	}
252}
253
254// Return the default cache directory
255std::string LLDir::buildSLOSCacheDir() const
256{
257	std::string res;
258	if (getOSCacheDir().empty())
259	{
260		if (getOSUserAppDir().empty())
261		{
262			res = "data";
263		}
264		else
265		{
266			res = getOSUserAppDir() + mDirDelimiter + "cache";
267		}
268	}
269	else
270	{
271		res = getOSCacheDir() + mDirDelimiter + "SecondLife";
272	}
273	return res;
274}
275
276
277
278const std::string &LLDir::getOSCacheDir() const
279{
280	return mOSCacheDir;
281}
282
283
284const std::string &LLDir::getCAFile() const
285{
286	return mCAFile;
287}
288
289const std::string &LLDir::getDirDelimiter() const
290{
291	return mDirDelimiter;
292}
293
294const std::string &LLDir::getSkinDir() const
295{
296	return mSkinDir;
297}
298
299const std::string &LLDir::getUserSkinDir() const
300{
301	return mUserSkinDir;
302}
303
304const std::string& LLDir::getDefaultSkinDir() const
305{
306	return mDefaultSkinDir;
307}
308
309const std::string LLDir::getSkinBaseDir() const
310{
311	return mSkinBaseDir;
312}
313
314const std::string &LLDir::getLLPluginDir() const
315{
316	return mLLPluginDir;
317}
318
319std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const
320{
321	return getExpandedFilename(location, "", filename);
322}
323
324std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subdir, const std::string& filename) const
325{
326	return getExpandedFilename(location, "", subdir, filename);
327}
328
329std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subdir1, const std::string& subdir2, const std::string& in_filename) const
330{
331	std::string prefix;
332	switch (location)
333	{
334	case LL_PATH_NONE:
335		// Do nothing
336		break;
337
338	case LL_PATH_APP_SETTINGS:
339		prefix = getAppRODataDir();
340		prefix += mDirDelimiter;
341		prefix += "app_settings";
342		break;
343	
344	case LL_PATH_CHARACTER:
345		prefix = getAppRODataDir();
346		prefix += mDirDelimiter;
347		prefix += "character";
348		break;
349		
350	case LL_PATH_HELP:
351		prefix = "help";
352		break;
353		
354	case LL_PATH_CACHE:
355		prefix = getCacheDir();
356		break;
357		
358	case LL_PATH_USER_SETTINGS:
359		prefix = getOSUserAppDir();
360		prefix += mDirDelimiter;
361		prefix += "user_settings";
362		break;
363
364	case LL_PATH_PER_SL_ACCOUNT:
365		prefix = getLindenUserDir();
366		if (prefix.empty())
367		{
368			// if we're asking for the per-SL-account directory but we haven't logged in yet (or otherwise don't know the account name from which to build this string), then intentionally return a blank string to the caller and skip the below warning about a blank prefix.
369			return std::string();
370		}
371		break;
372		
373	case LL_PATH_CHAT_LOGS:
374		prefix = getChatLogsDir();
375		break;
376		
377	case LL_PATH_PER_ACCOUNT_CHAT_LOGS:
378		prefix = getPerAccountChatLogsDir();
379		break;
380
381	case LL_PATH_LOGS:
382		prefix = getOSUserAppDir();
383		prefix += mDirDelimiter;
384		prefix += "logs";
385		break;
386
387	case LL_PATH_TEMP:
388		prefix = getTempDir();
389		break;
390
391	case LL_PATH_TOP_SKIN:
392		prefix = getSkinDir();
393		break;
394
395	case LL_PATH_DEFAULT_SKIN:
396		prefix = getDefaultSkinDir();
397		break;
398
399	case LL_PATH_USER_SKIN:
400		prefix = getUserSkinDir();
401		break;
402
403	case LL_PATH_SKINS:
404		prefix = getSkinBaseDir();
405		break;
406
407	case LL_PATH_LOCAL_ASSETS:
408		prefix = getAppRODataDir();
409		prefix += mDirDelimiter;
410		prefix += "local_assets";
411		break;
412
413	case LL_PATH_EXECUTABLE:
414		prefix = getExecutableDir();
415		break;
416		
417	case LL_PATH_FONTS:
418		prefix = getAppRODataDir();
419		prefix += mDirDelimiter;
420		prefix += "fonts";
421		break;
422		
423	default:
424		llassert(0);
425	}
426
427	std::string filename = in_filename;
428	if (!subdir2.empty())
429	{
430		filename = subdir2 + mDirDelimiter + filename;
431	}
432
433	if (!subdir1.empty())
434	{
435		filename = subdir1 + mDirDelimiter + filename;
436	}
437
438	if (prefix.empty())
439	{
440		llwarns << "prefix is empty, possible bad filename" << llendl;
441	}
442	
443	std::string expanded_filename;
444	if (!filename.empty())
445	{
446		if (!prefix.empty())
447		{
448			expanded_filename += prefix;
449			expanded_filename += mDirDelimiter;
450			expanded_filename += filename;
451		}
452		else
453		{
454			expanded_filename = filename;
455		}
456	}
457	else if (!prefix.empty())
458	{
459		// Directory only, no file name.
460		expanded_filename = prefix;
461	}
462	else
463	{
464		expanded_filename.assign("");
465	}
466
467	//llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl;
468	return expanded_filename;
469}
470
471std::string LLDir::getBaseFileName(const std::string& filepath, bool strip_exten) const
472{
473	std::size_t offset = filepath.find_last_of(getDirDelimiter());
474	offset = (offset == std::string::npos) ? 0 : offset+1;
475	std::string res = filepath.substr(offset, std::string::npos);
476	if (strip_exten)
477	{
478		offset = res.find_last_of('.');
479		if (offset != std::string::npos &&
480		    offset != 0) // if basename STARTS with '.', don't strip
481		{
482			res = res.substr(0, offset);
483		}
484	}
485	return res;
486}
487
488std::string LLDir::getDirName(const std::string& filepath) const
489{
490	std::size_t offset = filepath.find_last_of(getDirDelimiter());
491	S32 len = (offset == std::string::npos) ? 0 : offset;
492	std::string dirname = filepath.substr(0, len);
493	return dirname;
494}
495
496std::string LLDir::getExtension(const std::string& filepath) const
497{
498	if (filepath.empty())
499		return std::string();
500	std::string basename = getBaseFileName(filepath, false);
501	std::size_t offset = basename.find_last_of('.');
502	std::string exten = (offset == std::string::npos || offset == 0) ? "" : basename.substr(offset+1);
503	LLStringUtil::toLower(exten);
504	return exten;
505}
506
507std::string LLDir::findSkinnedFilename(const std::string &filename) const
508{
509	return findSkinnedFilename("", "", filename);
510}
511
512std::string LLDir::findSkinnedFilename(const std::string &subdir, const std::string &filename) const
513{
514	return findSkinnedFilename("", subdir, filename);
515}
516
517std::string LLDir::findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const
518{
519	// generate subdirectory path fragment, e.g. "/foo/bar", "/foo", ""
520	std::string subdirs = ((subdir1.empty() ? "" : mDirDelimiter) + subdir1)
521						 + ((subdir2.empty() ? "" : mDirDelimiter) + subdir2);
522
523	std::vector<std::string> search_paths;
524	
525	search_paths.push_back(getUserSkinDir() + subdirs);		// first look in user skin override
526	search_paths.push_back(getSkinDir() + subdirs);			// then in current skin
527	search_paths.push_back(getDefaultSkinDir() + subdirs);  // then default skin
528	search_paths.push_back(getCacheDir() + subdirs);		// and last in preload directory
529
530	std::string found_file = findFile(filename, search_paths);
531	return found_file;
532}
533
534std::string LLDir::getTempFilename() const
535{
536	LLUUID random_uuid;
537	std::string uuid_str;
538
539	random_uuid.generate();
540	random_uuid.toString(uuid_str);
541
542	std::string temp_filename = getTempDir();
543	temp_filename += mDirDelimiter;
544	temp_filename += uuid_str;
545	temp_filename += ".tmp";
546
547	return temp_filename;
548}
549
550// static
551std::string LLDir::getScrubbedFileName(const std::string uncleanFileName)
552{
553	std::string name(uncleanFileName);
554	const std::string illegalChars(getForbiddenFileChars());
555	// replace any illegal file chars with and underscore '_'
556	for( unsigned int i = 0; i < illegalChars.length(); i++ )
557	{
558		int j = -1;
559		while((j = name.find(illegalChars[i])) > -1)
560		{
561			name[j] = '_';
562		}
563	}
564	return name;
565}
566
567// static
568std::string LLDir::getForbiddenFileChars()
569{
570	return "\\/:*?\"<>|";
571}
572
573void LLDir::setLindenUserDir(const std::string &username)
574{
575	// if the username isn't set, that's bad
576	if (!username.empty())
577	{
578		// some platforms have case-sensitive filesystems, so be
579		// utterly consistent with our firstname/lastname case.
580		std::string userlower(username);
581		LLStringUtil::toLower(userlower);
582		LLStringUtil::replaceChar(userlower, ' ', '_');
583		mLindenUserDir = getOSUserAppDir();
584		mLindenUserDir += mDirDelimiter;
585		mLindenUserDir += userlower;
586	}
587	else
588	{
589		llerrs << "NULL name for LLDir::setLindenUserDir" << llendl;
590	}
591
592	dumpCurrentDirectories();	
593}
594
595void LLDir::setChatLogsDir(const std::string &path)
596{
597	if (!path.empty() )
598	{
599		mChatLogsDir = path;
600	}
601	else
602	{
603		llwarns << "Invalid name for LLDir::setChatLogsDir" << llendl;
604	}
605}
606
607void LLDir::setPerAccountChatLogsDir(const std::string &username)
608{
609	// if both first and last aren't set, assume we're grabbing the cached dir
610	if (!username.empty())
611	{
612		// some platforms have case-sensitive filesystems, so be
613		// utterly consistent with our firstname/lastname case.
614		std::string userlower(username);
615		LLStringUtil::toLower(userlower);
616		LLStringUtil::replaceChar(userlower, ' ', '_');
617		mPerAccountChatLogsDir = getChatLogsDir();
618		mPerAccountChatLogsDir += mDirDelimiter;
619		mPerAccountChatLogsDir += userlower;
620	}
621	else
622	{
623		llerrs << "NULL name for LLDir::setPerAccountChatLogsDir" << llendl;
624	}
625	
626}
627
628void LLDir::setSkinFolder(const std::string &skin_folder)
629{
630	mSkinDir = getSkinBaseDir();
631	mSkinDir += mDirDelimiter;
632	mSkinDir += skin_folder;
633
634	// user modifications to current skin
635	// e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle
636	mUserSkinDir = getOSUserAppDir();
637	mUserSkinDir += mDirDelimiter;
638	mUserSkinDir += "skins";
639	mUserSkinDir += mDirDelimiter;	
640	mUserSkinDir += skin_folder;
641
642	// base skin which is used as fallback for all skinned files
643	// e.g. c:\program files\secondlife\skins\default
644	mDefaultSkinDir = getSkinBaseDir();
645	mDefaultSkinDir += mDirDelimiter;	
646	mDefaultSkinDir += "default";
647}
648
649bool LLDir::setCacheDir(const std::string &path)
650{
651	if (path.empty() )
652	{
653		// reset to default
654		mCacheDir = "";
655		return true;
656	}
657	else
658	{
659		LLFile::mkdir(path);
660		std::string tempname = path + mDirDelimiter + "temp";
661		LLFILE* file = LLFile::fopen(tempname,"wt");
662		if (file)
663		{
664			fclose(file);
665			LLFile::remove(tempname);
666			mCacheDir = path;
667			return true;
668		}
669		return false;
670	}
671}
672
673void LLDir::dumpCurrentDirectories()
674{
675	LL_DEBUGS2("AppInit","Directories") << "Current Directories:" << LL_ENDL;
676
677	LL_DEBUGS2("AppInit","Directories") << "  CurPath:               " << getCurPath() << LL_ENDL;
678	LL_DEBUGS2("AppInit","Directories") << "  AppName:               " << getAppName() << LL_ENDL;
679	LL_DEBUGS2("AppInit","Directories") << "  ExecutableFilename:    " << getExecutableFilename() << LL_ENDL;
680	LL_DEBUGS2("AppInit","Directories") << "  ExecutableDir:         " << getExecutableDir() << LL_ENDL;
681	LL_DEBUGS2("AppInit","Directories") << "  ExecutablePathAndName: " << getExecutablePathAndName() << LL_ENDL;
682	LL_DEBUGS2("AppInit","Directories") << "  WorkingDir:            " << getWorkingDir() << LL_ENDL;
683	LL_DEBUGS2("AppInit","Directories") << "  AppRODataDir:          " << getAppRODataDir() << LL_ENDL;
684	LL_DEBUGS2("AppInit","Directories") << "  OSUserDir:             " << getOSUserDir() << LL_ENDL;
685	LL_DEBUGS2("AppInit","Directories") << "  OSUserAppDir:          " << getOSUserAppDir() << LL_ENDL;
686	LL_DEBUGS2("AppInit","Directories") << "  LindenUserDir:         " << getLindenUserDir() << LL_ENDL;
687	LL_DEBUGS2("AppInit","Directories") << "  TempDir:               " << getTempDir() << LL_ENDL;
688	LL_DEBUGS2("AppInit","Directories") << "  CAFile:				 " << getCAFile() << LL_ENDL;
689	LL_DEBUGS2("AppInit","Directories") << "  SkinBaseDir:           " << getSkinBaseDir() << LL_ENDL;
690	LL_DEBUGS2("AppInit","Directories") << "  SkinDir:               " << getSkinDir() << LL_ENDL;
691}
692
693
694void dir_exists_or_crash(const std::string &dir_name)
695{
696#if LL_WINDOWS
697	// *FIX: lame - it doesn't do the same thing on windows. not so
698	// important since we don't deploy simulator to windows boxes.
699	LLFile::mkdir(dir_name, 0700);
700#else
701	struct stat dir_stat;
702	if(0 != LLFile::stat(dir_name, &dir_stat))
703	{
704		S32 stat_rv = errno;
705		if(ENOENT == stat_rv)
706		{
707		   if(0 != LLFile::mkdir(dir_name, 0700))		// octal
708		   {
709			   llerrs << "Unable to create directory: " << dir_name << llendl;
710		   }
711		}
712		else
713		{
714			llerrs << "Unable to stat: " << dir_name << " errno = " << stat_rv
715				   << llendl;
716		}
717	}
718	else
719	{
720		// data_dir exists, make sure it's a directory.
721		if(!S_ISDIR(dir_stat.st_mode))
722		{
723			llerrs << "Data directory collision: " << dir_name << llendl;
724		}
725	}
726#endif
727}