aboutsummaryrefslogtreecommitdiff
path: root/winsup/cygwin/cygheap.h
blob: c394e7fd240a9ec75ef78178cb98635686ca0ac8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
/* cygheap.h: Cygwin heap manager.

   Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
   2011, 2012, 2013, 2014, 2015 Red Hat, Inc.

This file is part of Cygwin.

This software is a copyrighted work licensed under the terms of the
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
details. */

#include "hires.h"
#include "cygheap_malloc.h"
#include "pwdgrp.h"

#define incygheap(s) (cygheap && ((char *) (s) >= (char *) cygheap) && ((char *) (s) <= ((char *) cygheap_max)))

struct _cmalloc_entry
{
  union
  {
    unsigned b;
    char *ptr;
  };
  struct _cmalloc_entry *prev;
  char data[0];
};

struct cygheap_root_mount_info
{
  char posix_path[CYG_MAX_PATH];
  unsigned posix_pathlen;
  char native_path[CYG_MAX_PATH];
  unsigned native_pathlen;
  bool caseinsensitive;
};

/* CGF: FIXME This doesn't belong here */

class cygheap_root
{
  /* Root directory information.
     This is used after a chroot is called. */
  struct cygheap_root_mount_info *m;

public:
  bool posix_ok (const char *path)
  {
    if (!m)
      return 1;
    return path_prefix_p (m->posix_path, path, m->posix_pathlen,
			  m->caseinsensitive);
  }
  bool ischroot_native (const char *path)
  {
    if (!m)
      return 1;
    return strncasematch (m->native_path, path, m->native_pathlen)
	    && (path[m->native_pathlen] == '\\' || !path[m->native_pathlen]);
  }
  const char *unchroot (const char *path)
  {
    if (!m)
      return path;
    const char *p = path + m->posix_pathlen;
    if (!*p)
      p = "/";
    return p;
  }
  bool exists () {return !!m;}
  void set (const char *, const char *, bool);
  size_t posix_length () const { return m->posix_pathlen; }
  const char *posix_path () const { return m->posix_path; }
  size_t native_length () const { return m->native_pathlen; }
  const char *native_path () const { return m->native_path; }
};

enum homebodies
{
  CH_HOMEDRIVE,
  CH_HOMEPATH,
  CH_HOME
};

class cygheap_user
{
  /* Extendend user information.
     The information is derived from the internal_getlogin call
     when on a NT system. */
  char  *pname;		/* user's name */
  char  *plogsrv;       /* Logon server, may be FQDN */
  char  *pdomain;       /* Logon domain of the user */
  char  *homedrive;	/* User's home drive */
  char  *homepath;	/* User's home path */
  char  *psystemroot;	/* Value of SYSTEMROOT */
  char  *pwinname;	/* User's name as far as Windows knows it */
  char  *puserprof;	/* User profile */
  cygsid effec_cygsid;  /* buffer for user's SID */
  cygsid saved_cygsid;  /* Remains intact even after impersonation */
public:
  uid_t saved_uid;      /* Remains intact even after impersonation */
  gid_t saved_gid;      /* Ditto */
  uid_t real_uid;       /* Remains intact on seteuid, replaced by setuid */
  gid_t real_gid;       /* Ditto */
  user_groups groups;   /* Primary and supp SIDs */

  /* token is needed if set(e)uid should be called. It can be set by a call
     to `set_impersonation_token()'. */
  HANDLE external_token;
  HANDLE internal_token;
  HANDLE curr_primary_token;
  HANDLE curr_imp_token;
  bool ext_token_is_restricted;  /* external_token is restricted token */
  bool curr_token_is_restricted; /* curr_primary_token is restricted token */
  bool setuid_to_restricted;     /* switch to restricted token by setuid () */

  /* CGF 2002-06-27.  I removed the initializaton from this constructor
     since this class is always allocated statically.  That means that everything
     is zero anyway so there is no need to initialize it to zero.  Since the
     token initialization is always handled during process startup as well,
     I've removed the constructor entirely.  Please reinstate this if this
     situation ever changes.
  cygheap_user () : pname (NULL), plogsrv (NULL), pdomain (NULL),
		    homedrive (NULL), homepath (NULL),
		    token (INVALID_HANDLE_VALUE) {}
  */

  ~cygheap_user ();

