Update from Brian
authorRalph Boehme <slow@samba.org>
Tue, 24 Nov 2015 15:40:07 +0000 (16:40 +0100)
committerRalph Boehme <slow@samba.org>
Tue, 10 Jan 2023 14:07:34 +0000 (15:07 +0100)
Signed-off-by: Ralph Boehme <slow@samba.org>
lockchart.c

index 1b14ffce9e30d1645be3463b2c1c2622e92ea01d..957fff3878158e923ba1fcf4f5fe10cb54c11652 100644 (file)
@@ -1,8 +1,27 @@
+/*
+  Copyright 2012, 2015 EditShare LLC
+  Copyright 2012 NetAFP
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
 #include <stdio.h>
 #include <stdbool.h>
 #include <unistd.h>
 #include <err.h>
 #include <sys/wait.h>
+#include <sys/file.h>
 #include <fcntl.h>
 #include <stdlib.h>
 
@@ -11,171 +30,358 @@ static int verbose = 0;
 static char results[9][9];
 
 enum mode {
-    mread = 0,
-    mwrite,
-    mread_write,
-    n_modes
+        mread = 0,
+        mwrite,
+        mread_write,
+        n_modes
 };
 
 enum lock {
-    lexclusive = 0,
-    lshared,
-    lnone,
-    n_locks
+        lexclusive = 0,
+        lshared,
+        lnone,
+        n_locks
+};
+
+enum locktype {
+        tsharemode = 0,
+        tflock,
+        tfcntl,
+        n_locktypes
+};
+
+int modes[] = {
+       [mread]       = O_RDONLY,
+       [mwrite]      = O_WRONLY,
+       [mread_write] = O_RDWR
 };
 
-int modes[] = { [mread]       = O_RDONLY,
-                [mwrite]      = O_WRONLY,
-                [mread_write] = O_RDWR   };
+int locks[] = {
+       [lnone]       = 0,
+       [lshared]     = O_SHLOCK,
+       [lexclusive]  = O_EXLOCK
+};
+
+int flocks[] = {
+       [lnone]      = 0,
+       [lshared]    = LOCK_SH,
+       [lexclusive] = LOCK_EX
+};
+
+int fcntls[] = {
+       [lnone]      = 0,
+       [lshared]    = F_RDLCK,
+       [lexclusive] = F_WRLCK
+};
 
-int locks[] = { [lnone]       = 0,
-                [lshared]     = O_SHLOCK,
-                [lexclusive]  = O_EXLOCK };
+char *mode_names[] = {
+       [mread]       = "read only",
+       [mwrite]      = "write only",
+       [mread_write] = "read/write"
+};
 
-char *mode_names[] = { [mread]       = "read only",
-                       [mwrite]      = "write only",
-                       [mread_write] = "read/write" };
+char *lock_names[] = {
+       [lnone]       = "no",
+       [lshared]     = "shared",
+       [lexclusive]  = "exclusive"
+};
 
-char *lock_names[] = { [lnone]       = "no",
-                       [lshared]     = "shared",
-                       [lexclusive]  = "exclusive" };
+char *locktype_names[] = {
+       [tsharemode]  = "sharemode",
+       [tflock]      = "flock",
+       [tfcntl]      = "fcntl"
+};
 
 char *tableNames[] = {
-    "Deny RW     R   ",
-    "O_EXLOCK    W   ",
-    "            RW  ",
-    "DenyWrite   R   ",
-    "O_SHLOCK    W   ",
-    "            RW  ",
-    "Deny None   R   ",
-    "-           W   ",
-    "            RW  "
+        "exclusive   R   ",
+        "            W   ",
+        "            RW  ",
+        "shared      R   ",
+        "            W   ",
+        "            RW  ",
+        "none        R   ",
+        "            W   ",
+        "            RW  "
 };
 
 static void usage(const char *myname)
 {
-    printf(
-           "usage: %s [-f] PATH1 PATH2\n"
-           "               -f use ressource fork, default is data fork\n"
-           "               -v verbose, print error information\n"
-           "               -vv extra verbose, print all open calls\n",
-           myname);
-    exit(1);
+        printf("usage: %s [-f] PATH1 PATH2\n"
+               "               -f use ressource fork, default is data fork\n"
+               "               -v verbose, print error information\n"
+               "               -vv extra verbose, print all open calls\n"
+               "               -(s|l|c)(s|l|c)\n"
+               "                  Use sharemode (lock on open), flock, or fcntl,\n"
+               "                  for lock on first and second open respectively,\n"
+               "                  defaulting to sharemode.\n",
+               myname);
+        exit(1);
 }
 
-bool test(char *path1, int flags1, char *path2, int flags2) {
-    int fd1 = open(path1, flags1 | O_NONBLOCK);
-    if (fd1 < 0) {
-        if (verbose)
-            warn("outer open(%s, %02x)", path1, flags1);
-        return false;
-    } else {
-        if (verbose > 1)
-            fprintf(stderr, "outer open(%s, %02x)\n", path1, flags1);
-    }
-    int pid = fork();
-    if (pid < 0) {
-        err(1, "fork");
-    } else if (pid == 0) {
-        int fd2 = open(path2, flags2 | O_NONBLOCK);
-        if (fd2 < 0) {
-            if (verbose)
-                err(1, "inner open(%s, %02x)", path2, flags2);
-            else
-                exit(1);
-        } else {
-            if (verbose > 1)
-                fprintf(stderr, "inner open(%s, %02x)\n", path2, flags2);
-            exit(0);
+bool lock(int fd, enum lock lock, enum locktype locktype)
+{
+       int result;
+        int flags;
+
+        if (lock == lnone) {
+                return true;
+       }
+
+        switch (locktype) {
+        case tsharemode:
+               /*
+                * This is done upon open.
+                */
+               break;
+
+        case tflock:
+                flags = flocks[lock] | LOCK_NB;
+
+               result = flock(fd, flags);
+                if (result < 0) {
+                        if (verbose) {
+                                printf("flock(%d, %02x)\n", fd, flags);
+                       }
+                        return false;
+                } else {
+                        if (verbose > 1) {
+                                printf("flock(%d, %02x)\n", fd, flags);
+                       }
+                }
+                break;
+
+        case tfcntl:
+                struct flock flock_lock = {
+                        .l_start = 0,
+                        .l_len = 0,
+                        .l_whence = SEEK_SET,
+                        .l_type = fcntls[lock]
+                };
+
+               result = fcntl(fd, F_SETLK, &flock_lock);
+                if (result < 0) {
+                        if (verbose) {
+                                printf("fcntl(%d, F_SETLK, {.l_type = %02x})\n",
+                                      fd, type);
+                       }
+                        return false;
+                } else {
+                        if (verbose > 1) {
+                                printf("fcntl(%d, F_SETLK, {.l_type = %02x})\n",
+                                        fd, type);
+                       }
+                }
+                break;
+
+        default:
+               break;
         }
