1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "IOInterposer.h"
8 #include "NSPRInterposer.h"
11 #include "private/pprio.h"
15 #include <sys/param.h>
25 /* Original IO methods */
26 PRCloseFN sCloseFn
= nullptr;
27 PRReadFN sReadFn
= nullptr;
28 PRWriteFN sWriteFn
= nullptr;
29 PRFsyncFN sFSyncFn
= nullptr;
30 PRFileInfoFN sFileInfoFn
= nullptr;
31 PRFileInfo64FN sFileInfo64Fn
= nullptr;
33 static int32_t GetPathFromFd(int32_t aFd
, char* aBuf
, size_t aBufSize
) {
35 NS_ASSERTION(aBufSize
>= MAXPATHLEN
,
36 "aBufSize should be a least MAXPATHLEN long");
38 return fcntl(aFd
, F_GETPATH
, aBuf
);
41 if (PR_snprintf(procPath
, sizeof(procPath
), "/proc/self/fd/%i", aFd
) ==
46 int32_t ret
= readlink(procPath
, aBuf
, aBufSize
- 1);
56 * RAII class for timing the duration of an NSPR I/O call and reporting the
57 * result to the mozilla::IOInterposeObserver API.
59 class NSPRIOAutoObservation
: public mozilla::IOInterposeObserver::Observation
{
61 explicit NSPRIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp
,
63 : mozilla::IOInterposeObserver::Observation(aOp
, "NSPRIOInterposer") {
64 char filename
[MAXPATHLEN
];
65 if (mShouldReport
&& aFd
&&
66 GetPathFromFd(PR_FileDesc2NativeHandle(aFd
), filename
,
67 sizeof(filename
)) != -1) {
68 CopyUTF8toUTF16(mozilla::MakeStringSpan(filename
), mFilename
);
74 void Filename(nsAString
& aFilename
) override
{ aFilename
= mFilename
; }
76 ~NSPRIOAutoObservation() override
{ Report(); }
82 PRStatus PR_CALLBACK
interposedClose(PRFileDesc
* aFd
) {
83 // If we don't have a valid original function pointer something is very wrong.
84 NS_ASSERTION(sCloseFn
, "NSPR IO Interposing: sCloseFn is NULL");
86 NSPRIOAutoObservation
timer(mozilla::IOInterposeObserver::OpClose
, aFd
);
90 int32_t PR_CALLBACK
interposedRead(PRFileDesc
* aFd
, void* aBuf
, int32_t aAmt
) {
91 // If we don't have a valid original function pointer something is very wrong.
92 NS_ASSERTION(sReadFn
, "NSPR IO Interposing: sReadFn is NULL");
94 NSPRIOAutoObservation
timer(mozilla::IOInterposeObserver::OpRead
, aFd
);
95 return sReadFn(aFd
, aBuf
, aAmt
);
98 int32_t PR_CALLBACK
interposedWrite(PRFileDesc
* aFd
, const void* aBuf
,
100 // If we don't have a valid original function pointer something is very wrong.
101 NS_ASSERTION(sWriteFn
, "NSPR IO Interposing: sWriteFn is NULL");
103 NSPRIOAutoObservation
timer(mozilla::IOInterposeObserver::OpWrite
, aFd
);
104 return sWriteFn(aFd
, aBuf
, aAmt
);
107 PRStatus PR_CALLBACK
interposedFSync(PRFileDesc
* aFd
) {
108 // If we don't have a valid original function pointer something is very wrong.
109 NS_ASSERTION(sFSyncFn
, "NSPR IO Interposing: sFSyncFn is NULL");
111 NSPRIOAutoObservation
timer(mozilla::IOInterposeObserver::OpFSync
, aFd
);
112 return sFSyncFn(aFd
);
115 PRStatus PR_CALLBACK
interposedFileInfo(PRFileDesc
* aFd
, PRFileInfo
* aInfo
) {
116 // If we don't have a valid original function pointer something is very wrong.
117 NS_ASSERTION(sFileInfoFn
, "NSPR IO Interposing: sFileInfoFn is NULL");
119 NSPRIOAutoObservation
timer(mozilla::IOInterposeObserver::OpStat
, aFd
);
120 return sFileInfoFn(aFd
, aInfo
);
123 PRStatus PR_CALLBACK
interposedFileInfo64(PRFileDesc
* aFd
,
124 PRFileInfo64
* aInfo
) {
125 // If we don't have a valid original function pointer something is very wrong.
126 NS_ASSERTION(sFileInfo64Fn
, "NSPR IO Interposing: sFileInfo64Fn is NULL");
128 NSPRIOAutoObservation
timer(mozilla::IOInterposeObserver::OpStat
, aFd
);
129 return sFileInfo64Fn(aFd
, aInfo
);
136 void InitNSPRIOInterposing() {
137 // Check that we have not interposed any of the IO methods before
138 MOZ_ASSERT(!sCloseFn
&& !sReadFn
&& !sWriteFn
&& !sFSyncFn
&& !sFileInfoFn
&&
141 // We can't actually use this assertion because we initialize this code
142 // before XPCOM is initialized, so NS_IsMainThread() always returns false.
143 // MOZ_ASSERT(NS_IsMainThread());
145 // Get IO methods from NSPR and const cast the structure so we can modify it.
146 PRIOMethods
* methods
= const_cast<PRIOMethods
*>(PR_GetFileMethods());
148 // Something is badly wrong if we don't get IO methods... However, we don't
149 // want to crash over that in non-debug builds. This is unlikely to happen
150 // so an assert is enough, no need to report it to the caller.
156 // Store original functions
157 sCloseFn
= methods
->close
;
158 sReadFn
= methods
->read
;
159 sWriteFn
= methods
->write
;
160 sFSyncFn
= methods
->fsync
;
161 sFileInfoFn
= methods
->fileInfo
;
162 sFileInfo64Fn
= methods
->fileInfo64
;
164 // Overwrite with our interposed functions
165 methods
->close
= &interposedClose
;
166 methods
->read
= &interposedRead
;
167 methods
->write
= &interposedWrite
;
168 methods
->fsync
= &interposedFSync
;
169 methods
->fileInfo
= &interposedFileInfo
;
170 methods
->fileInfo64
= &interposedFileInfo64
;
173 void ClearNSPRIOInterposing() {
174 // If we have already cleared IO interposing, or not initialized it this is
176 MOZ_ASSERT(sCloseFn
&& sReadFn
&& sWriteFn
&& sFSyncFn
&& sFileInfoFn
&&
179 // Get IO methods from NSPR and const cast the structure so we can modify it.
180 PRIOMethods
* methods
= const_cast<PRIOMethods
*>(PR_GetFileMethods());
182 // Something is badly wrong if we don't get IO methods... However, we don't
183 // want to crash over that in non-debug builds. This is unlikely to happen
184 // so an assert is enough, no need to report it to the caller.
190 // Restore original functions
191 methods
->close
= sCloseFn
;
192 methods
->read
= sReadFn
;
193 methods
->write
= sWriteFn
;
194 methods
->fsync
= sFSyncFn
;
195 methods
->fileInfo
= sFileInfoFn
;
196 methods
->fileInfo64
= sFileInfo64Fn
;
198 // Forget about original functions
203 sFileInfoFn
= nullptr;
204 sFileInfo64Fn
= nullptr;
207 } // namespace mozilla