  void init ();
  void set_name (const char *new_name);
  const char *name () const { return pname; }

  const char *env_logsrv (const char *, size_t);
  const char *env_homepath (const char *, size_t);
  const char *env_homedrive (const char *, size_t);
  const char *env_userprofile (const char *, size_t);
  const char *env_domain (const char *, size_t);
  const char *env_name (const char *, size_t);
  const char *env_systemroot (const char *, size_t);

  const char *logsrv ()
  {
    const char *p = env_logsrv ("LOGONSERVER=", sizeof ("LOGONSERVER=") - 1);
    return (p == almost_null) ? NULL : p;
  }
  const char *winname ()
  {
    const char *p = env_name ("USERNAME=", sizeof ("USERNAME=") - 1);
    return (p == almost_null) ? NULL : p;
  }
  const char *domain ()
  {
    const char *p = env_domain ("USERDOMAIN=", sizeof ("USERDOMAIN=") - 1);
    return (p == almost_null) ? NULL : p;
  }
  void set_sid (PSID new_sid) { effec_cygsid = new_sid;}
  void set_saved_sid () { saved_cygsid = effec_cygsid; }
  cygpsid &sid () { return effec_cygsid; }
  cygpsid &saved_sid () { return saved_cygsid; }
  const char *ontherange (homebodies what, struct passwd * = NULL);
#define NO_IMPERSONATION NULL
  bool issetuid () const { return curr_imp_token != NO_IMPERSONATION; }
  HANDLE primary_token () { return curr_primary_token; }
  HANDLE imp_token () { return curr_imp_token; }
  void deimpersonate ()
  {
    RevertToSelf ();
  }
  bool reimpersonate ()
  {
    if (issetuid ())
      return ImpersonateLoggedOnUser (primary_token ());
    return true;
  }
  bool has_impersonation_tokens ()
    { return external_token != NO_IMPERSONATION
	     || internal_token != NO_IMPERSONATION
	     || curr_primary_token != NO_IMPERSONATION; }
  void close_impersonation_tokens ()
  {
    if (curr_imp_token != NO_IMPERSONATION)
      CloseHandle (curr_imp_token);
    if (curr_primary_token != NO_IMPERSONATION
	&& curr_primary_token != external_token
	&& curr_primary_token != internal_token)
      CloseHandle (curr_primary_token);
    if (external_token != NO_IMPERSONATION)
      CloseHandle (external_token);
    if (internal_token != NO_IMPERSONATION)
      CloseHandle (internal_token);
  }
  PWCHAR get_windows_id (PWCHAR buf)
  {
    return effec_cygsid.string (buf);
  }
  char *get_windows_id (char *buf)
  {
    return effec_cygsid.string (buf);
  }

  const char __reg3 *test_uid (char *&, const char *, size_t);
};

/* cwd cache stuff.  */

enum fcwd_version_t {
  FCWD_OLD,
  FCWD_W7,
  FCWD_W8
};

/* This class is used to store the CWD starting with Windows Vista.
   The CWD storage in the RTL_USER_PROCESS_PARAMETERS block is only
   an afterthought now.  The actual CWD storage is a FAST_CWD structure
   which is allocated on the process heap.  The new method only requires
   minimal locking and it's much more multi-thread friendly.  Presumably
   it minimizes contention when accessing the CWD.
   The class fcwd_access_t is supposed to encapsulate the gory implementation
   details depending on OS version from the calling functions.
   The layout of all structures has been tested on 32 and 64 bit. */
