Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

823 lines
22 KiB

  1. //#pragma title( "TReg.cpp - NT registry class" )
  2. /*
  3. Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
  4. ===============================================================================
  5. Module - TReg.cpp
  6. System - Common
  7. Author - Tom Bernhardt, Rich Denham
  8. Created - 1995-09-01
  9. Description - NT registry class.
  10. Updates -
  11. ===============================================================================
  12. */
  13. #ifdef USE_STDAFX
  14. # include "stdafx.h"
  15. #else
  16. # include <windows.h>
  17. #endif
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <time.h>
  21. #include "Common.hpp"
  22. #include "UString.hpp"
  23. #include "Err.hpp"
  24. #include "TNode.hpp"
  25. #include "TReg.hpp"
  26. // Short term solution
  27. #define MAX_REG_NAMELEN 512
  28. #define MAX_REG_VALUELEN 2048
  29. // Destructor function was formerly inline.
  30. // It is here to facilitate handle leak tracing.
  31. TRegKey::~TRegKey()
  32. {
  33. Close();
  34. };
  35. // Close function was formerly inline.
  36. // It is here to facilitate handle leak tracing.
  37. void
  38. TRegKey::Close()
  39. {
  40. if ( hKey != INVALID_HANDLE_VALUE )
  41. {
  42. RegCloseKey( hKey );
  43. hKey = (HKEY) INVALID_HANDLE_VALUE;
  44. }
  45. };
  46. // open registry on remote computer
  47. DWORD
  48. TRegKey::Connect(
  49. HKEY hPreDefined ,// in -must be HKEY_LOCAL_MACHINE or HKEY_USERS
  50. TCHAR const * machineName // in -remote computer name
  51. )
  52. {
  53. LONG rc; // return code
  54. if ( hKey != INVALID_HANDLE_VALUE )
  55. {
  56. Close();
  57. }
  58. rc = RegConnectRegistry( const_cast<TCHAR *>(machineName), hPreDefined, &hKey );
  59. if ( rc )
  60. {
  61. hKey = (HKEY) INVALID_HANDLE_VALUE;
  62. }
  63. return (DWORD)rc;
  64. }
  65. // create new key
  66. DWORD
  67. TRegKey::Create(
  68. TCHAR const * keyname ,// in -name/path of key to create/open
  69. HKEY hParent ,// in -handle of parent key
  70. DWORD * pDisp ,// out-disposition of create
  71. DWORD access // in -security access mask for key
  72. )
  73. {
  74. DWORD disp;
  75. LONG rc;
  76. if ( hKey != INVALID_HANDLE_VALUE )
  77. {
  78. Close();
  79. }
  80. rc = RegCreateKeyEx( hParent,
  81. keyname,
  82. 0,
  83. NULL,
  84. REG_OPTION_NON_VOLATILE,
  85. access,
  86. NULL,
  87. &hKey,
  88. (pDisp!=NULL) ? pDisp : &disp );
  89. if ( rc )
  90. {
  91. hKey = (HKEY) INVALID_HANDLE_VALUE;
  92. }
  93. return (DWORD)rc;
  94. }
  95. // create new key (using backup/restore)
  96. DWORD
  97. TRegKey::CreateBR(
  98. TCHAR const * keyname ,// in -name/path of key to create/open
  99. HKEY hParent ,// in -handle of parent key
  100. DWORD * pDisp ,// out-disposition of create
  101. DWORD access // in -security access mask for key
  102. )
  103. {
  104. DWORD disp;
  105. LONG rc;
  106. if ( hKey != INVALID_HANDLE_VALUE )
  107. {
  108. Close();
  109. }
  110. rc = RegCreateKeyEx( hParent,
  111. keyname,
  112. 0,
  113. NULL,
  114. REG_OPTION_BACKUP_RESTORE,
  115. access,
  116. NULL,
  117. &hKey,
  118. (pDisp!=NULL) ? pDisp : &disp );
  119. if ( rc )
  120. {
  121. hKey = (HKEY) INVALID_HANDLE_VALUE;
  122. }
  123. return (DWORD)rc;
  124. }
  125. // open existing key
  126. DWORD
  127. TRegKey::Open(
  128. TCHAR const * keyname ,// in -name/path of key to create/open
  129. HKEY hParent ,// in -handle of parent key
  130. DWORD access // in -security access mask for key
  131. )
  132. {
  133. LONG rc;
  134. if ( hKey != INVALID_HANDLE_VALUE )
  135. {
  136. Close();
  137. }
  138. rc = RegOpenKeyEx( hParent,
  139. keyname,
  140. 0,
  141. access,
  142. &hKey );
  143. if ( rc )
  144. {
  145. hKey = (HKEY) INVALID_HANDLE_VALUE;
  146. }
  147. return (DWORD)rc;
  148. }
  149. // Gets the subkey value of the specified index number
  150. DWORD // ret-os return code
  151. TRegKey::SubKeyEnum(
  152. DWORD n ,// in -ordinal number of subkey
  153. TCHAR * keyname ,// out-key name
  154. DWORD keylen // in -max size of key name in TCHARs
  155. ) const
  156. {
  157. LONG rc;
  158. DWORD keyLen = keylen;
  159. FILETIME lastWrite;
  160. rc = RegEnumKeyEx( hKey,
  161. n,
  162. keyname,
  163. &keyLen,
  164. 0,
  165. NULL,
  166. NULL,
  167. &lastWrite );
  168. return (DWORD)rc;
  169. }
  170. // Enumerate value
  171. DWORD // ret-0 or error code
  172. TRegKey::ValueEnum(
  173. DWORD index ,// in -ordinal number of subkey
  174. TCHAR * name ,// out-name
  175. DWORD namelen ,// in -name size in TCHARs
  176. void * value ,// out-value
  177. DWORD * valuelen ,// i/o-value size in BYTEs
  178. DWORD * type // out-value type code
  179. ) const
  180. {
  181. return (DWORD)RegEnumValue( hKey, index, name, &namelen, NULL, type, (BYTE *) value, valuelen );
  182. }
  183. // Get REG_DWORD value
  184. DWORD // ret-OS return code
  185. TRegKey::ValueGetDWORD(
  186. TCHAR const * name ,// in -value name
  187. DWORD * value // out-returned DWORD value
  188. ) const
  189. {
  190. LONG osRc; // OS return code
  191. DWORD type; // type of value
  192. DWORD len = sizeof *value; // value length
  193. osRc = RegQueryValueEx( hKey, name, NULL, &type, (BYTE *) value, &len );
  194. if ( !osRc && (type != REG_DWORD) )
  195. {
  196. osRc = ERROR_FILE_NOT_FOUND;
  197. }
  198. return (DWORD)osRc;
  199. }
  200. // Get REG_SZ value
  201. DWORD // ret-OS return code
  202. TRegKey::ValueGetStr(
  203. TCHAR const * name ,// in -value name
  204. TCHAR * value ,// out-value buffer
  205. DWORD maxlen // in -sizeof value buffer
  206. ) const
  207. {
  208. LONG osRc; // OS return code
  209. DWORD type; // type of value
  210. DWORD len; // value length
  211. // force maxlen to an integral number of TEXT characters
  212. maxlen = maxlen / (sizeof value[0]) * (sizeof value[0]);
  213. if ( !maxlen )
  214. {
  215. osRc = ERROR_FILE_NOT_FOUND;
  216. }
  217. else
  218. {
  219. len = maxlen;
  220. osRc = RegQueryValueEx( hKey, name, NULL, &type, (BYTE *) value, &len );
  221. len = len / (sizeof value[0]) * (sizeof value[0]);
  222. if ( !osRc && (type != REG_SZ) )
  223. {
  224. osRc = ERROR_FILE_NOT_FOUND;
  225. }
  226. if ( osRc )
  227. {
  228. value[0] = TEXT('\0');
  229. }
  230. else
  231. { // return of a null-terminated string is not guaranteed by API!
  232. // force null-terminated string, truncate string if necessary.
  233. if ( len >= maxlen )
  234. {
  235. len = maxlen - sizeof value[0];
  236. }
  237. value[len/(sizeof value[0])] = TEXT('\0');
  238. }
  239. }
  240. return (DWORD)osRc;
  241. }
  242. DWORD
  243. TRegKey::ValueGet(
  244. TCHAR const * name ,// in -name
  245. void * value ,// out-value
  246. DWORD * lenvalue ,// i/o-length of value
  247. DWORD * typevalue // out-type of value
  248. ) const
  249. {
  250. return (DWORD)RegQueryValueEx( hKey, name, 0, typevalue, (UCHAR *) value, lenvalue );
  251. }
  252. // Set REG_SZ value
  253. DWORD
  254. TRegKey::ValueSetStr(
  255. TCHAR const * name ,// in -value name
  256. TCHAR const * value ,// out-value
  257. DWORD type // in -value type
  258. ) const
  259. {
  260. return (DWORD)RegSetValueEx( hKey,
  261. name,
  262. NULL,
  263. type,
  264. (LPBYTE) value,
  265. (UStrLen(value) + 1) * sizeof value[0] );
  266. }
  267. DWORD
  268. TRegKey::ValueSet(
  269. TCHAR const * name ,// in -name
  270. void const * value ,// in -value
  271. DWORD lenvalue ,// in -length of value
  272. DWORD typevalue // in -type of value
  273. ) const
  274. {
  275. return (DWORD)RegSetValueEx( hKey,
  276. name,
  277. 0,
  278. typevalue,
  279. (UCHAR const *) value,
  280. lenvalue );
  281. }
  282. DWORD // ret-0 or error code
  283. TRegKey::ValueDel(
  284. TCHAR const * name // in -value name
  285. ) const
  286. {
  287. LONG rc;
  288. rc = (DWORD)RegDeleteValue(hKey, name);
  289. return rc;
  290. }
  291. DWORD // ret-OS return code
  292. TRegKey::HiveCopy(
  293. TRegKey const * source // in -source hive
  294. )
  295. {
  296. DWORD retval=0; // returned value
  297. DWORD index; // key/value index
  298. TCHAR name[MAX_REG_NAMELEN]; // key name
  299. TCHAR value[MAX_REG_VALUELEN]; // value name
  300. DWORD valuelen; // value length
  301. DWORD type; // value type
  302. TRegKey srcNest; // nested source registry
  303. TRegKey trgNest; // nested target registry
  304. // process values at this level
  305. for ( index = 0;
  306. !retval;
  307. index++ )
  308. {
  309. valuelen = sizeof value;
  310. retval = source->ValueEnum( index, name, MAX_REG_NAMELEN, value, &valuelen, &type );
  311. if ( !retval )
  312. {
  313. retval = this->ValueSet( name, value, valuelen, type );
  314. }
  315. else if ( retval == ERROR_MORE_DATA )
  316. {
  317. retval = 0;
  318. }
  319. }
  320. if ( retval == ERROR_NO_MORE_ITEMS )
  321. {
  322. retval = 0;
  323. }
  324. // process keys at this level; for each key make a recursive call
  325. for ( index = 0;
  326. !retval;
  327. index++ )
  328. {
  329. retval = source->SubKeyEnum( index, name, MAX_REG_NAMELEN );
  330. if ( !retval )
  331. {
  332. retval = srcNest.Open( name, source );
  333. if ( !retval )
  334. {
  335. retval = trgNest.Create( name, this );
  336. if ( !retval )
  337. {
  338. retval = trgNest.HiveCopy( &srcNest );
  339. trgNest.Close();
  340. }
  341. srcNest.Close();
  342. }
  343. }
  344. }
  345. if ( retval == ERROR_NO_MORE_ITEMS )
  346. {
  347. retval = 0;
  348. }
  349. return retval;
  350. }
  351. DWORD // ret-OS return code
  352. TRegKey::HiveDel()
  353. {
  354. DWORD retval = 0; // returned value
  355. DWORD index; // value/key index
  356. TCHAR name[MAX_REG_NAMELEN]; // name
  357. DWORD namelen; // name length
  358. BYTE value[MAX_REG_VALUELEN]; // value
  359. DWORD valuelen; // value length
  360. DWORD type; // value type code
  361. TRegKey trgNest; // nested target registry
  362. // delete values at this level
  363. for ( index = 0;
  364. !retval;
  365. /* index++ */ ) // note that index remains at zero
  366. {
  367. namelen = MAX_REG_NAMELEN;
  368. valuelen = sizeof value;
  369. retval = ValueEnum( index, name, namelen, value, &valuelen, &type );
  370. if ( retval == ERROR_MORE_DATA )
  371. {
  372. retval = 0;
  373. }
  374. if ( !retval )
  375. {
  376. retval = ValueDel( name );
  377. }
  378. }
  379. if ( retval == ERROR_NO_MORE_ITEMS )
  380. {
  381. retval = 0;
  382. }
  383. // process keys at this level; for each key make a recursive call
  384. for ( index = 0;
  385. !retval;
  386. /* index++ */ ) // note that index remains at zero
  387. {
  388. retval = SubKeyEnum( index, name, MAX_REG_NAMELEN );
  389. if ( !retval )
  390. {
  391. retval = trgNest.Open( name, this );
  392. if ( !retval )
  393. {
  394. retval = trgNest.HiveDel();
  395. trgNest.Close();
  396. }
  397. retval = SubKeyDel( name );
  398. }
  399. }
  400. if ( retval == ERROR_NO_MORE_ITEMS )
  401. {
  402. retval = 0;
  403. }
  404. return retval;
  405. }
  406. // These four classes are used only by TRegReplicate
  407. // Class to represent one registry key
  408. class RKey : public TNode
  409. {
  410. friend class RKeyList;
  411. private:
  412. TCHAR * name; // key name
  413. protected:
  414. public:
  415. RKey() { name = NULL; };
  416. ~RKey() { if ( name ) delete name; };
  417. BOOL New( TCHAR const * aname );
  418. TCHAR const * GetName() const { return name; };
  419. };
  420. BOOL
  421. RKey::New(
  422. TCHAR const * aname // in -key name
  423. )
  424. {
  425. name = new TCHAR[UStrLen(aname)+1];
  426. if ( name )
  427. {
  428. UStrCpy( name, aname );
  429. }
  430. return !!name;
  431. }
  432. // Class to represent the set of registry keys at one level
  433. class RKeyList : public TNodeListSortable
  434. {
  435. private:
  436. static TNodeCompare( Compare ) { return UStrICmp(
  437. ((RKey const *) v1)->name,
  438. ((RKey const *) v2)->name ); }
  439. protected:
  440. public:
  441. RKeyList() : TNodeListSortable( Compare ) {}
  442. ~RKeyList();
  443. };
  444. // RKeyList object destructor
  445. RKeyList::~RKeyList()
  446. {
  447. DeleteAllListItems( RKey );
  448. }
  449. // Class to represent one registry value
  450. class RValue : public TNode
  451. {
  452. friend class RValueList;
  453. private:
  454. TCHAR * name; // value's name
  455. BYTE * value; // value's value
  456. DWORD valuelen; // value's value length
  457. DWORD type; // value's type
  458. protected:
  459. public:
  460. RValue() { name = NULL; value = NULL; valuelen = type = 0; };
  461. ~RValue() { if ( name ) delete name;
  462. if ( value ) delete value; };
  463. BOOL New( TCHAR const * aname, BYTE const * avalue, DWORD valuelen, DWORD type );
  464. TCHAR const * GetName() const { return name; };
  465. BYTE const * GetValue() const { return value; };
  466. DWORD GetValueLen() const { return valuelen; };
  467. DWORD GetType() const { return type; };
  468. };
  469. BOOL
  470. RValue::New(
  471. TCHAR const * aname ,// in -value's name
  472. BYTE const * avalue ,// in -value's value
  473. DWORD avaluelen ,// in -value's value length
  474. DWORD atype // in -value's type
  475. )
  476. {
  477. name = new TCHAR[UStrLen(aname)+1];
  478. if ( name )
  479. {
  480. UStrCpy( name, aname );
  481. }
  482. value = new BYTE[avaluelen];
  483. if ( value )
  484. {
  485. memcpy( value, avalue, avaluelen );
  486. }
  487. valuelen = avaluelen;
  488. type = atype;
  489. return name && value;
  490. }
  491. // Class to represent the set of registry values at one level
  492. class RValueList : public TNodeListSortable
  493. {
  494. private:
  495. static TNodeCompare( Compare ) { return UStrICmp(
  496. ((RValue const *)v1)->name,
  497. ((RValue const *)v2)->name ); }
  498. protected:
  499. public:
  500. RValueList() : TNodeListSortable( Compare ) {}
  501. ~RValueList();
  502. };
  503. // RValueList object destructor
  504. RValueList::~RValueList()
  505. {
  506. DeleteAllListItems( RValue );
  507. }
  508. // Static subroutine used only by TRegReplicate
  509. // collect all values at one registry level into a RValueList
  510. DWORD static
  511. CollectValues(
  512. RValueList * pValueList ,// out-value list to be built
  513. TRegKey const * pRegKey // in -registry key
  514. )
  515. {
  516. DWORD retval=0; // returned value
  517. DWORD index; // value enum index
  518. TCHAR name[MAX_REG_NAMELEN]; // value name
  519. BYTE value[MAX_REG_VALUELEN]; // value value
  520. DWORD valuelen; // value length
  521. DWORD type; // value type
  522. RValue * pValue; // new value
  523. for ( index = 0;
  524. !retval;
  525. index++ )
  526. {
  527. valuelen = sizeof value;
  528. retval = pRegKey->ValueEnum( index, name, MAX_REG_NAMELEN, value, &valuelen, &type );
  529. if ( !retval )
  530. {
  531. pValue = new RValue;
  532. if ( pValue )
  533. {
  534. if ( pValue->New( name, value, valuelen, type ) )
  535. {
  536. pValueList->Insert( pValue );
  537. }
  538. else
  539. {
  540. delete pValue;
  541. pValue = NULL;
  542. }
  543. }
  544. if ( !pValue )
  545. {
  546. retval = ERROR_NOT_ENOUGH_MEMORY;
  547. }
  548. }
  549. else if ( retval == ERROR_MORE_DATA )
  550. {
  551. retval = 0;
  552. }
  553. }
  554. if ( retval == ERROR_NO_MORE_ITEMS )
  555. {
  556. retval = 0;
  557. }
  558. return retval;
  559. }
  560. // Static subroutine used only by TRegReplicate
  561. // collect all keys at one registry level into a RKeyList
  562. DWORD static
  563. CollectKeys(
  564. RKeyList * pKeyList ,// out-key list to be built
  565. TRegKey const * pRegKey // in -registry key
  566. )
  567. {
  568. DWORD retval=0; // returned value
  569. DWORD index; // key enum index
  570. TCHAR name[MAX_REG_NAMELEN]; // key name
  571. RKey * pKey; // new key object
  572. for ( index = 0;
  573. !retval;
  574. index++ )
  575. {
  576. retval = pRegKey->SubKeyEnum( index, name, MAX_REG_NAMELEN );
  577. if ( !retval )
  578. {
  579. pKey = new RKey;
  580. if ( pKey )
  581. {
  582. if ( pKey->New( name ) )
  583. {
  584. pKeyList->Insert( pKey );
  585. }
  586. else
  587. {
  588. delete pKey;
  589. pKey = NULL;
  590. }
  591. }
  592. if ( !pKey )
  593. {
  594. retval = ERROR_NOT_ENOUGH_MEMORY;
  595. }
  596. }
  597. }
  598. if ( retval == ERROR_NO_MORE_ITEMS )
  599. {
  600. retval = 0;
  601. }
  602. return retval;
  603. }
  604. // Replicate registry hive
  605. DWORD // ret-OS return code
  606. TRegKey::HiveReplicate(
  607. TRegKey const * source // in -source hive
  608. )
  609. {
  610. DWORD retval=0; // returned value
  611. RValueList srcValues; // source values
  612. RValueList trgValues; // target values
  613. TNodeListEnum eSrcValue; // enumerate source values
  614. RValue const * pSrcValue; // source value
  615. TNodeListEnum eTrgValue; // enumerate target values
  616. RValue const * pTrgValue; // target value
  617. RKeyList srcKeys; // source keys
  618. RKeyList trgKeys; // target keys
  619. TNodeListEnum eSrcKey; // enumerate source keys
  620. RKey const * pSrcKey; // source key
  621. TNodeListEnum eTrgKey; // enumerate target keys
  622. RKey const * pTrgKey; // target key
  623. int cmpRc; // compare return code
  624. TRegKey srcNest; // nested source registry
  625. TRegKey trgNest; // nested target registry
  626. // handle replication of values at this level
  627. CollectValues( &srcValues, source );
  628. CollectValues( &trgValues, this );
  629. // now merge the values
  630. pSrcValue = (RValue const *) eSrcValue.OpenFirst( &srcValues );
  631. pTrgValue = (RValue const *) eTrgValue.OpenFirst( &trgValues );
  632. while ( !retval && (pSrcValue || pTrgValue) )
  633. {
  634. if ( !pTrgValue )
  635. {
  636. cmpRc = -1;
  637. }
  638. else if ( !pSrcValue )
  639. {
  640. cmpRc = 1;
  641. }
  642. else
  643. {
  644. cmpRc = UStrICmp( pSrcValue->GetName(), pTrgValue->GetName() );
  645. }
  646. if ( cmpRc < 0 )
  647. { // source value only (copy)
  648. retval = this->ValueSet( pSrcValue->GetName(), pSrcValue->GetValue(),
  649. pSrcValue->GetValueLen(), pSrcValue->GetType() );
  650. pSrcValue = (RValue const *) eSrcValue.Next();
  651. }
  652. else if ( cmpRc > 0 )
  653. { // target value only (delete)
  654. retval = this->ValueDel( pTrgValue->GetName() );
  655. pTrgValue = (RValue const *) eTrgValue.Next();
  656. }
  657. else /* if ( cmpRc == 0 ) */
  658. { // identical value names (replicate)
  659. retval = this->ValueSet( pSrcValue->GetName(), pSrcValue->GetValue(),
  660. pSrcValue->GetValueLen(), pSrcValue->GetType() );
  661. pSrcValue = (RValue const *) eSrcValue.Next();
  662. pTrgValue = (RValue const *) eTrgValue.Next();
  663. }
  664. }
  665. eSrcValue.Close();
  666. eTrgValue.Close();
  667. // handle replication of keys at this level
  668. CollectKeys( &srcKeys, source );
  669. CollectKeys( &trgKeys, this );
  670. // now merge the values
  671. pSrcKey = (RKey const *) eSrcKey.OpenFirst( &srcKeys );
  672. pTrgKey = (RKey const *) eTrgKey.OpenFirst( &trgKeys );
  673. while ( !retval && (pSrcKey || pTrgKey) )
  674. {
  675. if ( !pTrgKey )
  676. {
  677. cmpRc = -1;
  678. }
  679. else if ( !pSrcKey )
  680. {
  681. cmpRc = 1;
  682. }
  683. else
  684. {
  685. cmpRc = UStrICmp( pSrcKey->GetName(), pTrgKey->GetName() );
  686. }
  687. if ( cmpRc < 0 )
  688. { // source key only (copy hive)
  689. retval = srcNest.Open( pSrcKey->GetName(), source );
  690. if ( !retval )
  691. {
  692. retval = trgNest.Create( pSrcKey->GetName(), this );
  693. if ( !retval )
  694. {
  695. retval = trgNest.HiveCopy( &srcNest );
  696. trgNest.Close();
  697. }
  698. srcNest.Close();
  699. }
  700. pSrcKey = (RKey const *) eSrcKey.Next();
  701. }
  702. else if ( cmpRc > 0 )
  703. { // target key only (delete hive)
  704. retval = trgNest.Open( pTrgKey->GetName(), this );
  705. if ( !retval )
  706. {
  707. retval = trgNest.HiveDel();
  708. trgNest.Close();
  709. }
  710. retval = SubKeyDel( pTrgKey->GetName() );
  711. pTrgKey = (RKey const *) eTrgKey.Next();
  712. }
  713. else /* if ( cmpRc == 0 ) */
  714. { // identical keys (replicate hive)
  715. retval = srcNest.Open( pSrcKey->GetName(), source );
  716. if ( !retval )
  717. {
  718. retval = trgNest.Open( pSrcKey->GetName(), this );
  719. if ( !retval )
  720. {
  721. retval = trgNest.HiveReplicate( &srcNest );
  722. trgNest.Close();
  723. }
  724. srcNest.Close();
  725. }
  726. pSrcKey = (RKey const *) eSrcKey.Next();
  727. pTrgKey = (RKey const *) eTrgKey.Next();
  728. }
  729. }
  730. eSrcKey.Close();
  731. eTrgKey.Close();
  732. return retval;
  733. }
  734. // TReg.cpp - end of file