-    } else {
-        int status;
-        waitpid(pid, &status, 0);
-        if(close(fd1) != 0)
-            err(1, "close");
-        if (WIFEXITED(status)) {
-            return !WEXITSTATUS(status);
+
+        return true;
+}
+
+bool test(char *path1, int flags1, enum lock lock1, enum locktype locktype1,
+          char *path2, int flags2, enum lock lock2, enum locktype locktype2)
+{
+       int fd1, fd2;
+       bool ok;
+       int pid;
+       int status;
+
+        /*
+        * Sharemodes need to be applied in open arguments before
+         * opening, rather than to fd afterwards
+        */
+
+        if (locktype1 == tsharemode) {
+                flags1 |= locks[lock1];
+       }
+        if (locktype2 == tsharemode) {
+                flags2 |= locks[lock2];
+       }
+
+        fd1 = open(path1, flags1 | O_NONBLOCK);
+        if (fd1 < 0) {
+                if (verbose) {
+                        printf("outer open(%s, %02x)\n", path1, flags1);
+               }
+                return false;
         } else {
-            return false;
+                if (verbose > 1) {
+                        printf("outer open(%s, %02x)\n", path1, flags1);
+               }
         }
-    }
+
+        ok = lock(fd1, lock1, locktype1);
+       if (!ok) {
+                return false;
+       }
+
+        pid = fork();
+        if (pid < 0) {
+                perror("fork");
+               exit(1);
+        }
+
+       if (pid == 0) {
+                fd2 = open(path2, flags2 | O_NONBLOCK);
+                if (fd2 < 0) {
+                        if (verbose) {
+                                printf("inner open(%s, %02x)\n",
+                                      path2, flags2);
+                       }
+                       exit(1);
+                } else {
+                        if (verbose > 1) {
+                                printf("inner open(%s, %02x)\n",
+                                      path2, flags2);
+                       }
+                }
+                ok = lock(fd2, lock2, locktype2);
+               if (!ok) {
+                        exit(1);
+               }
+               exit(0);
+       }
+
+       waitpid(pid, &status, 0);
+       result = close(fd1);
+       if (result != 0) {
+               perror("close");
+               exit(1);
+       }
+
+       if (WIFEXITED(status)) {
+               return !WEXITSTATUS(status);
+       } else {
+               return false;
+       }
 }
 
 int main(int argc, char **argv) {
-    bool resource = false;
-    char *path1, *path2, *myname = argv[0];
-    bool result;
-    char ch;
-    char str1[32], str2[32];
-
-    while ((ch = getopt(argc, argv, "fv")) != -1) {
-        switch (ch) {
-        case 'f':
-            resource = true;
-            break;
-        case 'v':
-            ++verbose;
-            break;
-        case '?':
-        case 'h':
-        default:
-            usage(myname);
+        bool resource = false;
+        char *path1, *path2, *myname = argv[0];
+        bool ok;
+       int result;
+        char ch;
+        char str1[36], str2[36];
+        int nlockargs = 0;
+        enum locktype locktypes[2] = {tsharemode, tsharemode};
+       char *p;
+
+        while ((ch = getopt(argc, argv, "fvslc")) != -1) {
+                switch (ch) {
+                case 'f':
+                        resource = true;
+                        break;
+
+                case 'v':
+                        ++verbose;
+                        break;
+
+                case 's':
+                case 'l':
+                case 'c':
+                        if (nlockargs >= 2) {
+                                usage(myname);
+                       }
+
+                        switch (ch) {
+                        case 's':
+                               break;
+                        case 'l':
+                                locktypes[nlockargs] = tflock;
+                                break;
+                        case 'c':
+                                locktypes[nlockargs] = tfcntl;
+                                break;
+                        default:
+                                usage(myname);
+                        }
+                        ++nlockargs;
+                        break;
+
+                case '?':
+                case 'h':
+                default:
+                        usage(myname);
+                }
+        }
+        argc -= optind;
+        argv += optind;
+
+        if (argc < 2) {
+                usage(myname);
+       }
+
+        if (resource) {
+                result = asprintf(&path1, "%s/..namedfork/rsrc", argv[0]);
+               if (result == -1) {
+                        printf("asprintf %s\n", argv[0]);
+                       exit(1);
+               }
+
+                result = asprintf(&path2, "%s/..namedfork/rsrc", argv[1]);
+               if (result == -1) {
+                        printf("asprintf %s\n", argv[1]);
+                       exit(1);
+               }
+        } else {
+                path1 = argv[0];
+                path2 = argv[1];
         }
-    }
-    argc -= optind;
-    argv += optind;
-
-    if (argc < 2)
-        usage(myname);
-
-    if (resource) {
-        if (asprintf(&path1, "%s/..namedfork/rsrc", argv[0]) == -1)
-            err(1, "asprintf %s", argv[0]);
-        if (asprintf(&path2, "%s/..namedfork/rsrc", argv[1]) == -1)
-            err(1, "asprintf %s", argv[1]);
-    } else {
-        path1 = argv[0];
-        path2 = argv[1];
-    }
-
-    if (verbose)
-        printf("       %-32s %-32s\n", path1, path2);
-
-    char *p = &results[0][0];
-    for (enum lock lock1 = 0; lock1 < n_locks; ++lock1) {
-        for (enum mode mode1 = 0; mode1 < n_modes; ++mode1) {
-            for (enum lock lock2 = 0; lock2 < n_locks; ++lock2) {
-                for (enum mode mode2 = 0; mode2 < n_modes; ++mode2) {
-                    result = test(path1, modes[mode1] | locks[lock1],
-                                  path2, modes[mode2] | locks[lock2]);
-                    if (verbose) {
-                        snprintf(str1, sizeof(str1), "%s with %s lock",
-                                 mode_names[mode1], lock_names[lock1]);
-                        snprintf(str2, sizeof(str2), "%s with %s lock",
-                                 mode_names[mode2], lock_names[lock2]);
-                        printf("%s : %-32s %-32s \n",
-                               result ? " ok " : "fail",
-                               str1, str2);
-                    }
-                    *p++ = result ? '.' : 'x';
+
+        if (verbose) {
+                printf("       %-32s %-32s\n", path1, path2);
+       }
+
+        p = &results[0][0];
+        for (enum lock lock1 = 0; lock1 < n_locks; ++lock1) {
+                for (enum mode mode1 = 0; mode1 < n_modes; ++mode1) {
+                        for (enum lock lock2 = 0; lock2 < n_locks; ++lock2) {
+                                for (enum mode mode2 = 0; mode2 < n_modes; ++mode2) {
+                                        ok = test(path1, modes[mode1], lock1, locktypes[0],
+                                                 path2, modes[mode2], lock2, locktypes[1]);
+                                        if (verbose) {
+                                                snprintf(str1, sizeof(str1), "%s with %s %s",
+                                                         mode_names[mode1],
+                                                         lock_names[lock1],
+                                                         locktype_names[locktypes[0]]);
+                                                snprintf(str2, sizeof(str2), "%s with %s %s",
+                                                         mode_names[mode2],
+                                                         lock_names[lock2],
+                                                         locktype_names[locktypes[1]]);
+                                                printf("%s : %-36s %-36s \n",
+                                                       ok ? " ok " : "fail",
+                                                       str1, str2);
+                                        }
+                                        *p++ = ok ? '.' : 'x';
+                                }
+                        }
                 }
-            }
         }
-    }
-
-    printf("           \\    Attempted mode\n"
-           "            \\   Deny RW     | DenyWrite   | Deny None\n"
-           "Current mode \\  O_EXLOCK    | O_SHLOCK    | -\n"
-           "                R   W  RW   | R   W  RW   | R   W  RW\n");
-    for (int i = 0; i < 9; i++) {
-        if (i == 3 || i == 6)
-            printf("----------\n");
-        printf("%s", tableNames[i]);
-        for (int j = 0; j < 9; j++) {
-            printf("%s%c   ",
-                   (j == 3 || j == 6) ? "  " : "",
-                   results[i][j]);
+
+        printf("           \\    %-10s\n"
+               "%-10s  \\   Attempted mode\n"
+               "Current mode \\  exclusive   | shared      | none\n"
+               "                R   W  RW   | R   W  RW   | R   W  RW\n",
+               locktype_names[locktypes[1]],
+               locktype_names[locktypes[0]]);
+
+        for (int i = 0; i < 9; i++) {
+                if (i == 3 || i == 6) {
+                        printf("----------\n");
+               }
+
+                printf("%s", tableNames[i]);
+
+                for (int j = 0; j < 9; j++) {
+                        printf("%s%c   ",
+                               (j == 3 || j == 6) ? "  " : "",
+                               results[i][j]);
+                }
+                printf("\n");
         }
-        printf("\n");
-    }
+
+       return 0;
 }