class fcwd_access_t {
  /* This is the layout used in Windows 8. */
  struct FAST_CWD_8 {
    LONG           ReferenceCount;	/* Only release when this is 0. */
    HANDLE         DirectoryHandle;
    ULONG          OldDismountCount;	/* Reflects the system DismountCount
					   at the time the CWD has been set. */
    UNICODE_STRING Path;		/* Path's Buffer member always refers
					   to the following Buffer array. */
    LONG           FSCharacteristics;	/* Taken from FileFsDeviceInformation */
    WCHAR          Buffer[MAX_PATH] __attribute ((aligned (8)));
  };
  /* This is the layout used in Windows 7 and Vista. */
  struct FAST_CWD_7 {
    UNICODE_STRING Path;		/* Path's Buffer member always refers
					   to the following Buffer array. */
    HANDLE         DirectoryHandle;
    LONG           FSCharacteristics;	/* Taken from FileFsDeviceInformation */
    LONG           ReferenceCount;	/* Only release when this is 0. */
    ULONG          OldDismountCount;	/* Reflects the system DismountCount
					   at the time the CWD has been set. */
    WCHAR          Buffer[MAX_PATH] __attribute ((aligned (8)));
  };
  /* This is the old FAST_CWD structure up to the patch from KB 2393802,
     release in February 2011. */
  struct FAST_CWD_OLD {
    LONG           ReferenceCount;	/* Only release when this is 0. */
    HANDLE         DirectoryHandle;
    ULONG          OldDismountCount;	/* Reflects the system DismountCount
					   at the time the CWD has been set. */
    UNICODE_STRING Path;		/* Path's Buffer member always refers
					   to the following Buffer array. */
    WCHAR          Buffer[MAX_PATH];
  };
  union {
    FAST_CWD_OLD fold;
    FAST_CWD_7   f7;
    FAST_CWD_8   f8;
  };

#define IMPLEMENT(type, name) \
  type name () { \
    switch (fast_cwd_version ()) { \
      case FCWD_OLD: \
      default: \
	return fold.name; \
      case FCWD_W7: \
	return f7.name; \
      case FCWD_W8: \
	return f8.name; \
    } \
  }
  IMPLEMENT (LONG &, ReferenceCount)
  IMPLEMENT (HANDLE &, DirectoryHandle)
  IMPLEMENT (ULONG &, OldDismountCount)
  IMPLEMENT (UNICODE_STRING &, Path)
  IMPLEMENT (WCHAR *, Buffer)
  void SetFSCharacteristics (LONG val);
  static fcwd_version_t &fast_cwd_version (void);

public:
  void CopyPath (UNICODE_STRING &target);
  void Free (PVOID heap);
  void FillIn (HANDLE dir, PUNICODE_STRING name, ULONG old_dismount_count);
  static void SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir);
  static void SetVersionFromPointer (PBYTE buf_p, bool is_buffer);
};

class cwdstuff
{
private:
  char *posix;
  HANDLE dir;
  DWORD drive_length;
  int error;		/* This contains an errno number which corresponds
			   to the problem with this path when trying to start
			   a native Win32 application.  See cwdstuff::set for
			   how it gets set.  See child_info_spawn::worker for how
			   it's evaluated. */

  friend class fcwd_access_t;
  /* fast_cwd_ptr is a pointer to the global RtlpCurDirRef pointer in
     ntdll.dll pointing to the FAST_CWD structure which constitutes the CWD.
     Unfortunately RtlpCurDirRef is not exported from ntdll.dll. */
  fcwd_access_t **fast_cwd_ptr;
  /* Type of FAST_CWD used on this system.  Keeping this information
     available in shared memory avoids to test for the version every time
     around.  Default to new version. */
  fcwd_version_t fast_cwd_version;
  void override_win32_cwd (bool init, ULONG old_dismount_count);

public:
  UNICODE_STRING win32;
  static muto cwd_lock;
  const char *get_posix () const { return posix; };
  void reset_posix (wchar_t *w_cwd);
  char *get (char *buf, int need_posix = 1, int with_chroot = 0,
	     unsigned ulen = NT_MAX_PATH);
  PWCHAR get (PWCHAR buf, unsigned buflen = NT_MAX_PATH)
  {
    cwd_lock.acquire ();
    buf[0] = L'\0';
    wcsncat (buf, win32.Buffer, buflen - 1);
    cwd_lock.release ();
    return buf;
  }
  HANDLE get_handle () { return dir; }
  DWORD get_drive (char * dst)
  {
    cwd_lock.acquire ();
    DWORD ret = sys_wcstombs (dst, NT_MAX_PATH, win32.Buffer, drive_length);
    cwd_lock.release ();
    return ret;
  }
  int get_error () const { return error; }
  const char *get_error_desc () const;
  void init ();
  int set (path_conv *, const char *);
};

#ifdef DEBUGGING
struct cygheap_debug
{
  handle_list starth;
  handle_list freeh[500];
};
#endif

struct cygheap_locale
{
  mbtowc_p mbtowc;
  wctomb_p wctomb;
  char charset[ENCODING_LEN + 1];
};

