Merge "linker: the global group is added to all built-in namespaces"
diff --git a/libc/include/sched.h b/libc/include/sched.h
index d407202..b8ef44b 100644
--- a/libc/include/sched.h
+++ b/libc/include/sched.h
@@ -25,6 +25,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #ifndef _SCHED_H_
 #define _SCHED_H_
 
@@ -41,22 +42,22 @@
   int sched_priority;
 };
 
-int sched_setscheduler(pid_t, int, const struct sched_param*);
-int sched_getscheduler(pid_t);
+int sched_setscheduler(pid_t pid, int policy, const struct sched_param* param);
+int sched_getscheduler(pid_t pid);
 int sched_yield(void);
-int sched_get_priority_max(int);
-int sched_get_priority_min(int);
-int sched_setparam(pid_t, const struct sched_param*);
-int sched_getparam(pid_t, struct sched_param*);
-int sched_rr_get_interval(pid_t, struct timespec*);
+int sched_get_priority_max(int policy);
+int sched_get_priority_min(int policy);
+int sched_setparam(pid_t pid, const struct sched_param* param);
+int sched_getparam(pid_t pid, struct sched_param* param);
+int sched_rr_get_interval(pid_t pid, struct timespec* quantum);
 
 #if defined(__USE_GNU)
 
-int clone(int (*)(void*), void*, int, void*, ...) __INTRODUCED_IN_ARM(9)
-    __INTRODUCED_IN_MIPS(12) __INTRODUCED_IN_X86(17);
-int unshare(int) __INTRODUCED_IN(17);
+int clone(int (*fn)(void*), void* child_stack, int flags, void* arg, ...)
+    __INTRODUCED_IN_ARM(9) __INTRODUCED_IN_MIPS(12) __INTRODUCED_IN_X86(17);
+int unshare(int flags) __INTRODUCED_IN(17);
 int sched_getcpu(void) __INTRODUCED_IN(12);
-int setns(int, int) __INTRODUCED_IN(21);
+int setns(int fd, int ns_type) __INTRODUCED_IN(21);
 
 #ifdef __LP64__
 #define CPU_SETSIZE 1024
diff --git a/libc/include/stdio_ext.h b/libc/include/stdio_ext.h
index fdf6772..3c5abc3 100644
--- a/libc/include/stdio_ext.h
+++ b/libc/include/stdio_ext.h
@@ -40,7 +40,9 @@
 
 size_t __fbufsize(FILE*) __INTRODUCED_IN(23);
 int __freadable(FILE*) __INTRODUCED_IN(23);
+int __freading(FILE*) __INTRODUCED_IN_FUTURE;
 int __fwritable(FILE*) __INTRODUCED_IN(23);
+int __fwriting(FILE*) __INTRODUCED_IN_FUTURE;
 int __flbf(FILE*) __INTRODUCED_IN(23);
 void __fpurge(FILE*) __INTRODUCED_IN(23);
 size_t __fpending(FILE*) __INTRODUCED_IN(23);
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index d79e36b..06856c8 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1320,6 +1320,8 @@
 
 LIBC_P {
   global:
+    __freading; # future
+    __fwriting; # future
     getlogin_r; # future
 } LIBC_O;
 
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index ed1e82c..50e4390 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1240,6 +1240,8 @@
 
 LIBC_P {
   global:
+    __freading; # future
+    __fwriting; # future
     getlogin_r; # future
 } LIBC_O;
 
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index af8e550..3e4d36e 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1345,6 +1345,8 @@
 
 LIBC_P {
   global:
+    __freading; # future
+    __fwriting; # future
     getlogin_r; # future
 } LIBC_O;
 
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 90a65e0..3fa7c9d 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1304,6 +1304,8 @@
 
 LIBC_P {
   global:
+    __freading; # future
+    __fwriting; # future
     getlogin_r; # future
 } LIBC_O;
 
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index ed1e82c..50e4390 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1240,6 +1240,8 @@
 
 LIBC_P {
   global:
+    __freading; # future
+    __fwriting; # future
     getlogin_r; # future
 } LIBC_O;
 
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 8217562..bd74fbe 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1302,6 +1302,8 @@
 
 LIBC_P {
   global:
+    __freading; # future
+    __fwriting; # future
     getlogin_r; # future
 } LIBC_O;
 
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index ed1e82c..50e4390 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1240,6 +1240,8 @@
 
 LIBC_P {
   global:
+    __freading; # future
+    __fwriting; # future
     getlogin_r; # future
 } LIBC_O;
 
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 6fb2c0d..bf6a8f8 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -127,10 +127,11 @@
 // Values for `__sFILE::_flags`.
 #define __SLBF 0x0001  // Line buffered.
 #define __SNBF 0x0002  // Unbuffered.
