PageRenderTime 18ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 1ms

/mordor/streams/efs.cpp

http://github.com/mozy/mordor
C++ | 277 lines | 248 code | 20 blank | 9 comment | 73 complexity | af63d73a1dc7aa308a371d22e9257521 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include <boost/bind.hpp>
  3. #include "efs.h"
  4. #include "buffer.h"
  5. #include "mordor/assert.h"
  6. #include "mordor/fiber.h"
  7. #include "mordor/string.h"
  8. namespace Mordor {
  9. EFSStream::EFSStream(void *context, bool read, bool ownContext)
  10. : m_context(context),
  11. m_read(read),
  12. m_own(ownContext),
  13. m_readBuffer(NULL),
  14. m_todo(0),
  15. m_pos(0),
  16. m_seekTarget(0)
  17. {
  18. init();
  19. }
  20. EFSStream::EFSStream(const char *filename, bool read)
  21. : m_context(NULL),
  22. m_read(read),
  23. m_own(true),
  24. m_readBuffer(NULL),
  25. m_todo(0),
  26. m_pos(0),
  27. m_seekTarget(0)
  28. {
  29. DWORD dwRet = OpenEncryptedFileRawW(toUtf16(filename).c_str(),
  30. read ? 0 : CREATE_FOR_IMPORT, &m_context);
  31. if (dwRet != ERROR_SUCCESS)
  32. MORDOR_THROW_EXCEPTION_FROM_ERROR_API(dwRet, "OpenEncryptedFileRawW");
  33. init();
  34. }
  35. EFSStream::EFSStream(const wchar_t *filename, bool read)
  36. : m_context(NULL),
  37. m_read(read),
  38. m_own(true),
  39. m_readBuffer(NULL),
  40. m_todo(0),
  41. m_pos(0),
  42. m_seekTarget(0)
  43. {
  44. DWORD dwRet = OpenEncryptedFileRawW(filename,
  45. read ? 0 : CREATE_FOR_IMPORT, &m_context);
  46. if (dwRet != ERROR_SUCCESS)
  47. MORDOR_THROW_EXCEPTION_FROM_ERROR_API(dwRet, "OpenEncryptedFileRawW");
  48. init();
  49. }
  50. EFSStream::EFSStream(const std::string &filename, bool read)
  51. : m_context(NULL),
  52. m_read(read),
  53. m_own(true),
  54. m_readBuffer(NULL),
  55. m_todo(0),
  56. m_pos(0),
  57. m_seekTarget(0)
  58. {
  59. DWORD dwRet = OpenEncryptedFileRawW(toUtf16(filename).c_str(),
  60. read ? 0 : CREATE_FOR_IMPORT, &m_context);
  61. if (dwRet != ERROR_SUCCESS)
  62. MORDOR_THROW_EXCEPTION_FROM_ERROR_API(dwRet, "OpenEncryptedFileRawW");
  63. init();
  64. }
  65. EFSStream::EFSStream(const std::wstring &filename, bool read)
  66. : m_context(NULL),
  67. m_read(read),
  68. m_own(true),
  69. m_readBuffer(NULL),
  70. m_todo(0),
  71. m_pos(0),
  72. m_seekTarget(0)
  73. {
  74. DWORD dwRet = OpenEncryptedFileRawW(filename.c_str(),
  75. read ? 0 : CREATE_FOR_IMPORT, &m_context);
  76. if (dwRet != ERROR_SUCCESS)
  77. MORDOR_THROW_EXCEPTION_FROM_ERROR_API(dwRet, "OpenEncryptedFileRawW");
  78. init();
  79. }
  80. void
  81. EFSStream::init()
  82. {
  83. MORDOR_ASSERT(m_context);
  84. boost::function<void ()> dg;
  85. if (m_read) {
  86. dg = boost::bind(&EFSStream::readFiber, this);
  87. } else {
  88. dg = boost::bind(&EFSStream::writeFiber, this);
  89. }
  90. if (m_fiber) {
  91. m_fiber->reset(dg);
  92. } else {
  93. m_fiber.reset(new Fiber(dg));
  94. }
  95. }
  96. EFSStream::~EFSStream()
  97. {
  98. if (m_fiber && m_fiber->state() == Fiber::HOLD) {
  99. m_pos = -1;
  100. m_fiber->call();
  101. }
  102. if (m_context && m_own) {
  103. CloseEncryptedFileRaw(m_context);
  104. m_context = NULL;
  105. }
  106. }
  107. void
  108. EFSStream::close(Stream::CloseType type)
  109. {
  110. MORDOR_ASSERT(type == BOTH);
  111. if (!m_read && m_fiber && m_fiber->state() == Fiber::HOLD) {
  112. m_todo = 0;
  113. m_fiber->call();
  114. }
  115. if (m_fiber && m_fiber->state() == Fiber::HOLD) {
  116. m_pos = -1;
  117. m_fiber->call();
  118. }
  119. if (m_context && m_own) {
  120. CloseEncryptedFileRaw(m_context);
  121. m_context = NULL;
  122. }
  123. }
  124. size_t
  125. EFSStream::read(Buffer &b, size_t len)
  126. {
  127. MORDOR_ASSERT(m_read);
  128. if (m_fiber->state() == Fiber::TERM)
  129. return 0;
  130. b.reserve(len);
  131. m_readBuffer = &b;
  132. m_todo = len;
  133. m_fiber->call();
  134. m_readBuffer = NULL;
  135. m_pos += len - m_todo;
  136. return len - m_todo;
  137. }
  138. size_t
  139. EFSStream::write(const Buffer &b, size_t len)
  140. {
  141. MORDOR_ASSERT(!m_read);
  142. MORDOR_ASSERT(m_fiber->state() != Fiber::TERM);
  143. if (len == 0)
  144. return 0;
  145. // Deconstifying, but we really do treat it as const
  146. m_writeBuffer = &b;
  147. m_todo = len;
  148. m_fiber->call();
  149. m_writeBuffer = NULL;
  150. m_pos += len - m_todo;
  151. return len - m_todo;
  152. }
  153. long long
  154. EFSStream::seek(long long offset, Anchor anchor)
  155. {
  156. if (anchor == END)
  157. MORDOR_THROW_EXCEPTION(std::invalid_argument("anchor == END is not supported"));
  158. if (anchor == CURRENT) {
  159. offset = m_pos + offset;
  160. anchor = BEGIN;
  161. }
  162. MORDOR_ASSERT(anchor == BEGIN);
  163. if (offset < 0)
  164. MORDOR_THROW_EXCEPTION(std::invalid_argument("negative offset"));
  165. m_seekTarget = offset;
  166. if (m_seekTarget < m_pos) {
  167. if(m_fiber->state() != Fiber::TERM) {
  168. m_pos = -2;
  169. m_fiber->call();
  170. MORDOR_ASSERT(m_fiber->state() == Fiber::TERM);
  171. }
  172. init();
  173. m_pos = 0;
  174. m_todo = 0;
  175. } else if (m_seekTarget == m_pos) {
  176. return m_pos;
  177. }
  178. m_fiber->call();
  179. return m_pos;
  180. }
  181. void
  182. EFSStream::readFiber()
  183. {
  184. MORDOR_ASSERT(m_read);
  185. DWORD dwRet = ReadEncryptedFileRaw(&EFSStream::ExportCallback, this, m_context);
  186. if (dwRet != ERROR_SUCCESS && dwRet != ERROR_CANCELLED)
  187. MORDOR_THROW_EXCEPTION_FROM_ERROR_API(dwRet, "ReadEncryptedFileRaw");
  188. }
  189. DWORD WINAPI
  190. EFSStream::ExportCallback(PBYTE pbData, PVOID pvCallbackContext, ULONG ulLength)
  191. {
  192. EFSStream *self = (EFSStream *)pvCallbackContext;
  193. while (ulLength > 0) {
  194. if (self->m_pos == -1) {
  195. return ERROR_CANCELLED;
  196. } else if (self->m_pos == -2) {
  197. // it's dumb ... but if we seek'ed backward in the file,
  198. // so we need to start over ... ideally we'd say ERROR_CANCELED
  199. // here so we don't get any more callbacks. however, if we return
  200. // a failure code here, the EFS context will become invalid
  201. // and we will not be able to do ReadEncryptedFileRaw() again.
  202. // So we have to let Windows feed us the rest of the file even
  203. // though we don't want it.
  204. return ERROR_SUCCESS;
  205. } else if (self->m_todo == 0) {
  206. MORDOR_ASSERT(self->m_seekTarget >= self->m_pos);
  207. ULONG toAdvance =
  208. (ULONG)std::min<long long>(self->m_seekTarget - self->m_pos, ulLength);
  209. if (toAdvance == 0) {
  210. Fiber::yield();
  211. } else {
  212. ulLength -= toAdvance;
  213. pbData += toAdvance;
  214. self->m_pos += toAdvance;
  215. }
  216. } else {
  217. MORDOR_ASSERT(self->m_readBuffer);
  218. size_t toCopy = std::min<size_t>(self->m_todo, ulLength);
  219. self->m_readBuffer->copyIn(pbData, toCopy);
  220. ulLength -= (ULONG)toCopy;
  221. pbData += toCopy;
  222. self->m_todo -= toCopy;
  223. Fiber::yield();
  224. }
  225. }
  226. return ERROR_SUCCESS;
  227. }
  228. void
  229. EFSStream::writeFiber()
  230. {
  231. MORDOR_ASSERT(!m_read);
  232. DWORD dwRet = WriteEncryptedFileRaw(&EFSStream::ImportCallback, this, m_context);
  233. if (dwRet != ERROR_SUCCESS && dwRet != ERROR_CANCELLED)
  234. MORDOR_THROW_EXCEPTION_FROM_ERROR_API(dwRet, "WriteEncryptedFileRaw");
  235. }
  236. DWORD WINAPI
  237. EFSStream::ImportCallback(PBYTE pbData, PVOID pvCallbackContext, PULONG ulLength)
  238. {
  239. EFSStream *self = (EFSStream *)pvCallbackContext;
  240. if (self->m_pos == -1) {
  241. return ERROR_CANCELLED;
  242. } else if (self->m_todo == 0) {
  243. *ulLength = 0;
  244. return ERROR_SUCCESS;
  245. } else {
  246. ULONG toCopy = (ULONG)std::min<size_t>(self->m_todo, *ulLength);
  247. if (toCopy > 0) {
  248. MORDOR_ASSERT(self->m_writeBuffer);
  249. self->m_writeBuffer->copyOut(pbData, toCopy);
  250. }
  251. *ulLength = toCopy;
  252. self->m_todo -= toCopy;
  253. Fiber::yield();
  254. return ERROR_SUCCESS;
  255. }
  256. }
  257. }