Leaked source code of windows server 2003
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.

1359 lines
40 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Job Object Handler
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: persist.cxx
  9. //
  10. // Contents: persistent storage methods
  11. //
  12. // Classes: CJob (continued)
  13. //
  14. // Interfaces: IPersist, IPersistFile
  15. //
  16. // History: 24-May-95 EricB created
  17. // 19-Jul-97 AnirudhS Rewrote SaveP and LoadP to minimize calls
  18. // to ReadFile, WriteFile and LocalAlloc
  19. //
  20. //-----------------------------------------------------------------------------
  21. #include "..\pch\headers.hxx"
  22. #pragma hdrstop
  23. #include <align.h> // needed only for use in some schasserts
  24. #include "job.hxx"
  25. #include "defines.hxx"
  26. #include "SASecRPC.h" // Get/SetAccountInformation RPC definition.
  27. #include <strsafe.h>
  28. // defined in security.cxx
  29. HRESULT
  30. SetTaskFileSecurity(LPCWSTR pwszTaskPath, BOOL fIsATTask);
  31. #define JOB_SIGNATURE_VERSION 1 // data version we write
  32. #define JOB_SIGNATURE_CLIENT_VERSION 1 // software version we are
  33. #define JOB_SIGNATURE_MIN_CLIENT_VERSION 1 // min s/w version that can
  34. // read data we write
  35. struct JOB_SIGNATURE_HEADER
  36. {
  37. WORD wSignatureVersion;
  38. WORD wMinClientVersion;
  39. };
  40. void GenerateUniqueID(GUID * pUuid);
  41. BOOL ReadString(CInputBuffer * pBuf, LPWSTR *ppwsz);
  42. //
  43. // This array of members is used to iterate through the string fields
  44. // of a CJob that are initially held in m_MainBlock.
  45. //
  46. LPWSTR CJob::* const CJob::s_StringField[] =
  47. {
  48. &CJob::m_pwszApplicationName,
  49. &CJob::m_pwszParameters,
  50. &CJob::m_pwszWorkingDirectory,
  51. &CJob::m_pwszCreator,
  52. &CJob::m_pwszComment
  53. };
  54. // IPersist method
  55. //+----------------------------------------------------------------------------
  56. //
  57. // Member: CJob::IPersist::GetClassID
  58. //
  59. // Synopsis: supplies VBScript class object CLSID
  60. //
  61. //-----------------------------------------------------------------------------
  62. STDMETHODIMP
  63. CJob::GetClassID(CLSID * pClsID)
  64. {
  65. TRACE(CJob, GetClassID);
  66. *pClsID = CLSID_CTask;
  67. return S_OK;
  68. }
  69. // IPersistFile methods
  70. //+----------------------------------------------------------------------------
  71. //
  72. // Member: CJob::IPersistFile::IsDirty
  73. //
  74. // Synopsis: checks for changes since it was last saved
  75. //
  76. //-----------------------------------------------------------------------------
  77. STDMETHODIMP
  78. CJob::IsDirty(void)
  79. {
  80. TRACE3(CJob,IsDirty);
  81. return (IsFlagSet(JOB_I_FLAG_PROPERTIES_DIRTY) ||
  82. IsFlagSet(JOB_I_FLAG_TRIGGERS_DIRTY) ||
  83. IsFlagSet(JOB_I_FLAG_SET_ACCOUNT_INFO)) ? S_OK : S_FALSE;
  84. }
  85. //+----------------------------------------------------------------------------
  86. //
  87. // Member: CJob::IPersistFile::Load
  88. //
  89. // Synopsis: loads the job object indicated by the given filename
  90. //
  91. // Arguments: [pwszFileName] - name of the job object file
  92. // [dwMode] - open mode, currently ignored.
  93. //
  94. // Notes: All OLE32 strings are UNICODE, including the filename passed
  95. // in the IPersistFile methods.
  96. //-----------------------------------------------------------------------------
  97. STDMETHODIMP
  98. CJob::Load(LPCOLESTR pwszFileName, DWORD dwMode)
  99. {
  100. return LoadP(pwszFileName, dwMode, TRUE, TRUE);
  101. }
  102. //+----------------------------------------------------------------------------
  103. //
  104. // Member: CJob::LoadP, private
  105. //
  106. // Synopsis: private load method that takes a TCHAR filename
  107. //
  108. // Arguments: [ptszFileName] - name of the job object file
  109. // [dwMode] - open mode, currently ignored.
  110. // [fRemember] - save the file name?
  111. // [fAllData] - if TRUE, load the entire job. If false, load
  112. // only the fixed length data at the head of the
  113. // job file + the job command.
  114. //
  115. //-----------------------------------------------------------------------------
  116. HRESULT
  117. CJob::LoadP(
  118. LPCTSTR ptszFileName,
  119. DWORD dwMode,
  120. BOOL fRemember,
  121. BOOL fAllData
  122. )
  123. {
  124. TRACE3(CJob, LoadP);
  125. HRESULT hr = S_OK;
  126. BYTE * HeapBlock = NULL;
  127. //WCHAR tszFileName[MAX_PATH + 1] = L"";
  128. //
  129. // check the file name
  130. //
  131. //if (!CheckFileName((LPOLESTR)ptszFileName, tszFileName, NULL))
  132. //{
  133. // return E_INVALIDARG;
  134. //}
  135. //
  136. // Save the file name?
  137. //
  138. if (fRemember)
  139. {
  140. LPTSTR ptsz = new TCHAR[lstrlen(ptszFileName) + 1];
  141. if (!ptsz)
  142. {
  143. ERR_OUT("CJob::LoadP", E_OUTOFMEMORY);
  144. return E_OUTOFMEMORY;
  145. }
  146. if (m_ptszFileName)
  147. {
  148. delete m_ptszFileName;
  149. }
  150. m_ptszFileName = ptsz;
  151. StringCchCopy(m_ptszFileName, lstrlen(ptszFileName) + 1, ptszFileName);
  152. }
  153. //
  154. // Open the file.
  155. //
  156. HANDLE hFile = NULL;
  157. hr = OpenFileWithRetry(ptszFileName, GENERIC_READ, FILE_SHARE_READ, &hFile);
  158. if (FAILED(hr))
  159. {
  160. ERR_OUT("CJob::LoadP, file open", hr);
  161. return hr;
  162. }
  163. if (FILE_TYPE_DISK != GetFileType(hFile))
  164. {
  165. CloseHandle(hFile);
  166. ERR_OUT("CJob::Load, Not a disk file", E_ACCESSDENIED);
  167. return E_ACCESSDENIED;
  168. }
  169. if (fRemember)
  170. {
  171. m_fFileCreated = TRUE;
  172. }
  173. //
  174. // Get the file size.
  175. //
  176. DWORD dwFileSize;
  177. {
  178. DWORD dwFileSizeHigh;
  179. DWORD dwError;
  180. dwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
  181. if (dwFileSize == 0xFFFFFFFF &&
  182. (dwError = GetLastError()) != NO_ERROR)
  183. {
  184. hr = HRESULT_FROM_WIN32(dwError);
  185. ERR_OUT("CJob::Load, GetFileSize", hr);
  186. goto Cleanup;
  187. }
  188. if (dwFileSizeHigh > 0)
  189. {
  190. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  191. schDebugOut((DEB_ERROR, "CJob::Load: file too big, SizeHigh = %u\n",
  192. dwFileSizeHigh));
  193. goto Cleanup;
  194. }
  195. }
  196. if (dwFileSize < sizeof(FIXDLEN_DATA))
  197. {
  198. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  199. schDebugOut((DEB_ERROR, "CJob::Load: file too small, size = %u\n",
  200. dwFileSize));
  201. goto Cleanup;
  202. }
  203. //
  204. // Read the entire file into memory.
  205. //
  206. HeapBlock = new BYTE[dwFileSize];
  207. if (HeapBlock == NULL)
  208. {
  209. hr = E_OUTOFMEMORY;
  210. ERR_OUT("CJob::Load, buffer alloc", hr);
  211. goto Cleanup;
  212. }
  213. DWORD dwBytesRead;
  214. if (!ReadFile(hFile, HeapBlock, dwFileSize, &dwBytesRead, NULL))
  215. {
  216. hr = HRESULT_FROM_WIN32(GetLastError());
  217. ERR_OUT("CJob::Load, read file", hr);
  218. delete [] HeapBlock;
  219. goto Cleanup;
  220. }
  221. if (dwBytesRead != dwFileSize)
  222. {
  223. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  224. ERR_OUT("CJob::Load bytes read", hr);
  225. delete [] HeapBlock;
  226. goto Cleanup;
  227. }
  228. //
  229. // Close the file.
  230. //
  231. CloseHandle(hFile);
  232. hFile = INVALID_HANDLE_VALUE;
  233. //
  234. // Free old values of properties that we are about to read, and switch
  235. // to the new heap block.
  236. //
  237. FreeProperties();
  238. m_MainBlock.Set(HeapBlock, dwFileSize);
  239. //
  240. // Get the fixed length Job data.
  241. // (We already verified that the file is at least as big as FIXDLEN_DATA.)
  242. //
  243. { // scope flData and CInputBuffer to avoid "initialization skipped" error
  244. const FIXDLEN_DATA& flData = *(FIXDLEN_DATA *)&HeapBlock[0];
  245. CInputBuffer Buf(&HeapBlock[sizeof FIXDLEN_DATA],
  246. &HeapBlock[dwFileSize]);
  247. //
  248. // Check the version.
  249. //
  250. // Here is where, in future versions, we would look at the version
  251. // properties to determine how to read the job properties.
  252. // For now, though, we just reject old versions as being invalid.
  253. //
  254. #ifdef IN_THE_FUTURE
  255. if (m_wVersion != flData.wVersion)
  256. {
  257. fNewVersion = TRUE;
  258. //
  259. // Add version specific processing here.
  260. //
  261. }
  262. #else // !IN_THE_FUTURE
  263. if (m_wFileObjVer != flData.wFileObjVer)
  264. {
  265. hr = SCHED_E_UNKNOWN_OBJECT_VERSION;
  266. ERR_OUT("CJob::Load invalid object version", 0);
  267. goto Cleanup;
  268. }
  269. #endif // !IN_THE_FUTURE
  270. //schDebugOut((DEB_TRACE, "Load: job object version: %d.%d, build %d\n",
  271. // HIBYTE(flData.wVersion), LOBYTE(flData.wVersion),
  272. // flData.wFileObjVer));
  273. m_wVersion = flData.wVersion;
  274. m_wFileObjVer = flData.wFileObjVer;
  275. m_uuidJob = flData.uuidJob;
  276. m_wTriggerOffset = flData.wTriggerOffset;
  277. m_wErrorRetryCount = flData.wErrorRetryCount;
  278. m_wErrorRetryInterval = flData.wErrorRetryInterval;
  279. m_wIdleWait = flData.wIdleWait;
  280. m_wIdleDeadline = flData.wIdleDeadline;
  281. m_dwPriority = flData.dwPriority;
  282. m_dwMaxRunTime = flData.dwMaxRunTime;
  283. m_ExitCode = flData.ExitCode;
  284. m_hrStatus = flData.hrStatus;
  285. m_rgFlags = flData.rgFlags;
  286. m_stMostRecentRunTime = flData.stMostRecentRunTime;
  287. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  288. if (!Buf.Read(&m_cRunningInstances, sizeof m_cRunningInstances))
  289. {
  290. ERR_OUT("CJob::Load, read Running Instance Count", 0);
  291. goto Cleanup;
  292. }
  293. //
  294. // Get the variable length Job properties.
  295. //
  296. // In all cases, retrieve the job application name.
  297. // (Notice that flData.wAppNameLenOffset is not honored. We have to
  298. // leave it this way now for backward compatibility.)
  299. //
  300. if (!ReadString(&Buf, &m_pwszApplicationName))
  301. {
  302. goto Cleanup;
  303. }
  304. //
  305. // If a full load, retrieve the rest of the variable length data,
  306. // including the triggers.
  307. //
  308. if (fAllData)
  309. {
  310. // Strings
  311. if (!(ReadString(&Buf, &m_pwszParameters) &&
  312. ReadString(&Buf, &m_pwszWorkingDirectory) &&
  313. ReadString(&Buf, &m_pwszCreator) &&
  314. ReadString(&Buf, &m_pwszComment)
  315. ))
  316. {
  317. goto Cleanup;
  318. }
  319. // User Data size
  320. if (!Buf.Read(&m_cbTaskData, sizeof m_cbTaskData))
  321. {
  322. goto Cleanup;
  323. }
  324. // User Data
  325. if (m_cbTaskData == 0)
  326. {
  327. m_pbTaskData = NULL;
  328. }
  329. else
  330. {
  331. m_pbTaskData = Buf.CurrentPosition();
  332. if (!Buf.Advance(m_cbTaskData))
  333. {
  334. goto Cleanup;
  335. }
  336. }
  337. // Size of reserved data
  338. if (!Buf.Read(&m_cReserved, sizeof m_cReserved))
  339. {
  340. goto Cleanup;
  341. }
  342. // Reserved data
  343. //
  344. // If there is reserved data, it must begin with a structure
  345. // that we recognize. In this version we recognize the
  346. // TASKRESERVED1 structure.
  347. //
  348. m_pbReserved = NULL;
  349. if (m_cReserved == 0)
  350. {
  351. //
  352. // There is no reserved data. Initialize the members that
  353. // we would have read from the reserved data to defaults.
  354. //
  355. m_hrStartError = SCHED_S_TASK_HAS_NOT_RUN;
  356. m_rgTaskFlags = 0;
  357. }
  358. else if (m_cReserved < sizeof(TASKRESERVED1))
  359. {
  360. ERR_OUT("CJob::Load, invalid reserved data", hr);
  361. m_cReserved = 0;
  362. goto Cleanup;
  363. }
  364. else
  365. {
  366. m_pbReserved = Buf.CurrentPosition();
  367. if (!Buf.Advance(m_cReserved))
  368. {
  369. goto Cleanup;
  370. }
  371. //
  372. // Copy the portion of the Reserved Data that we understand
  373. // into private data members.
  374. // It may not be aligned properly, so use CopyMemory.
  375. //
  376. TASKRESERVED1 Reserved;
  377. CopyMemory(&Reserved, m_pbReserved, sizeof Reserved);
  378. m_hrStartError = Reserved.hrStartError;
  379. m_rgTaskFlags = Reserved.rgTaskFlags;
  380. }
  381. //
  382. // Load trigger data.
  383. //
  384. hr = this->_LoadTriggersFromBuffer(&Buf);
  385. if (FAILED(hr))
  386. {
  387. ERR_OUT("Loading triggers from storage", hr);
  388. goto Cleanup;
  389. }
  390. //
  391. // If there is more data after the triggers, it must begin with a
  392. // signature header and a signature. If there is less data than
  393. // that, treat it as though the file has no signature.
  394. // If a signature is present, but its "minimum client version" is
  395. // greater than our version, treat it as though the file has no
  396. // signature.
  397. //
  398. JOB_SIGNATURE_HEADER SignHead;
  399. if (Buf.Read(&SignHead, sizeof SignHead) &&
  400. SignHead.wMinClientVersion <= JOB_SIGNATURE_CLIENT_VERSION)
  401. {
  402. m_pbSignature = Buf.CurrentPosition();
  403. if (!Buf.Advance(SIGNATURE_SIZE))
  404. {
  405. schDebugOut((DEB_ERROR, "CJob::Load: si too small, ignoring\n"));
  406. m_pbSignature = NULL;
  407. }
  408. }
  409. // else m_pbSignature was set to NULL in FreeProperties
  410. }
  411. } // end CInputBuffer and flData scope
  412. hr = S_OK;
  413. Cleanup:
  414. //
  415. // Close the file.
  416. //
  417. if (hFile != INVALID_HANDLE_VALUE)
  418. {
  419. CloseHandle(hFile);
  420. }
  421. return hr;
  422. }
  423. //+----------------------------------------------------------------------------
  424. //
  425. // Member: CJob::SaveP, private
  426. //
  427. // Synopsis: saves the object to storage, takes a TCHAR file name
  428. //
  429. // Arguments: [ptszFileName] - if null, save to the previously loaded file.
  430. // [fRemember] - if TRUE, the object becomes associated with
  431. // the new filename.
  432. // [flOptions] - can have the following bits set:
  433. // SAVEP_VARIABLE_LENGTH_DATA:
  434. // if set, saves all job data except the running
  435. // instance count. If not set, saves
  436. // only the fixed length data at the beginning
  437. // of the job. The file must already exist (the
  438. // filename must be NULL) in this case.
  439. //
  440. // SAVEP_RUNNING_INSTANCE_COUNT:
  441. // if set, the running instance count is saved
  442. //
  443. // SAVEP_PRESERVE_NET_SCHEDULE:
  444. // if NOT set, the JOB_I_FLAG_NET_SCHEDULE flag
  445. // is automatically cleared.
  446. //
  447. // Returns: HRESULT codes.
  448. //
  449. //-----------------------------------------------------------------------------
  450. HRESULT
  451. CJob::SaveP(LPCTSTR ptszFileName, BOOL fRemember, ULONG flOptions)
  452. {
  453. TRACE3(CJob, SaveP);
  454. HRESULT hr = S_OK;
  455. HANDLE hFile = NULL;
  456. BOOL fSetSecurity = FALSE;
  457. //
  458. // Decide which name to save the file as. Use the one passed in if
  459. // there is one, otherwise use the previously remembered one.
  460. //
  461. LPCTSTR ptszFileToSaveAs = ptszFileName ? ptszFileName : m_ptszFileName;
  462. if (!(ptszFileToSaveAs && *ptszFileToSaveAs))
  463. {
  464. //
  465. // Can't do a save if there is no file name.
  466. //
  467. return E_INVALIDARG;
  468. }
  469. //
  470. // Figure out whether we will create a new file or open an existing one.
  471. // If using the passed in filename, then this is a save-as (or if
  472. // fRemember is false, a save-copy-as) operation and a new file must
  473. // be created.
  474. // If using the previously remembered filename, then a new file must
  475. // be created iff the file wasn't saved before.
  476. //
  477. DWORD dwDisposition;
  478. DWORD dwAttributes;
  479. if (!(ptszFileToSaveAs == m_ptszFileName && m_fFileCreated))
  480. {
  481. dwDisposition = CREATE_NEW;
  482. if (!(flOptions & SAVEP_VARIABLE_LENGTH_DATA))
  483. {
  484. //
  485. // Creating a new file is only valid if all the data is to be
  486. // saved. Otherwise we would end up with a partial (invalid) file.
  487. //
  488. return E_INVALIDARG;
  489. }
  490. dwAttributes = FILE_ATTRIBUTE_NORMAL;
  491. if (IsFlagSet(TASK_FLAG_HIDDEN))
  492. {
  493. dwAttributes = FILE_ATTRIBUTE_HIDDEN;
  494. }
  495. //
  496. // Always write running instance count on file create.
  497. //
  498. flOptions |= SAVEP_RUNNING_INSTANCE_COUNT;
  499. //
  500. // On file creation, generate a unique ID for this job.
  501. // Done for Win95 as well as NT.
  502. //
  503. GenerateUniqueID(&m_uuidJob);
  504. //
  505. // Set security on file creation. This is done after all writes
  506. // have succeeded and the file has been closed.
  507. //
  508. fSetSecurity = TRUE;
  509. }
  510. else
  511. {
  512. //
  513. // This is a save to an existing file.
  514. //
  515. dwDisposition = OPEN_EXISTING;
  516. dwAttributes = GetFileAttributes(m_ptszFileName);
  517. if (dwAttributes == -1)
  518. {
  519. hr = HRESULT_FROM_WIN32(GetLastError());
  520. ERR_OUT("CJob::Save, GetFileAttributes", hr);
  521. return hr;
  522. }
  523. DWORD dwOrgAttributes = dwAttributes;
  524. //
  525. // Remove the read-only attribute.
  526. //
  527. dwAttributes &= ~FILE_ATTRIBUTE_READONLY;
  528. //
  529. // If the hidden flag is set and the hidden attribute has
  530. // not been set yet, set it now.
  531. //
  532. if (IsFlagSet(TASK_FLAG_HIDDEN))
  533. {
  534. dwAttributes |= FILE_ATTRIBUTE_HIDDEN;
  535. }
  536. if (dwAttributes != dwOrgAttributes)
  537. {
  538. SetFileAttributes(m_ptszFileName, dwAttributes);
  539. }
  540. }
  541. //
  542. // Create/Open the file.
  543. //
  544. hFile = CreateFile(ptszFileToSaveAs,
  545. GENERIC_WRITE,
  546. 0,
  547. NULL,
  548. dwDisposition,
  549. dwAttributes |
  550. FILE_FLAG_SEQUENTIAL_SCAN,
  551. NULL);
  552. if (hFile == INVALID_HANDLE_VALUE)
  553. {
  554. hr = HRESULT_FROM_WIN32(GetLastError());
  555. ERR_OUT("CJob::Save, file open/create", hr);
  556. return hr;
  557. }
  558. if (FILE_TYPE_DISK != GetFileType(hFile))
  559. {
  560. CloseHandle(hFile);
  561. ERR_OUT("CJob::Save, Not a disk file", E_ACCESSDENIED);
  562. return E_ACCESSDENIED;
  563. }
  564. if (fRemember || ptszFileToSaveAs == m_ptszFileName)
  565. {
  566. m_fFileCreated = TRUE;
  567. }
  568. //
  569. // Initialize the creator property if not already done so.
  570. // Set it to the caller's username.
  571. //
  572. if (m_pwszCreator == NULL)
  573. {
  574. //
  575. // Other than out of memory, if an error occurs, leave the
  576. // creator field alone.
  577. //
  578. TCHAR tszUserName[MAX_USERNAME + 1];
  579. DWORD ccUserName = MAX_USERNAME + 1;
  580. if (GetUserName(tszUserName, &ccUserName))
  581. {
  582. // NB : GetUserName returned char count includes the null
  583. // character.
  584. //
  585. m_pwszCreator = new WCHAR[ccUserName];
  586. if (m_pwszCreator == NULL)
  587. {
  588. hr = E_OUTOFMEMORY;
  589. CHECK_HRESULT(hr);
  590. goto ErrExit;
  591. }
  592. StringCchCopy(m_pwszCreator, ccUserName, tszUserName);
  593. }
  594. }
  595. { // scope to avoid "initialization skipped" error
  596. BOOL fUpdateJobState = FALSE;
  597. //
  598. // The disabled flag takes precedence over other states except running.
  599. //
  600. if (IsFlagSet(TASK_FLAG_DISABLED))
  601. {
  602. if (!IsStatus(SCHED_S_TASK_RUNNING))
  603. {
  604. SetStatus(SCHED_S_TASK_DISABLED);
  605. }
  606. }
  607. else
  608. {
  609. if (IsStatus(SCHED_S_TASK_DISABLED))
  610. {
  611. //
  612. // UpdateJobState will set the correct status if no longer
  613. // disabled.
  614. //
  615. fUpdateJobState = TRUE;
  616. }
  617. }
  618. //
  619. // Check to see if the triggers are dirty. If so, update the job status
  620. // and flags before writing it out.
  621. //
  622. if (IsFlagSet(JOB_I_FLAG_TRIGGERS_DIRTY))
  623. {
  624. fUpdateJobState = TRUE;
  625. }
  626. //
  627. // The flag value JOB_I_FLAG_RUN_PROP_CHANGE is never written to disk.
  628. // Instead, the need for a wait list rebuild is signalled by clearing
  629. // JOB_I_FLAG_NO_RUN_PROP_CHANGE. If JOB_I_FLAG_RUN_PROP_CHANGE were to be
  630. // written to disk, then CheckDir would see this bit set and do a wait
  631. // list rebuild, but it would also need to clear the bit to prevent
  632. // successive wait list rebuilds. However, the write to clear the bit
  633. // would cause an additional change notification and CheckDir call. So, to
  634. // avoid this thrashing, we signal a wait list rebuild by the absence of
  635. // the JOB_I_FLAG_NO_RUN_PROP_CHANGE bit.
  636. //
  637. if (IsFlagSet(JOB_I_FLAG_RUN_PROP_CHANGE))
  638. {
  639. ClearFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
  640. ClearFlag(JOB_I_FLAG_NO_RUN_PROP_CHANGE);
  641. fUpdateJobState = TRUE;
  642. }
  643. if (fUpdateJobState)
  644. {
  645. UpdateJobState(FALSE);
  646. }
  647. //
  648. // Regenerate a unique id for the job (a GUID) when the application
  649. // changes. This is done for security reasons.
  650. //
  651. // NB : We need to do this for Win95 as well as NT since we may be
  652. // editing an NT job from a Win95 machine.
  653. //
  654. if (IsFlagSet(JOB_I_FLAG_APPNAME_CHANGE))
  655. {
  656. GenerateUniqueID(&m_uuidJob);
  657. ClearFlag(JOB_I_FLAG_APPNAME_CHANGE);
  658. }
  659. if (!(flOptions & SAVEP_PRESERVE_NET_SCHEDULE))
  660. {
  661. ClearFlag(JOB_I_FLAG_NET_SCHEDULE);
  662. }
  663. //
  664. // Save Job fixed length data.
  665. //
  666. // Allocate a stack buffer which will be sufficient if we are not
  667. // saving variable length data.
  668. // Note that sizeof flStruct > sizeof FIXDLEN_DATA + sizeof WORD,
  669. // due to struct packing requirements, so don't use sizeof flStruct
  670. // to compute offsets.
  671. //
  672. struct FLSTRUCT
  673. {
  674. FIXDLEN_DATA flData;
  675. WORD cRunningInstances;
  676. } flStruct;
  677. // This code depends on flData and cRunningInstances being contiguous
  678. schAssert(FIELD_OFFSET(FLSTRUCT, cRunningInstances) == sizeof FIXDLEN_DATA);
  679. flStruct.flData.wVersion = m_wVersion;
  680. flStruct.flData.wFileObjVer = m_wFileObjVer;
  681. flStruct.flData.uuidJob = m_uuidJob;
  682. flStruct.flData.wAppNameLenOffset = sizeof(FIXDLEN_DATA) +
  683. sizeof(m_cRunningInstances);
  684. flStruct.flData.wTriggerOffset = m_wTriggerOffset;
  685. flStruct.flData.wErrorRetryCount = m_wErrorRetryCount;
  686. flStruct.flData.wErrorRetryInterval = m_wErrorRetryInterval;
  687. flStruct.flData.wIdleWait = m_wIdleWait;
  688. flStruct.flData.wIdleDeadline = m_wIdleDeadline;
  689. flStruct.flData.dwPriority = m_dwPriority;
  690. flStruct.flData.dwMaxRunTime = m_dwMaxRunTime;
  691. flStruct.flData.ExitCode = m_ExitCode;
  692. flStruct.flData.hrStatus = m_hrStatus;
  693. //
  694. // Don't save the dirty & set account information flags.
  695. //
  696. flStruct.flData.rgFlags = m_rgFlags & ~NON_PERSISTED_JOB_FLAGS;
  697. flStruct.flData.stMostRecentRunTime = m_stMostRecentRunTime;
  698. flStruct.cRunningInstances = m_cRunningInstances;
  699. //
  700. // Compute the number of bytes to write to the file. This will be
  701. // the size of the intermediate buffer to be allocated.
  702. //
  703. BYTE * pSource = (BYTE *) &flStruct;
  704. DWORD cbToWrite = sizeof(flStruct.flData);
  705. //
  706. // Save the running instance data only if (a) the file is being created,
  707. // or (b) the SAVEP_RUNNING_INSTANCE_COUNT option is set.
  708. // If SAVEP_VARIABLE_LENGTH_DATA is set, allocate temporary space for the
  709. // running instance count, regardless of whether we are going to write it.
  710. //
  711. if (flOptions & (SAVEP_RUNNING_INSTANCE_COUNT |
  712. SAVEP_VARIABLE_LENGTH_DATA))
  713. {
  714. cbToWrite += sizeof(flStruct.cRunningInstances);
  715. }
  716. if (flOptions & SAVEP_VARIABLE_LENGTH_DATA)
  717. {
  718. //
  719. // Add the space needed to write the strings and their lengths.
  720. // Save the lengths for use later.
  721. //
  722. WORD acStringLen[ARRAY_LEN(s_StringField)]; // array of string lengths
  723. cbToWrite += sizeof acStringLen;
  724. for (int i = 0; i < ARRAY_LEN(acStringLen); i++)
  725. {
  726. LPWSTR pwsz = this->*s_StringField[i];
  727. acStringLen[i] = (pwsz && *pwsz) ? wcslen(pwsz) + 1 : 0;
  728. cbToWrite += acStringLen[i] * sizeof WCHAR;
  729. }
  730. //
  731. // Add the space needed to write the user data, the reserved data
  732. // and their lengths.
  733. //
  734. if (m_cReserved < sizeof TASKRESERVED1)
  735. {
  736. schAssert(m_cReserved == 0);
  737. m_cReserved = sizeof TASKRESERVED1;
  738. }
  739. cbToWrite += sizeof(m_cbTaskData) + m_cbTaskData
  740. + sizeof(m_cReserved) + m_cReserved;
  741. //
  742. // cbToWrite is now the offset to the trigger data. Save it.
  743. //
  744. schAssert(cbToWrite <= MAXUSHORT); // BUGBUG Do NOT just assert.
  745. // Also return a "limit exceeded" error.
  746. flStruct.flData.wTriggerOffset = m_wTriggerOffset = (WORD) cbToWrite;
  747. //
  748. // Add the space needed to write the triggers and their count.
  749. //
  750. WORD cTriggers = m_Triggers.GetCount();
  751. cbToWrite += sizeof cTriggers + cTriggers * sizeof TASK_TRIGGER;
  752. //
  753. // If the job has a signature, add the space needed to write it.
  754. //
  755. if (m_pbSignature != NULL)
  756. {
  757. cbToWrite += sizeof(JOB_SIGNATURE_HEADER) + SIGNATURE_SIZE;
  758. }
  759. //
  760. // We have now computed the required space. Allocate a buffer of
  761. // that size.
  762. //
  763. pSource = new BYTE[cbToWrite];
  764. if (pSource == NULL)
  765. {
  766. hr = E_OUTOFMEMORY;
  767. goto ErrExit;
  768. }
  769. //
  770. // Copy data into the buffer.
  771. //
  772. BYTE * pCurrent = pSource; // current write position
  773. #define WRITE_DATA(pSrc, cbSize) \
  774. CopyMemory(pCurrent, (pSrc), (cbSize)); \
  775. pCurrent += (cbSize);
  776. // FIXDLEN_DATA and Running Instance Count
  777. WRITE_DATA(&flStruct, sizeof flStruct.flData +
  778. sizeof flStruct.cRunningInstances);
  779. // Strings
  780. for (i = 0; i < ARRAY_LEN(acStringLen); i++)
  781. {
  782. schAssert(POINTER_IS_ALIGNED(pCurrent, ALIGN_WORD));
  783. *(WORD *) pCurrent = acStringLen[i];
  784. pCurrent += sizeof WORD;
  785. WRITE_DATA(this->*s_StringField[i],
  786. acStringLen[i] * sizeof WCHAR);
  787. }
  788. // User data
  789. schAssert(POINTER_IS_ALIGNED(pCurrent, ALIGN_WORD));
  790. *(WORD *) pCurrent = m_cbTaskData;
  791. pCurrent += sizeof WORD;
  792. WRITE_DATA(m_pbTaskData, m_cbTaskData);
  793. // Note that pCurrent may no longer be WORD-aligned
  794. // Reserved data
  795. WRITE_DATA(&m_cReserved, sizeof m_cReserved);
  796. // Copy private members into the reserved data block to save
  797. TASKRESERVED1 Reserved1 = { m_hrStartError, m_rgTaskFlags };
  798. if (m_pbReserved == NULL)
  799. {
  800. schAssert(m_cReserved == sizeof Reserved1);
  801. WRITE_DATA(&Reserved1, m_cReserved);
  802. }
  803. else
  804. {
  805. schAssert(m_cReserved >= sizeof Reserved1);
  806. CopyMemory(m_pbReserved, &Reserved1, sizeof Reserved1);
  807. WRITE_DATA(m_pbReserved, m_cReserved);
  808. }
  809. // Triggers
  810. WRITE_DATA(&cTriggers, sizeof cTriggers);
  811. WRITE_DATA(m_Triggers.GetArray(), sizeof TASK_TRIGGER * cTriggers);
  812. // Signature
  813. if (m_pbSignature != NULL)
  814. {
  815. JOB_SIGNATURE_HEADER SignHead;
  816. SignHead.wSignatureVersion = JOB_SIGNATURE_VERSION;
  817. SignHead.wMinClientVersion = JOB_SIGNATURE_MIN_CLIENT_VERSION;
  818. WRITE_DATA(&SignHead, sizeof SignHead);
  819. WRITE_DATA(m_pbSignature, SIGNATURE_SIZE);
  820. }
  821. #undef WRITE_DATA
  822. schAssert(pCurrent == pSource + cbToWrite);
  823. }
  824. //
  825. // Actually write the data to the file
  826. //
  827. if ((flOptions & SAVEP_VARIABLE_LENGTH_DATA) &&
  828. !(flOptions & SAVEP_RUNNING_INSTANCE_COUNT))
  829. {
  830. //
  831. // Write FIXDLEN_DATA, skip over the running instance count, and
  832. // write the variable length data
  833. //
  834. DWORD cbWritten;
  835. if (!WriteFile(hFile, pSource, sizeof FIXDLEN_DATA, &cbWritten, NULL)
  836. || cbWritten != sizeof FIXDLEN_DATA)
  837. {
  838. hr = HRESULT_FROM_WIN32(GetLastError());
  839. ERR_OUT("CJob::Save, write of Job fixed length data", hr);
  840. goto Cleanup;
  841. }
  842. DWORD dwNewPos = SetFilePointer(hFile,
  843. sizeof(m_cRunningInstances),
  844. NULL,
  845. FILE_CURRENT);
  846. if (dwNewPos == 0xffffffff)
  847. {
  848. hr = HRESULT_FROM_WIN32(GetLastError());
  849. ERR_OUT("CJob::Save, moving past running instance count", hr);
  850. goto Cleanup;
  851. }
  852. cbToWrite -= (sizeof FIXDLEN_DATA + sizeof m_cRunningInstances);
  853. if (!WriteFile(hFile,
  854. pSource + (sizeof FIXDLEN_DATA + sizeof m_cRunningInstances),
  855. cbToWrite,
  856. &cbWritten,
  857. NULL)
  858. || cbWritten != cbToWrite)
  859. {
  860. hr = HRESULT_FROM_WIN32(GetLastError());
  861. ERR_OUT("CJob::Save, write of Job variable length data", hr);
  862. goto Cleanup;
  863. }
  864. }
  865. else
  866. {
  867. //
  868. // We can do it all in a single WriteFile.
  869. //
  870. DWORD cbWritten;
  871. if (!WriteFile(hFile, pSource, cbToWrite, &cbWritten, NULL)
  872. || cbWritten != cbToWrite)
  873. {
  874. hr = HRESULT_FROM_WIN32(GetLastError());
  875. ERR_OUT("CJob::Save, write of Job", hr);
  876. goto Cleanup;
  877. }
  878. }
  879. if ((flOptions & SAVEP_VARIABLE_LENGTH_DATA) &&
  880. !SetEndOfFile(hFile))
  881. {
  882. ERR_OUT("CJob::Save, SetEOF", HRESULT_FROM_WIN32(GetLastError()));
  883. }
  884. Cleanup:
  885. CloseHandle(hFile);
  886. hFile = NULL;
  887. if (pSource != (BYTE *) &flStruct)
  888. {
  889. delete [] pSource;
  890. }
  891. if (FAILED(hr))
  892. {
  893. goto ErrExit;
  894. }
  895. } // end scope
  896. //
  897. // Notify the shell of the changes.
  898. //
  899. if (ptszFileName != NULL)
  900. {
  901. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, ptszFileName, NULL);
  902. }
  903. else
  904. {
  905. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, m_ptszFileName, NULL);
  906. }
  907. //
  908. // If doing a Save-As, save the new filename.
  909. //
  910. if (fRemember && ptszFileName != NULL)
  911. {
  912. delete m_ptszFileName;
  913. m_ptszFileName = new TCHAR[lstrlen(ptszFileName) + 1];
  914. if (!m_ptszFileName)
  915. {
  916. ERR_OUT("CJob::SaveP", E_OUTOFMEMORY);
  917. hr = E_OUTOFMEMORY;
  918. goto ErrExit;
  919. }
  920. StringCchCopy(m_ptszFileName, lstrlen(ptszFileName) + 1, ptszFileName);
  921. }
  922. if (ptszFileName == NULL || fRemember)
  923. {
  924. //
  925. // BUGBUG: this is not strictly accurate. There could be a dirty
  926. // string prop that wouldn't be saved during a light save
  927. // (SAVEP_VARIABLE_LENGTH_DATA not specified). This source of
  928. // potential error could be alleviated by breaking
  929. // JOB_I_FLAG_PROPERTIES_DIRTY into two flags:
  930. // JOB_I_FLAG_FIXED_PROPS_DIRTY & JOB_I_FLAG_VAR_PROPS_DIRTY
  931. //
  932. ClearFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  933. if (flOptions & SAVEP_VARIABLE_LENGTH_DATA)
  934. {
  935. ClearFlag(JOB_I_FLAG_TRIGGERS_DIRTY);
  936. }
  937. }
  938. //
  939. // Set default privileges on the file. This is done only for new
  940. // files created as a result of save.
  941. //
  942. /**********************************
  943. This ** SHOULD BE ** redundant. Inherits from container.
  944. if (fSetSecurity)
  945. {
  946. //
  947. // NB : Logic prior to CreateFile guarantees the file name
  948. // will not be NULL.
  949. //
  950. hr = SetTaskFileSecurity(ptszFileToSaveAs,
  951. this->IsFlagSet(JOB_I_FLAG_NET_SCHEDULE));
  952. if (FAILED(hr))
  953. {
  954. goto ErrExit;
  955. }
  956. }
  957. ************************************/
  958. return S_OK;
  959. ErrExit:
  960. if (hFile != NULL) CloseHandle(hFile);
  961. if (dwDisposition == CREATE_NEW)
  962. {
  963. if (!DeleteFile(ptszFileToSaveAs))
  964. {
  965. ERR_OUT("CJob::SaveP: DeleteFile", GetLastError());
  966. }
  967. }
  968. return hr;
  969. }
  970. //+---------------------------------------------------------------------------
  971. //
  972. // Member: CJob::SaveWithRetry, private
  973. //
  974. // Synopsis: Use CJob::SaveP to save the job object to disk with failure retry.
  975. //
  976. // Arguments: [ptszFileName] - See CJob::SaveP
  977. // [fRemember] - See CJob::SaveP
  978. // [flOptions] - See CJob::SaveP
  979. //
  980. // Notes: This method is called everywhere SaveP was originally called to
  981. // add robustness in the event the file is already opened.
  982. //
  983. //----------------------------------------------------------------------------
  984. HRESULT
  985. CJob::SaveWithRetry(LPCTSTR ptszFileName, BOOL fRemember, ULONG flOptions)
  986. {
  987. HRESULT hr;
  988. //
  989. // Write the updated values to the job object. If there are sharing
  990. // violations, retry up to two times.
  991. //
  992. for (int i = 0; i < 3; i++)
  993. {
  994. hr = SaveP(ptszFileName, fRemember, flOptions);
  995. if (SUCCEEDED(hr))
  996. {
  997. return S_OK;
  998. }
  999. if (hr != HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION))
  1000. {
  1001. //
  1002. // If we have a failure other than sharing violation, we will
  1003. // retry anyway after reporting the error.
  1004. //
  1005. ERR_OUT("SaveWithRetry", hr);
  1006. }
  1007. //
  1008. // Wait semi-pseudo-random amount of time before trying again.
  1009. //
  1010. Sleep(250 + (rand() % 250));
  1011. }
  1012. return hr;
  1013. }
  1014. //+----------------------------------------------------------------------------
  1015. //
  1016. // Member: CJob::IPersistFile::SaveCompleted
  1017. //
  1018. // Synopsis: indicates the caller has saved the file with a call to
  1019. // IPersistFile::Save and is finished working with it
  1020. //
  1021. //-----------------------------------------------------------------------------
  1022. STDMETHODIMP
  1023. CJob::SaveCompleted(LPCOLESTR pwszFileName)
  1024. {
  1025. TRACE(CJob, SaveCompleted);
  1026. return S_OK;
  1027. }
  1028. //+----------------------------------------------------------------------------
  1029. //
  1030. // Member: CJob::IPersistFile::GetCurFile
  1031. //
  1032. // Synopsis: supplies either the absolute path of the currently loaded
  1033. // script file or the default filename prompt, if there is no
  1034. // currently-associated file
  1035. //
  1036. //-----------------------------------------------------------------------------
  1037. STDMETHODIMP
  1038. CJob::GetCurFile(LPOLESTR * ppwszFileName)
  1039. {
  1040. TRACE(CJob, GetCurFile);
  1041. HRESULT hr;
  1042. TCHAR * ptszName, tszDefaultName[SCH_SMBUF_LEN];
  1043. WCHAR * pwszName, * pwszBuf = NULL;
  1044. if (!m_ptszFileName || m_ptszFileName[0] == TEXT('\0'))
  1045. {
  1046. //
  1047. // No file currently loaded, return default prompt 'cause that is
  1048. // what the OLE spec says to do.
  1049. //
  1050. StringCchCopy(tszDefaultName, SCH_SMBUF_LEN, TEXT("*.") TSZ_JOB);
  1051. ptszName = tszDefaultName;
  1052. hr = S_FALSE;
  1053. }
  1054. else
  1055. {
  1056. ptszName = m_ptszFileName;
  1057. hr = S_OK;
  1058. }
  1059. pwszName = ptszName;
  1060. int size = wcslen(pwszName);
  1061. LPOLESTR pwz;
  1062. pwz = (LPOLESTR)CoTaskMemAlloc((size + 1) * sizeof(WCHAR));
  1063. if (!pwz)
  1064. {
  1065. *ppwszFileName = NULL;
  1066. return E_OUTOFMEMORY;
  1067. }
  1068. StringCchCopy(pwz, size + 1, pwszName);
  1069. *ppwszFileName = pwz;
  1070. return hr;
  1071. }
  1072. //+----------------------------------------------------------------------------
  1073. //
  1074. // Member: CJob::FreeProperties
  1075. //
  1076. // Synopsis: Frees variable length property memory
  1077. //
  1078. //-----------------------------------------------------------------------------
  1079. void
  1080. CJob::FreeProperties(void)
  1081. {
  1082. for (int iProperty = 0;
  1083. iProperty < ARRAY_LEN(s_StringField);
  1084. iProperty++)
  1085. {
  1086. DELETE_CJOB_FIELD(this->*s_StringField[iProperty])
  1087. }
  1088. DELETE_CJOB_FIELD(m_pbTaskData)
  1089. m_cbTaskData = 0;
  1090. DELETE_CJOB_FIELD(m_pbReserved)
  1091. m_cReserved = 0;
  1092. DELETE_CJOB_FIELD(m_pbSignature)
  1093. m_pbSignature = 0;
  1094. }
  1095. //+----------------------------------------------------------------------------
  1096. //
  1097. // Function: ReadString
  1098. //
  1099. // Synopsis: Reads a wide char string in from an in-memory buffer
  1100. //
  1101. //-----------------------------------------------------------------------------
  1102. BOOL
  1103. ReadString(CInputBuffer * pBuf, LPWSTR *ppwsz)
  1104. {
  1105. schAssert(POINTER_IS_ALIGNED(pBuf->CurrentPosition(), ALIGN_WORD));
  1106. schAssert(*ppwsz == NULL);
  1107. //
  1108. // Read the string length
  1109. //
  1110. WORD cch;
  1111. if (!pBuf->Read(&cch, sizeof cch))
  1112. {
  1113. ERR_OUT("ReadString, file lacks string length", 0);
  1114. return FALSE;
  1115. }
  1116. if (cch != 0)
  1117. {
  1118. LPWSTR pwsz = (LPWSTR) pBuf->CurrentPosition();
  1119. //
  1120. // The string length mustn't exceed the buffer size
  1121. //
  1122. if (!pBuf->Advance(cch * sizeof WCHAR))
  1123. {
  1124. ERR_OUT("ReadString, string overruns file size", 0);
  1125. return FALSE;
  1126. }
  1127. //
  1128. // Verify null termination
  1129. //
  1130. if (pwsz[cch-1] != L'\0')
  1131. {
  1132. ERR_OUT("ReadString, string not null terminated", 0);
  1133. return FALSE;
  1134. }
  1135. *ppwsz = pwsz;
  1136. }
  1137. return TRUE;
  1138. }
  1139. //+----------------------------------------------------------------------------
  1140. //
  1141. // Function: GenerateUniqueID
  1142. //
  1143. // Synopsis: Intialize the UUID passed to a unique ID. On NT, UuidCreate
  1144. // initializes it. If UuidCreate fails, default to our custom
  1145. // ID generation code which is used always on Win95.
  1146. //
  1147. // Arguments: [pUuid] -- Ptr to UUID to initialize.
  1148. //
  1149. // Returns: None.
  1150. //
  1151. // Notes: None.
  1152. //
  1153. //-----------------------------------------------------------------------------
  1154. void
  1155. GenerateUniqueID(GUID * pUuid)
  1156. {
  1157. schAssert(pUuid != NULL);
  1158. //
  1159. // Call UuidCreate only on NT. If this should fail, drop down to
  1160. // our own id generation.
  1161. //
  1162. if (UuidCreate(pUuid) == RPC_S_OK)
  1163. {
  1164. return;
  1165. }
  1166. //
  1167. // Must generate our own unique id.
  1168. //
  1169. // Set Data 1 to the windows tick count.
  1170. //
  1171. pUuid->Data1 = GetTickCount();
  1172. //
  1173. // Set Data2 & Data3 to the current system time milliseconds
  1174. // and seconds values respectively.
  1175. //
  1176. SYSTEMTIME systime;
  1177. GetSystemTime(&systime);
  1178. pUuid->Data2 = systime.wMilliseconds;
  1179. pUuid->Data3 = systime.wSecond;
  1180. //
  1181. // Write the passed uuid ptr address into the first 4 bytes of
  1182. // Data4. Then write the current system time minute value into
  1183. // the following 2. The remaining 2 we'll leave as-is.
  1184. //
  1185. CopyMemory(&pUuid->Data4, &pUuid, sizeof(GUID *));
  1186. CopyMemory((&pUuid->Data4) + sizeof(GUID *), &systime.wMinute,
  1187. sizeof(systime.wMinute));
  1188. }