struct user_heap_info
{
  void *base;
  void *ptr;
  void *top;
  void *max;
  SIZE_T chunk;
  void __reg2 *sbrk (ptrdiff_t);
  void __reg1 init ();
};

class cygheap_domain_info
{
  PWCHAR pdom_name;
  PWCHAR pdom_dns_name;
  cygsid pdom_sid;

  PWCHAR adom_name;
  cygsid adom_sid;

  PDS_DOMAIN_TRUSTSW tdom;
  ULONG tdom_count;

  PWCHAR rfc2307_domain_buf;

public:
  bool init ();

  inline PCWSTR primary_flat_name () const { return pdom_name; }
  inline PCWSTR primary_dns_name () const { return pdom_dns_name; }
  inline cygsid &primary_sid () { return pdom_sid; }

  inline bool member_machine () const { return pdom_sid != NO_SID; }

  inline PCWSTR account_flat_name () const { return adom_name; }
  inline cygsid &account_sid () { return adom_sid; }

  inline PDS_DOMAIN_TRUSTSW trusted_domain (ULONG idx) const
    { return (idx < tdom_count) ? tdom + idx : NULL; }
  PDS_DOMAIN_TRUSTSW add_domain (PCWSTR, PSID);

  inline PWCHAR get_rfc2307_domain () const
    { return rfc2307_domain_buf ?: NULL; }
};

class cygheap_pwdgrp
{
  enum nss_pfx_t {
    NSS_PFX_AUTO = 0,
    NSS_PFX_PRIMARY,
    NSS_PFX_ALWAYS
  };
public:
  enum nss_scheme_method {
    NSS_SCHEME_FALLBACK = 0,
    NSS_SCHEME_WINDOWS,
    NSS_SCHEME_CYGWIN,
    NSS_SCHEME_UNIX,
    NSS_SCHEME_DESC,
    NSS_SCHEME_PATH,
    NSS_SCHEME_FREEATTR
  };
  struct nss_scheme_t {
    nss_scheme_method	method;
    PWCHAR		attrib;
  };
private:
  bool		nss_inited;
  uint32_t	pwd_src;
  uint32_t	grp_src;
  nss_pfx_t	prefix;
  WCHAR		separator[2];
  bool		caching;

#define NSS_SCHEME_MAX	4
  nss_scheme_t	home_scheme[NSS_SCHEME_MAX];
  nss_scheme_t	shell_scheme[NSS_SCHEME_MAX];
  nss_scheme_t	gecos_scheme[NSS_SCHEME_MAX];

  uint32_t	enums;
  PWCHAR	enum_tdoms;

  void nss_init_line (const char *line);
  void _nss_init ();

public:
  struct {
    pwdgrp cygserver;
    pwdgrp file;
    pwdgrp win;
  } pwd_cache;
  struct {
    pwdgrp cygserver;
    pwdgrp file;
    pwdgrp win;
  } grp_cache;

  void init ();

  /* Implemented in ldap.cc */
  PWCHAR *ldap_user_attr;
  void init_ldap_user_attr ();

  inline void nss_init () { if (!nss_inited) _nss_init (); }
  inline bool nss_pwd_files () const { return !!(pwd_src & NSS_SRC_FILES); }
  inline bool nss_pwd_db () const { return !!(pwd_src & NSS_SRC_DB); }
  inline int  nss_pwd_src () const { return pwd_src; } /* CW_GETNSS_PWD_SRC */
  inline bool nss_grp_files () const { return !!(grp_src & NSS_SRC_FILES); }
  inline bool nss_grp_db () const { return !!(grp_src & NSS_SRC_DB); }
  inline int  nss_grp_src () const { return grp_src; } /* CW_GETNSS_GRP_SRC */
  inline bool nss_prefix_auto () const { return prefix == NSS_PFX_AUTO; }
  inline bool nss_prefix_primary () const { return prefix == NSS_PFX_PRIMARY; }
  inline bool nss_prefix_always () const { return prefix == NSS_PFX_ALWAYS; }
  inline PCWSTR nss_separator () const { return separator; }
  inline bool nss_cygserver_caching () const { return caching; }
  inline void nss_disable_cygserver_caching () { caching = false; }

  char *get_home (cyg_ldap *pldap, cygpsid &sid, PCWSTR dom, PCWSTR dnsdomain,
		  PCWSTR name, bool fq);
  char *get_home (struct _USER_INFO_3 *ui, cygpsid &sid, PCWSTR dom,
		  PCWSTR name, bool fq);

