PageRenderTime 204ms CodeModel.GetById 80ms app.highlight 69ms RepoModel.GetById 52ms app.codeStats 0ms

/mordor/streams/efs.cpp

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