Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsTemporaryFileInputStream.h"
7 : #include "nsStreamUtils.h"
8 : #include "mozilla/ipc/InputStreamUtils.h"
9 : #include "private/pprio.h"
10 : #include <algorithm>
11 :
12 : using namespace mozilla;
13 : using namespace mozilla::ipc;
14 :
15 : typedef mozilla::ipc::FileDescriptor::PlatformHandleType FileHandleType;
16 :
17 0 : NS_IMPL_ISUPPORTS(nsTemporaryFileInputStream,
18 : nsIInputStream,
19 : nsISeekableStream,
20 : nsIIPCSerializableInputStream)
21 :
22 0 : nsTemporaryFileInputStream::nsTemporaryFileInputStream(FileDescOwner* aFileDescOwner, uint64_t aStartPos, uint64_t aEndPos)
23 : : mFileDescOwner(aFileDescOwner),
24 : mStartPos(aStartPos),
25 : mCurPos(aStartPos),
26 : mEndPos(aEndPos),
27 0 : mClosed(false)
28 : {
29 0 : NS_ASSERTION(aStartPos <= aEndPos, "StartPos should less equal than EndPos!");
30 0 : }
31 :
32 0 : nsTemporaryFileInputStream::nsTemporaryFileInputStream()
33 : : mStartPos(0),
34 : mCurPos(0),
35 : mEndPos(0),
36 0 : mClosed(false)
37 : {
38 0 : }
39 :
40 : NS_IMETHODIMP
41 0 : nsTemporaryFileInputStream::Close()
42 : {
43 0 : mClosed = true;
44 0 : return NS_OK;
45 : }
46 :
47 : NS_IMETHODIMP
48 0 : nsTemporaryFileInputStream::Available(uint64_t * bytesAvailable)
49 : {
50 0 : if (mClosed)
51 0 : return NS_BASE_STREAM_CLOSED;
52 :
53 0 : NS_ASSERTION(mCurPos <= mEndPos, "CurPos should less equal than EndPos!");
54 :
55 0 : *bytesAvailable = mEndPos - mCurPos;
56 0 : return NS_OK;
57 : }
58 :
59 : NS_IMETHODIMP
60 0 : nsTemporaryFileInputStream::Read(char* buffer, uint32_t count, uint32_t* bytesRead)
61 : {
62 0 : return ReadSegments(NS_CopySegmentToBuffer, buffer, count, bytesRead);
63 : }
64 :
65 : NS_IMETHODIMP
66 0 : nsTemporaryFileInputStream::ReadSegments(nsWriteSegmentFun writer,
67 : void * closure,
68 : uint32_t count,
69 : uint32_t * result)
70 : {
71 0 : NS_ASSERTION(result, "null ptr");
72 0 : NS_ASSERTION(mCurPos <= mEndPos, "bad stream state");
73 0 : *result = 0;
74 :
75 0 : if (mClosed) {
76 0 : return NS_BASE_STREAM_CLOSED;
77 : }
78 :
79 0 : mozilla::MutexAutoLock lock(mFileDescOwner->FileMutex());
80 0 : int64_t offset = PR_Seek64(mFileDescOwner->mFD, mCurPos, PR_SEEK_SET);
81 0 : if (offset == -1) {
82 0 : return NS_ErrorAccordingToNSPR();
83 : }
84 :
85 : // Limit requested count to the amount remaining in our section of the file.
86 0 : count = std::min(count, uint32_t(mEndPos - mCurPos));
87 :
88 : char buf[4096];
89 0 : while (*result < count) {
90 0 : uint32_t bufCount = std::min(count - *result, (uint32_t) sizeof(buf));
91 0 : int32_t bytesRead = PR_Read(mFileDescOwner->mFD, buf, bufCount);
92 0 : if (bytesRead == 0) {
93 0 : mClosed = true;
94 0 : return NS_OK;
95 : }
96 :
97 0 : if (bytesRead < 0) {
98 0 : return NS_ErrorAccordingToNSPR();
99 : }
100 :
101 0 : int32_t bytesWritten = 0;
102 0 : while (bytesWritten < bytesRead) {
103 0 : uint32_t writerCount = 0;
104 0 : nsresult rv = writer(this, closure, buf + bytesWritten, *result,
105 0 : bytesRead - bytesWritten, &writerCount);
106 0 : if (NS_FAILED(rv) || writerCount == 0) {
107 : // nsIInputStream::ReadSegments' contract specifies that errors
108 : // from writer are not propagated to ReadSegments' caller.
109 : //
110 : // If writer fails, leaving bytes still in buf, that's okay: we
111 : // only update mCurPos to reflect successful writes, so the call
112 : // to PR_Seek64 at the top will restart us at the right spot.
113 0 : return NS_OK;
114 : }
115 0 : NS_ASSERTION(writerCount <= (uint32_t) (bytesRead - bytesWritten),
116 : "writer should not write more than we asked it to write");
117 0 : bytesWritten += writerCount;
118 0 : *result += writerCount;
119 0 : mCurPos += writerCount;
120 : }
121 : }
122 :
123 0 : return NS_OK;
124 : }
125 :
126 : NS_IMETHODIMP
127 0 : nsTemporaryFileInputStream::IsNonBlocking(bool * nonBlocking)
128 : {
129 0 : *nonBlocking = false;
130 0 : return NS_OK;
131 : }
132 :
133 : NS_IMETHODIMP
134 0 : nsTemporaryFileInputStream::Seek(int32_t aWhence, int64_t aOffset)
135 : {
136 0 : if (mClosed) {
137 0 : return NS_BASE_STREAM_CLOSED;
138 : }
139 :
140 0 : switch (aWhence) {
141 : case nsISeekableStream::NS_SEEK_SET:
142 0 : aOffset += mStartPos;
143 0 : break;
144 :
145 : case nsISeekableStream::NS_SEEK_CUR:
146 0 : aOffset += mCurPos;
147 0 : break;
148 :
149 : case nsISeekableStream::NS_SEEK_END:
150 0 : aOffset += mEndPos;
151 0 : break;
152 :
153 : default:
154 0 : return NS_ERROR_FAILURE;
155 : }
156 :
157 0 : if (aOffset < (int64_t)mStartPos || aOffset > (int64_t)mEndPos) {
158 0 : return NS_ERROR_INVALID_ARG;
159 : }
160 :
161 0 : mCurPos = aOffset;
162 0 : return NS_OK;
163 : }
164 :
165 : NS_IMETHODIMP
166 0 : nsTemporaryFileInputStream::Tell(int64_t* aPos)
167 : {
168 0 : if (!aPos) {
169 0 : return NS_ERROR_FAILURE;
170 : }
171 :
172 0 : if (mClosed) {
173 0 : return NS_BASE_STREAM_CLOSED;
174 : }
175 :
176 0 : MOZ_ASSERT(mStartPos <= mCurPos, "StartPos should less equal than CurPos!");
177 0 : *aPos = mCurPos - mStartPos;
178 0 : return NS_OK;
179 : }
180 :
181 : NS_IMETHODIMP
182 0 : nsTemporaryFileInputStream::SetEOF()
183 : {
184 0 : if (mClosed) {
185 0 : return NS_BASE_STREAM_CLOSED;
186 : }
187 :
188 0 : return Close();
189 : }
190 :
191 : void
192 0 : nsTemporaryFileInputStream::Serialize(InputStreamParams& aParams,
193 : FileDescriptorArray& aFileDescriptors)
194 : {
195 0 : TemporaryFileInputStreamParams params;
196 :
197 0 : MutexAutoLock lock(mFileDescOwner->FileMutex());
198 0 : MOZ_ASSERT(mFileDescOwner->mFD);
199 0 : if (!mClosed) {
200 0 : FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFileDescOwner->mFD));
201 0 : NS_ASSERTION(fd, "This should never be null!");
202 :
203 0 : DebugOnly<FileDescriptor*> dbgFD = aFileDescriptors.AppendElement(fd);
204 0 : NS_ASSERTION(dbgFD->IsValid(), "Sending an invalid file descriptor!");
205 :
206 0 : params.fileDescriptorIndex() = aFileDescriptors.Length() - 1;
207 :
208 0 : Close();
209 : } else {
210 : NS_WARNING("The stream is already closed. "
211 0 : "Sending an invalid file descriptor to the other process!");
212 :
213 0 : params.fileDescriptorIndex() = UINT32_MAX;
214 : }
215 0 : params.startPos() = mCurPos;
216 0 : params.endPos() = mEndPos;
217 0 : aParams = params;
218 0 : }
219 :
220 : bool
221 0 : nsTemporaryFileInputStream::Deserialize(const InputStreamParams& aParams,
222 : const FileDescriptorArray& aFileDescriptors)
223 : {
224 0 : const TemporaryFileInputStreamParams& params = aParams.get_TemporaryFileInputStreamParams();
225 :
226 0 : uint32_t fileDescriptorIndex = params.fileDescriptorIndex();
227 0 : FileDescriptor fd;
228 0 : if (fileDescriptorIndex < aFileDescriptors.Length()) {
229 0 : fd = aFileDescriptors[fileDescriptorIndex];
230 0 : NS_WARNING_ASSERTION(fd.IsValid(),
231 : "Received an invalid file descriptor!");
232 : } else {
233 0 : NS_WARNING("Received a bad file descriptor index!");
234 : }
235 :
236 0 : if (fd.IsValid()) {
237 0 : auto rawFD = fd.ClonePlatformHandle();
238 0 : PRFileDesc* fileDesc = PR_ImportFile(PROsfd(rawFD.release()));
239 0 : if (!fileDesc) {
240 0 : NS_WARNING("Failed to import file handle!");
241 0 : return false;
242 : }
243 0 : mFileDescOwner = new FileDescOwner(fileDesc);
244 : } else {
245 0 : mClosed = true;
246 : }
247 :
248 0 : mStartPos = mCurPos = params.startPos();
249 0 : mEndPos = params.endPos();
250 0 : return true;
251 : }
252 :
253 : Maybe<uint64_t>
254 0 : nsTemporaryFileInputStream::ExpectedSerializedLength()
255 : {
256 0 : return Nothing();
257 : }
|