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.

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