  char *get_shell (cyg_ldap *pldap, cygpsid &sid, PCWSTR dom, PCWSTR dnsdomain,
		   PCWSTR name, bool fq);
  char *get_shell (struct _USER_INFO_3 *ui, cygpsid &sid, PCWSTR dom,
		   PCWSTR name, bool fq);

  char *get_gecos (cyg_ldap *pldap, cygpsid &sid, PCWSTR dom, PCWSTR dnsdomain,
		   PCWSTR name, bool fq);
  char *get_gecos (struct _USER_INFO_3 *ui, cygpsid &sid, PCWSTR dom,
		   PCWSTR name, bool fq);

  inline int nss_db_enums () const { return enums; }
  inline PCWSTR nss_db_enum_tdoms () const { return enum_tdoms; }
};

class cygheap_ugid_cache
{
  struct idmap {
    uint32_t nfs_id;
    uint32_t cyg_id;
  };
  class idmaps {
    uint32_t _cnt;
    uint32_t _max;
    idmap *_map;
  public:
    uint32_t get (uint32_t id) const
    {
      for (uint32_t i = 0; i < _cnt; ++i)
	if (_map[i].nfs_id == id)
	  return _map[i].cyg_id;
      return (uint32_t) -1;
    }
    uint32_t reverse_get (uint32_t id) const
    {
      for (uint32_t i = 0; i < _cnt; ++i)
	if (_map[i].cyg_id == id)
	  return _map[i].nfs_id;
      return (uint32_t) -1;
    }
    void add (uint32_t nfs_id, uint32_t cyg_id)
    {
      if (_cnt >= _max)
	_map = (idmap *) crealloc (_map, (_max += 10) * sizeof (*_map));
      _map[_cnt].nfs_id = nfs_id;
      _map[_cnt].cyg_id = cyg_id;
      ++_cnt;
    }
  };
  idmaps uids;
  idmaps gids;

public:
  uid_t get_uid (uid_t uid) const { return uids.get (uid); }
  gid_t get_gid (gid_t gid) const { return gids.get (gid); }
  uid_t reverse_get_uid (uid_t uid) const { return uids.reverse_get (uid); }
  gid_t reverse_get_gid (gid_t gid) const { return gids.reverse_get (gid); }
  void add_uid (uid_t nfs_uid, uid_t cyg_uid) { uids.add (nfs_uid, cyg_uid); }
  void add_gid (gid_t nfs_gid, gid_t cyg_gid) { gids.add (nfs_gid, cyg_gid); }
};

struct hook_chain
{
  void **loc;
  const void *func;
  struct hook_chain *next;
};

struct mini_cygheap
{
  cygheap_locale locale;
};

#define NBUCKETS 40

struct threadlist_t
{
  struct _cygtls *thread;
  HANDLE mutex;			/* Used to avoid accessing tls area of
				   deleted thread.  See comment in
				   cygheap::remove_tls for a description. */
};

struct init_cygheap: public mini_cygheap
{
  _cmalloc_entry *chain;
  unsigned bucket_val[NBUCKETS];
  char *buckets[NBUCKETS];
  WCHAR installation_root[PATH_MAX];
  WCHAR installation_dir[PATH_MAX];
  size_t installation_dir_len;
  UNICODE_STRING installation_key;
  WCHAR installation_key_buf[18];
  cygheap_root root;
  cygheap_domain_info dom;
  cygheap_pwdgrp pg;
  cygheap_ugid_cache ugid_cache;
  cygheap_user user;
  user_heap_info user_heap;
  mode_t umask;
  unsigned long rlim_core;
  HANDLE console_h;
  cwdstuff cwd;
  dtable fdtab;
#ifdef DEBUGGING
  cygheap_debug debug;
#endif
  struct sigaction *sigs;

  fhandler_termios *ctty;	/* Current tty */
  threadlist_t *threadlist;
  uint32_t sthreads;
  pid_t pid;			/* my pid */
  struct {			/* Equivalent to using LIST_HEAD. */
    struct inode_t *lh_first;
  } inode_list;			/* Global inode pointer for adv. locking. */
  hook_chain hooks;
  void close_ctty ();
  void init_installation_root ();
  void __reg1 init_tls_list ();;
  void __reg2 add_tls (_cygtls *);
  HANDLE __reg3 remove_tls (_cygtls *);
  threadlist_t __reg2 *find_tls (_cygtls *);
  threadlist_t __reg3 *find_tls (int, bool&);
  void unlock_tls (threadlist_t *t) { if (t) ReleaseMutex (t->mutex); }
};


