The Pedigree Project  0.1
UnixFilesystem.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "UnixFilesystem.h"
21 #include "modules/subsys/posix/logging.h"
22 #include "pedigree/kernel/LockGuard.h"
23 #include "pedigree/kernel/process/Mutex.h"
24 #include "pedigree/kernel/process/Process.h"
25 #include "pedigree/kernel/process/Thread.h"
26 #include "pedigree/kernel/processor/Processor.h"
27 
28 String UnixFilesystem::m_VolumeLabel("unix");
29 
30 UnixSocket::UnixSocket(
31  String name, Filesystem *pFs, File *pParent, UnixSocket *other,
32  SocketType type)
33  : File(name, 0, 0, 0, 0, pFs, 0, pParent), m_Type(type), m_State(Inactive),
34  m_Datagrams(MAX_UNIX_DGRAM_BACKLOG), m_pOther(other),
35  m_Stream(MAX_UNIX_STREAM_QUEUE), m_PendingSockets(), m_Mutex(false)
36 #ifdef THREADS
37  ,
38  m_AckWaiter(0)
39 #endif
40  ,
41  m_Creds()
42 {
43  if (m_Type == Datagram)
44  {
45  // Datagram sockets are always active, they don't bind to each other.
46  m_State = Active;
47  }
48 
49  m_Creds.uid = -1;
50  m_Creds.gid = -1;
51  m_Creds.pid = -1;
52 }
53 
55 {
56  // unbind from the other side of our connection if needed
57  if (m_Type == Streaming)
58  {
59  if (m_pOther)
60  {
61  LockGuard<Mutex> guard(m_pOther->m_Mutex);
62 
65  assert(m_pOther->m_pOther == this);
66  m_pOther->m_pOther = nullptr;
67  m_pOther->m_State = Inactive;
68  }
69  }
70 
71  // remove name on disk that points to us
72  if (getName().length() > 0)
73  {
74  Directory *parent = Directory::fromFile(getParent());
75  parent->remove(getName());
76  }
77 }
78 
79 int UnixSocket::select(bool bWriting, int timeout)
80 {
81  if (m_Type == Streaming)
82  {
83  if (m_State == Inactive || m_State == Connecting)
84  {
85  return false;
86  }
87 
88  if (bWriting)
89  {
90  if (m_pOther->m_Stream.canWrite(timeout == 1))
91  {
92  return true;
93  }
94  }
95  else
96  {
97  if (m_Stream.canRead(timeout == 1))
98  {
99  return true;
100  }
101  }
102 
103  return false;
104  }
105  else
106  {
107  if (timeout)
108  {
109  return m_Datagrams.waitFor(
110  bWriting ? RingBufferWait::Writing : RingBufferWait::Reading);
111  }
112  else if (bWriting)
113  {
114  return m_Datagrams.canWrite();
115  }
116  else
117  {
118  return m_Datagrams.dataReady();
119  }
120  }
121 }
122 
124  uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock)
125 {
126  String remote;
127  return recvfrom(size, buffer, bCanBlock, remote);
128 }
129 
130 uint64_t UnixSocket::recvfrom(
131  uint64_t size, uintptr_t buffer, bool bCanBlock, String &from)
132 {
133  if (m_State != Active)
134  {
135  // attempt a read if at all possible to clear out remainder of socket
136  // but non-blocking so we return 0 on true EOF
137  N_NOTICE("UnixSocket::read => EOF (reading remainder of stream first)");
138  return m_Stream.read(reinterpret_cast<uint8_t *>(buffer), size, false);
139  }
140 
141  if (m_pOther)
142  {
143  from = String();
144  return m_Stream.read(
145  reinterpret_cast<uint8_t *>(buffer), size, bCanBlock);
146  }
147 
148  if (bCanBlock)
149  {
150  if (!select(false, 1))
151  {
152  return 0; // Interrupted
153  }
154  }
155  else if (!select(false, 0))
156  {
157  // No data available.
158  return 0;
159  }
160 
161  struct buf *b = m_Datagrams.read();
162  if (size > b->len)
163  size = b->len;
164  MemoryCopy(reinterpret_cast<void *>(buffer), b->pBuffer, size);
165  if (b->remotePath)
166  {
167  from = b->remotePath;
168  delete[] b->remotePath;
169  }
170  delete[] b->pBuffer;
171  delete b;
172 
173  return size;
174 }
175 
177  uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock)
178 {
179  if (m_State != Active)
180  {
181  // other side has gone away, EOF
182  N_NOTICE("UnixSocket::write => EOF");
183  return 0;
184  }
185 
186  if (m_pOther)
187  {
188  return m_pOther->m_Stream.write(
189  reinterpret_cast<uint8_t *>(buffer), size, bCanBlock);
190  }
191 
192  if (bCanBlock)
193  {
194  if (!select(true, 1))
195  {
196  return 0; // Interrupted
197  }
198  }
199  else if (!select(true, 0))
200  {
201  // No data available.
202  return 0;
203  }
204 
205  struct buf *b = new struct buf;
206  b->pBuffer = new char[size];
207  MemoryCopy(b->pBuffer, reinterpret_cast<void *>(buffer), size);
208  b->len = size;
209  b->remotePath = 0;
210  if (location)
211  {
212  b->remotePath = new char[255];
213  StringCopyN(b->remotePath, reinterpret_cast<char *>(location), 255);
214  }
215  m_Datagrams.write(b);
216 
217  dataChanged();
218 
219  return size;
220 }
221 
222 bool UnixSocket::bind(UnixSocket *other, bool block)
223 {
224  if (other->m_pOther)
225  {
226  ERROR("UnixSocket: trying to bind a socket that's already bound");
227  return false;
228  }
229 
230  {
231  LockGuard<Mutex> guard1(m_Mutex);
232  if (m_State != Inactive)
233  {
234  N_NOTICE("bind failed because this socket is already in a "
235  "non-inactive state");
236  return false;
237  }
238 
239  m_pOther = other;
240 
241  LockGuard<Mutex> guard2(m_pOther->m_Mutex);
242  if (m_pOther->m_State != Inactive)
243  {
244  N_NOTICE("bind failed because other socket is already in a "
245  "non-inactive state");
246  m_pOther = nullptr;
247  return false;
248  }
249 
250  other->m_pOther = this;
251 
252  m_State = Connecting;
253  m_pOther->m_State = Connecting;
254 
255  setCreds();
256  }
257 
258  if (!block)
259  {
260  N_NOTICE("bind is not blocking, use poll() etc");
261  return true;
262  }
263 
264 #ifdef THREADS
265  N_NOTICE("bind is waiting for an ack");
266  m_AckWaiter.acquire();
267 
268  if (m_State != Active)
269  {
270  N_NOTICE("got ack but we're inactive");
271  return false;
272  }
273 
274  N_NOTICE("got ack and we're now active");
275 #endif
276 
277  return true;
278 }
279 
280 void UnixSocket::unbind()
281 {
282  LockGuard<Mutex> guard1(m_Mutex);
283 
284  if (!m_pOther)
285  {
286  return;
287  }
288 
289  LockGuard<Mutex> guard2(m_pOther->m_Mutex);
290 
291  N_NOTICE("UnixSocket::unbind");
292 
293  m_State = Closed;
294  m_pOther->m_State = Closed;
295 
296 #ifdef THREADS
297  m_AckWaiter.release();
298  m_pOther->m_AckWaiter.release();
299 #endif
300 
301  if (m_Type == Streaming)
302  {
303  N_NOTICE("streaming notify eof");
304 
305  // notify anything waiting on this socket that we're shutting down
306  m_Stream.notifyMonitors();
307  m_pOther->m_Stream.notifyMonitors();
308  }
309 }
310 
311 void UnixSocket::acknowledgeBind()
312 {
313  LockGuard<Mutex> guard1(m_Mutex);
314 
315  if (!m_pOther)
316  {
317  return;
318  }
319 
320  LockGuard<Mutex> guard2(m_pOther->m_Mutex);
321 
322  if (m_State != Connecting || m_pOther->m_State != Connecting)
323  {
324  N_NOTICE("can't ack bind - one or both sockets are not connecting");
325  return;
326  }
327 
328  N_NOTICE("acking bind");
329 
330  m_State = Active;
331  m_pOther->m_State = Active;
332 
333  setCreds();
334 
335 #ifdef THREADS
336  m_AckWaiter.release();
337  m_pOther->m_AckWaiter.release();
338 #endif
339 }
340 
341 void UnixSocket::addSocket(UnixSocket *socket)
342 {
343  LockGuard<Mutex> guard(m_Mutex);
344 
345  if (m_State != Listening)
346  {
347  // not listening
348  return;
349  }
350 
351  m_PendingSockets.pushBack(socket);
352 
353  N_NOTICE("adding listening socket");
354 
355  // No data moving on listen sockets so we use the stream buffer as a
356  // signaling primitive.
357  uint8_t c = 0;
358  m_Stream.write(&c, 1);
359 }
360 
361 UnixSocket *UnixSocket::getSocket(bool block)
362 {
363  if (m_State != Listening)
364  {
365  // not listening
366  return nullptr;
367  }
368 
369  uint8_t c = 0;
370  if (m_Stream.read(&c, 1, block) != 1)
371  {
372  return nullptr;
373  }
374 
375  N_NOTICE("got a socket");
376 
377  LockGuard<Mutex> guard(m_Mutex);
378 
379  N_NOTICE("popping & acking it");
380 
381  UnixSocket *result = m_PendingSockets.popFront();
382  result->acknowledgeBind();
383  return result;
384 }
385 
386 void UnixSocket::addWaiter(Semaphore *waiter)
387 {
388  m_Stream.monitor(waiter);
389  if (m_pOther)
390  {
391  m_pOther->m_Stream.monitor(waiter);
392  }
393 }
394 
395 void UnixSocket::removeWaiter(Semaphore *waiter)
396 {
397  m_Stream.cullMonitorTargets(waiter);
398  if (m_pOther)
399  {
400  m_pOther->m_Stream.cullMonitorTargets(waiter);
401  }
402 }
403 
404 void UnixSocket::addWaiter(Thread *thread, Event *event)
405 {
406  m_Stream.monitor(thread, event);
407  if (m_pOther)
408  {
409  m_pOther->m_Stream.monitor(thread, event);
410  }
411 }
412 
413 void UnixSocket::removeWaiter(Event *event)
414 {
415  m_Stream.cullMonitorTargets(event);
416  if (m_pOther)
417  {
418  m_pOther->m_Stream.cullMonitorTargets(event);
419  }
420 }
421 
422 bool UnixSocket::markListening()
423 {
424  if (m_Type != Streaming)
425  {
426  // can't listen() on a non-streaming socket
427  return false;
428  }
429 
430  if (m_State != Inactive)
431  {
432  // can't listen on a bound socket
433  return false;
434  }
435 
436  m_State = Listening;
437  return true;
438 }
439 
440 void UnixSocket::setCreds()
441 {
442 #ifdef THREADS
443  Process *pCurrentProcess =
444  Processor::information().getCurrentThread()->getParent();
445  m_Creds.uid = pCurrentProcess->getUserId();
446  m_Creds.gid = pCurrentProcess->getGroupId();
447  m_Creds.pid = pCurrentProcess->getId();
448 #endif
449 }
450 
451 UnixDirectory::UnixDirectory(String name, Filesystem *pFs, File *pParent)
452  : Directory(name, 0, 0, 0, 0, pFs, 0, pParent), m_Lock(false)
453 {
454  cacheDirectoryContents();
455 }
456 
457 UnixDirectory::~UnixDirectory()
458 {
459 }
460 
461 bool UnixDirectory::addEntry(String filename, File *pFile)
462 {
463  LockGuard<Mutex> guard(m_Lock);
464  addDirectoryEntry(filename, pFile);
465  return true;
466 }
467 
468 bool UnixDirectory::removeEntry(File *pFile)
469 {
470  String filename = pFile->getName();
471 
472  LockGuard<Mutex> guard(m_Lock);
473  remove(filename.view());
474  return true;
475 }
476 
478 {
479  markCachePopulated();
480 }
481 
482 UnixFilesystem::UnixFilesystem() : Filesystem(), m_pRoot(0)
483 {
484  UnixDirectory *pRoot = new UnixDirectory(String(""), this, 0);
485  pRoot->addEntry(String("."), pRoot);
486  pRoot->addEntry(String(".."), pRoot);
487 
488  m_pRoot = pRoot;
489 
490  // allow owner/group rwx but others only r-x on the filesystem root
491  m_pRoot->setPermissions(
492  FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GW | FILE_GX | FILE_OR |
493  FILE_OX);
494 }
495 
496 UnixFilesystem::~UnixFilesystem()
497 {
498  delete m_pRoot;
499 }
500 
502  File *parent, const String &filename, uint32_t mask)
503 {
504  UnixDirectory *pParent =
505  static_cast<UnixDirectory *>(Directory::fromFile(parent));
506 
507  UnixSocket *pSocket = new UnixSocket(filename, this, parent);
508  if (!pParent->addEntry(filename, pSocket))
509  {
510  delete pSocket;
511  return false;
512  }
513 
514  // give owner/group full permission to the socket by default
515  pSocket->setPermissions(
516  FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GW | FILE_GX | FILE_OR |
517  FILE_OX);
518 
519  return true;
520 }
521 
523  File *parent, const String &filename, uint32_t mask)
524 {
525  UnixDirectory *pParent =
526  static_cast<UnixDirectory *>(Directory::fromFile(parent));
527 
528  UnixDirectory *pChild = new UnixDirectory(filename, this, parent);
529  if (!pParent->addEntry(filename, pChild))
530  {
531  delete pChild;
532  return false;
533  }
534 
535  pChild->addEntry(String("."), pChild);
536  pChild->addEntry(String(".."), pParent);
537 
538  // give owner/group full permission to the directory by default
539  pChild->setPermissions(
540  FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GW | FILE_GX | FILE_OR |
541  FILE_OX);
542 
543  return true;
544 }
545 
546 bool UnixFilesystem::remove(File *parent, File *file)
547 {
548  UnixDirectory *pParent =
549  static_cast<UnixDirectory *>(Directory::fromFile(parent));
550  return pParent->removeEntry(file);
551 }
virtual bool createDirectory(File *parent, const String &filename, uint32_t mask)
virtual bool createFile(File *parent, const String &filename, uint32_t mask)
size_t getId()
Definition: Process.h:108
virtual bool remove(File *parent, File *file)
static Directory * fromFile(File *pF)
Definition: Directory.h:50
virtual int64_t getUserId() const
Definition: Process.cc:320
Definition: String.h:49
static ProcessorInformation & information()
Definition: Processor.cc:45
StringView view() const
Definition: String.cc:880
virtual void cacheDirectoryContents()
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
#define assert(x)
Definition: assert.h:37
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual ~UnixSocket()
String getName() const
Definition: File.cc:411
Definition: Thread.h:54
Definition: Event.h:48
void remove(const HashedStringView &s)
Definition: Directory.cc:102
#define ERROR(text)
Definition: Log.h:82
Definition: File.h:66
virtual int select(bool bWriting=false, int timeout=0)