-// RD and WR are never simultaneously asserted: use _SRW instead.
-#define __SRD  0x0004  // OK to read.
-#define __SWR  0x0008  // OK to write.
-#define __SRW  0x0010  // Open for reading & writing.
+// __SRD and __SWR are mutually exclusive because they indicate what we did last.
+// If you want to know whether we were opened read-write, check __SRW instead.
+#define __SRD  0x0004  // Last operation was read.
+#define __SWR  0x0008  // Last operation was write.
+#define __SRW  0x0010  // Was opened for reading & writing.
 #define __SEOF 0x0020  // Found EOF.
 #define __SERR 0x0040  // Found error.
 #define __SMBF 0x0080  // `_buf` is from malloc.
diff --git a/libc/stdio/stdio_ext.cpp b/libc/stdio/stdio_ext.cpp
index ebc705c..8cf4f4b 100644
--- a/libc/stdio/stdio_ext.cpp
+++ b/libc/stdio/stdio_ext.cpp
@@ -39,17 +39,13 @@
   return fp->_bf._size;
 }
 
-/* For a _SRW stream, we don't know whether we last read or wrote.
 int __freading(FILE* fp) {
-  return (fp->_flags & _SRD) != 0 || ...;
+  return (fp->_flags & __SRD) != 0;
 }
-*/
 
-/* For a _SRW stream, we don't know whether we last read or wrote.
-int __fwriting(FILE*) {
-  return (fp->_flags & _SWR) != 0 || ...;
+int __fwriting(FILE* fp) {
+  return (fp->_flags & __SWR) != 0;
 }
-*/
 
 int __freadable(FILE* fp) {
   return (fp->_flags & (__SRD|__SRW)) != 0;
diff --git a/tests/sched_test.cpp b/tests/sched_test.cpp
index a4cffc0..e70528e 100644
--- a/tests/sched_test.cpp
+++ b/tests/sched_test.cpp
@@ -270,3 +270,34 @@
   CPU_FREE(set1);
   CPU_FREE(set2);
 }