#define _CYGHEAPSIZE_SLOP (128 * 1024)
#define CYGHEAPSIZE (sizeof (init_cygheap) + (20000 * sizeof (fhandler_union)) + _CYGHEAPSIZE_SLOP)
#define CYGHEAPSIZE_MIN (sizeof (init_cygheap) + (10000 * sizeof (fhandler_union)))

extern init_cygheap *cygheap;
extern void *cygheap_max;

class cygheap_fdmanip
{
 protected:
  int fd;
  bool locked;
 public:
  cygheap_fdmanip (): fd (-1), locked (false) {}
  virtual ~cygheap_fdmanip ()
  {
    if (locked)
      cygheap->fdtab.unlock ();
  }
  virtual void release () { cygheap->fdtab.release (fd); }
  operator int &() {return fd;}
  operator fhandler_base* &() {return cygheap->fdtab[fd];}
  operator fhandler_socket* () const {return reinterpret_cast<fhandler_socket *> (cygheap->fdtab[fd]);}
  operator fhandler_pipe* () const {return reinterpret_cast<fhandler_pipe *> (cygheap->fdtab[fd]);}
  void operator = (fhandler_base *fh) {cygheap->fdtab[fd] = fh;}
  fhandler_base *operator -> () const {return cygheap->fdtab[fd];}
  bool isopen () const
  {
    if (cygheap->fdtab[fd])
      return true;
    set_errno (EBADF);
    return false;
  }
};

class cygheap_fdnew : public cygheap_fdmanip
{
 public:
  cygheap_fdnew (int seed_fd = -1, bool lockit = true)
  {
    if (lockit)
      cygheap->fdtab.lock ();
    if (seed_fd < 0)
      fd = cygheap->fdtab.find_unused_handle ();
    else
      fd = cygheap->fdtab.find_unused_handle (seed_fd + 1);
    if (fd >= 0)
      locked = lockit;
    else
      {
	/* errno set by find_unused_handle */
	if (lockit)
	  cygheap->fdtab.unlock ();
	locked = false;
      }
  }
  ~cygheap_fdnew ()
  {
    if (cygheap->fdtab[fd])
      cygheap->fdtab[fd]->inc_refcnt ();
  }
  void operator = (fhandler_base *fh) {cygheap->fdtab[fd] = fh;}
};

class cygheap_fdget : public cygheap_fdmanip
{
  fhandler_base *fh;
public:
  cygheap_fdget (int fd, bool lockit = false, bool do_set_errno = true)
  {
    if (lockit)
      cygheap->fdtab.lock ();
    if (fd >= 0 && fd < (int) cygheap->fdtab.size && cygheap->fdtab[fd] != NULL)
      {
	this->fd = fd;
	locked = lockit;
	fh = cygheap->fdtab[fd];
	fh->inc_refcnt ();
      }
    else
      {
	this->fd = -1;
	if (do_set_errno)
	  set_errno (EBADF);
	if (lockit)
	  cygheap->fdtab.unlock ();
	locked = false;
	fh = NULL;
      }
  }
  ~cygheap_fdget ()
  {
    if (fh && fh->dec_refcnt () <= 0)
      {
	debug_only_printf ("deleting fh %p", fh);
	delete fh;
      }
  }
  void release () { cygheap->fdtab.release (fd); }
};

class cygheap_fdenum : public cygheap_fdmanip
{
 public:
  cygheap_fdenum (bool lockit = false)
  {
    locked = lockit;
    if (lockit)
      cygheap->fdtab.lock ();
    fd = -1;
  }
  int next ()
  {
    while (++fd < (int) cygheap->fdtab.size)
      if (cygheap->fdtab[fd] != NULL)
	return fd;
    return -1;
  }
  void rewind ()
  {
    fd = -1;
  }
};

void __stdcall cygheap_fixup_in_child (bool);
void __stdcall cygheap_init ();
void setup_cygheap ();
extern char _cygheap_start[] __attribute__((section(".idata")));