+
+TEST(sched, sched_get_priority_min_sched_get_priority_max) {
+  EXPECT_LE(sched_get_priority_min(SCHED_BATCH), sched_get_priority_max(SCHED_BATCH));
+  EXPECT_LE(sched_get_priority_min(SCHED_FIFO), sched_get_priority_max(SCHED_FIFO));
+  EXPECT_LE(sched_get_priority_min(SCHED_IDLE), sched_get_priority_max(SCHED_IDLE));
+  EXPECT_LE(sched_get_priority_min(SCHED_OTHER), sched_get_priority_max(SCHED_OTHER));
+  EXPECT_LE(sched_get_priority_min(SCHED_RR), sched_get_priority_max(SCHED_RR));
+}
+
+TEST(sched, sched_getscheduler_sched_setscheduler) {
+  // POSIX: "If pid is zero, the scheduling policy shall be returned for the
+  // calling process".
+  ASSERT_EQ(sched_getscheduler(getpid()), sched_getscheduler(0));
+
+  const int original_policy = sched_getscheduler(getpid());
+  sched_param p = {};
+  p.sched_priority = sched_get_priority_min(original_policy);
+  errno = 0;
+  ASSERT_EQ(-1, sched_setscheduler(getpid(), INT_MAX, &p));
+  ASSERT_EQ(EINVAL, errno);
+
+  ASSERT_EQ(0, sched_getparam(getpid(), &p));
+  ASSERT_EQ(original_policy, sched_setscheduler(getpid(), SCHED_BATCH, &p));
+  // POSIX says this should return the previous policy (here SCHED_BATCH),
+  // but the Linux system call doesn't, and the glibc wrapper doesn't correct
+  // this (the "returns 0" behavior is even documented on the man page in
+  // the BUGS section). This was our historical behavior too, so in the
+  // absence of reasons to break compatibility with ourselves and glibc, we
+  // don't behave as POSIX specifies. http://b/26203902.
+  ASSERT_EQ(0, sched_setscheduler(getpid(), original_policy, &p));
+}
diff --git a/tests/stdio_ext_test.cpp b/tests/stdio_ext_test.cpp
index 7872567..128e255 100644
--- a/tests/stdio_ext_test.cpp
+++ b/tests/stdio_ext_test.cpp
@@ -116,20 +116,78 @@
 }
 
 TEST(stdio_ext, __freadable__fwritable) {
-  FILE* fp = fopen("/dev/null", "r");
+  FILE* fp;
+
+  // Read-only.
+  fp = fopen("/dev/null", "r");
   ASSERT_TRUE(__freadable(fp));
   ASSERT_FALSE(__fwritable(fp));
   fclose(fp);
 
+  // Write-only.
   fp = fopen("/dev/null", "w");
   ASSERT_FALSE(__freadable(fp));
   ASSERT_TRUE(__fwritable(fp));
   fclose(fp);
 
-  fp = fopen("/dev/null", "w+");
-  ASSERT_TRUE(__freadable(fp));
+  // Append (aka write-only).
+  fp = fopen("/dev/null", "a");
+  ASSERT_FALSE(__freadable(fp));
   ASSERT_TRUE(__fwritable(fp));
   fclose(fp);
+
+  // The three read-write modes.
+  for (auto read_write_mode : {"r+", "w+", "a+"}) {
+    fp = fopen("/dev/null", read_write_mode);
+    ASSERT_TRUE(__freadable(fp));
+    ASSERT_TRUE(__fwritable(fp));
+    fclose(fp);
+  }
+}
+
+TEST(stdio_ext, __freading__fwriting) {
+  FILE* fp;
+
+  // Append (aka write-only). Never reading. Always writing.
+  fp = fopen("/dev/zero", "a");
+  ASSERT_FALSE(__freading(fp)); // Not reading initially.
+  ASSERT_TRUE(__fwriting(fp)); // Writing initially.
+  ASSERT_TRUE(fputc('x', fp) != EOF);
+  ASSERT_FALSE(__freading(fp)); // Not reading after write.
+  ASSERT_TRUE(__fwriting(fp)); // Still writing after write.
+  fclose(fp);
+
+  // Write-only. Never reading. Always writing.
+  fp = fopen("/dev/zero", "w");
+  ASSERT_FALSE(__freading(fp)); // Not reading initially.
+  ASSERT_TRUE(__fwriting(fp)); // Writing initially.
+  ASSERT_TRUE(fputc('x', fp) != EOF);
+  ASSERT_FALSE(__freading(fp)); // Not reading after write.
+  ASSERT_TRUE(__fwriting(fp)); // Still writing after write.
+  fclose(fp);
+
+  // Read-only. Always reading. Never writing.
+  fp = fopen("/dev/zero", "r");
+  ASSERT_TRUE(__freading(fp)); // Reading initially.
+  ASSERT_FALSE(__fwriting(fp)); // Not writing initially.
+  ASSERT_TRUE(fgetc(fp) == 0);
+  ASSERT_TRUE(__freading(fp)); // Still reading after read.
+  ASSERT_FALSE(__fwriting(fp)); // Still not writing after read.
+  fclose(fp);
+
+  // The three read-write modes.
+  for (auto read_write_mode : {"r+", "w+", "a+"}) {
+    fp = fopen("/dev/zero", read_write_mode);
+    ASSERT_FALSE(__freading(fp)); // Not reading initially.
+    ASSERT_FALSE(__fwriting(fp)); // Not writing initially.
+    ASSERT_TRUE(fgetc(fp) == 0);
+    ASSERT_TRUE(__freading(fp)); // Reading after read.
+    ASSERT_FALSE(__fwriting(fp)); // Not writing after read.
+    ASSERT_TRUE(fputc('x', fp) != EOF);
+    ASSERT_FALSE(__freading(fp)); // Not reading after write.
+    ASSERT_TRUE(__fwriting(fp)); // Writing after write.
+    fclose(fp);
+  }
 }
 
 TEST(stdio_ext, __fsetlocking) {