"Fossies" - the Fresh Open Source Software Archive

Member "xorriso-1.5.4/libburn/ddlpa.c" (30 Jan 2021, 14247 Bytes) of package /linux/misc/xorriso-1.5.4.pl02.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ddlpa.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.5.2_vs_1.5.4.

    1 
    2 /* ddlpa
    3    Implementation of Delicate Device Locking Protocol level A.
    4    Copyright (C) 2007 Thomas Schmitt <scdbackup@gmx.net>
    5    Provided under any of the following licenses: GPL, LGPL, BSD. Choose one.
    6 
    7 
    8    Compile as test program:
    9 
   10      cc -g -Wall \
   11         -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 -D_LARGEFILE64_SOURCE \
   12         -DDDLPA_C_STANDALONE -o ddlpa ddlpa.c
   13 
   14    The system macros enable 64-bit off_t and open(2) flag O_LARGEFILE, which
   15    are not absolutely necessary but explicitly take into respect that
   16    our devices can offer more than 2 GB of addressable data. 
   17 
   18    Run test program:
   19 
   20      ./ddlpa /dev/sr0 15
   21      ./ddlpa 0,0,0    15
   22 
   23 */
   24 
   25 #ifdef HAVE_CONFIG_H
   26 #include "../config.h"
   27 #endif
   28 
   29 #include <stdio.h>
   30 #include <sys/types.h>
   31 #include <unistd.h>
   32 #include <string.h>
   33 #include <stdlib.h>
   34 #include <errno.h>
   35 #include <sys/stat.h>
   36 #include <fcntl.h>
   37 #include <sys/ioctl.h>
   38 #include <scsi/scsi.h>
   39 
   40 
   41 /* All callers of ddlpa must do this */
   42 #include "ddlpa.h"
   43 
   44 
   45 /* 1 = Enable progress message on stderr, 0 = normal silent operation */
   46 static int ddlpa_debug_mode = 1;
   47 
   48 
   49 /* #define _GNU_SOURCE  or  _LARGEFILE64_SOURCE  to get real O_LARGEFILE */
   50 #ifndef O_LARGEFILE
   51 #define O_LARGEFILE 0
   52 #endif
   53 #ifndef O_BINARY
   54 #define O_BINARY 0
   55 #endif
   56 
   57 /* ----------------------- private -------------------- */
   58 
   59 
   60 static int ddlpa_new(struct ddlpa_lock **lck, int o_flags, int ddlpa_flags)
   61 {
   62     int i;
   63     struct ddlpa_lock *o;
   64 
   65     o = *lck = (struct ddlpa_lock *) malloc(sizeof(struct ddlpa_lock));
   66     if (o == NULL)
   67         return ENOMEM;
   68     for (i = 0; i < sizeof(struct ddlpa_lock); i++)
   69         ((char *) o)[i] = 0;
   70     o->path = NULL;
   71     o->fd = -1;
   72     for (i = 0; i < DDLPA_MAX_SIBLINGS; i++)
   73         o->sibling_fds[i] = -1;
   74     o->errmsg = NULL;
   75 
   76     o->o_flags = o_flags;
   77     o->ddlpa_flags = ddlpa_flags;
   78     return 0;
   79 }
   80 
   81 
   82 static int ddlpa_enumerate(struct ddlpa_lock *o, int *idx,
   83             char path[DDLPA_MAX_STD_LEN + 1])
   84 {
   85     if (*idx < 0)
   86         *idx = 0;
   87 
   88     if (*idx < 26)
   89         sprintf(path, "/dev/hd%c", 'a' + *idx);
   90     else if (*idx < 256 + 26)
   91         sprintf(path, "/dev/sr%d", *idx - 26);
   92     else if (*idx < 2 * 256 + 26)
   93         sprintf(path, "/dev/scd%d", *idx - 256 - 26);
   94     else if (*idx < 3 * 256 + 26)
   95         sprintf(path, "/dev/sg%d", *idx - 2 * 256 - 26);
   96     else
   97         return 1;
   98     (*idx)++;
   99     return 0;
  100 }
  101 
  102 
  103 static int ddlpa_std_by_rdev(struct ddlpa_lock *o)
  104 {
  105     int idx = 0;
  106     char try_path[DDLPA_MAX_STD_LEN+1];
  107     struct stat path_stbuf, try_stbuf;
  108 
  109     if (!o->path_is_valid)
  110         return EFAULT;
  111     if (stat(o->path, &path_stbuf) == -1)
  112         return errno;
  113 
  114     while (ddlpa_enumerate(o, &idx, try_path) == 0) {
  115         if (stat(try_path, &try_stbuf) == -1)
  116             continue;
  117         if (path_stbuf.st_rdev != try_stbuf.st_rdev)
  118             continue;
  119         strcpy(o->std_path, try_path);
  120 
  121         if (ddlpa_debug_mode)
  122             fprintf(stderr,
  123                "DDLPA_DEBUG: ddlpa_std_by_rdev(\"%s\") = \"%s\"\n",
  124                 o->path, o->std_path);
  125 
  126         return 0;
  127     }
  128     return ENOENT;
  129 }
  130 
  131 
  132 /* Caution : these tests are valid only with standard paths */
  133 
  134 static int ddlpa_is_scsi(struct ddlpa_lock *o, char *path)
  135 {
  136     return (strncmp(path, "/dev/s", 6) == 0);
  137 }
  138 
  139 static int ddlpa_is_sg(struct ddlpa_lock *o, char *path)
  140 {
  141     return (strncmp(path, "/dev/sg", 7) == 0);
  142 }
  143 
  144 static int ddlpa_is_sr(struct ddlpa_lock *o, char *path)
  145 {
  146     return (strncmp(path, "/dev/sr", 7) == 0);
  147 }
  148 
  149 static int ddlpa_is_scd(struct ddlpa_lock *o, char *path)
  150 {
  151     return (strncmp(path, "/dev/scd", 8) == 0);
  152 }
  153 
  154 
  155 static int ddlpa_fcntl_lock(struct ddlpa_lock *o, int fd, int l_type)
  156 {
  157     struct flock lockthing;
  158     int ret;
  159 
  160     memset(&lockthing, 0, sizeof(lockthing));
  161     lockthing.l_type = l_type;
  162     lockthing.l_whence = SEEK_SET;
  163     lockthing.l_start = 0;
  164     lockthing.l_len = 0;
  165     ret = fcntl(fd, F_SETLK, &lockthing);
  166     if (ret == -1)
  167         return EBUSY;
  168     return 0;
  169 }
  170 
  171 
  172 static int ddlpa_occupy(struct ddlpa_lock *o, char *path, int *fd,
  173             int no_o_excl)
  174 {
  175     int ret, o_flags, o_rw, l_type;
  176     char *o_rwtext;
  177 
  178     o_flags = o->o_flags | O_NDELAY | O_BINARY;
  179     if(!no_o_excl)
  180         o_flags |= O_EXCL;
  181     o_rw = (o_flags) & (O_RDONLY | O_WRONLY | O_RDWR);
  182     o_rwtext = (o_rw == O_RDONLY ? "O_RDONLY" :
  183             (o_rw == O_WRONLY ? "O_WRONLY" :
  184             (o_rw == O_RDWR ? "O_RDWR  " : "O_?rw-mode?")));
  185 
  186     *fd = open(path, o_flags);
  187     if (*fd == -1) {
  188         o->errmsg = malloc(strlen(path)+160);
  189         if (o->errmsg)
  190             sprintf(o->errmsg,
  191                 "Failed to open %s | O_NDELAY %s: '%s'", 
  192                 o_rwtext,
  193                 (o_flags & O_EXCL ? "| O_EXCL " : ""), path);
  194         return (errno ? errno : EBUSY);
  195     }
  196     if (o_rw == O_RDWR || o_rw == O_WRONLY)
  197         l_type = F_WRLCK;
  198     else
  199         l_type = F_RDLCK;
  200     ret = ddlpa_fcntl_lock(o, *fd, l_type);
  201     if (ret) {
  202         o->errmsg = malloc(strlen(path)+160);
  203         if (o->errmsg)
  204             sprintf(o->errmsg,
  205                 "Failed to lock fcntl(F_WRLCK) : '%s'",path);
  206         close(*fd);
  207         *fd = -1;
  208         return ret;
  209     }
  210     if (ddlpa_debug_mode)
  211         fprintf(stderr, "DDLPA_DEBUG: ddlpa_occupy() %s %s: '%s'\n",
  212             o_rwtext,
  213             (no_o_excl ? "       " : "O_EXCL "), path);
  214     return 0;
  215 }
  216 
  217 
  218 static int ddlpa_obtain_scsi_adr(struct ddlpa_lock *o, char *path,
  219             int *bus, int *host, int *channel, int *id, int *lun)
  220 {
  221     int fd, ret, open_mode = O_RDONLY | O_NDELAY | O_BINARY;
  222     struct my_scsi_idlun {
  223         int x;
  224         int host_unique_id;
  225     };
  226     struct my_scsi_idlun idlun;
  227 
  228     fd = open(path, open_mode);
  229     if (fd == -1)
  230         return (errno ? errno : EBUSY);
  231     if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, bus) == -1)
  232         *bus = -1;
  233     ret = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun);
  234     close(fd);
  235     if (ret  == -1)
  236         return (errno ? errno : EIO);
  237     *host =    (idlun.x >> 24) & 255;
  238     *channel = (idlun.x >> 16) & 255;
  239     *id =      (idlun.x)       & 255;
  240     *lun =     (idlun.x >> 8 ) & 255;
  241     return 0;
  242 }
  243 
  244 
  245 static int ddlpa_collect_siblings(struct ddlpa_lock *o)
  246 {
  247     int idx = 0, ret, have_sg = 0, have_sr = 0, have_scd = 0;
  248     dev_t path_dev;
  249     ino_t path_inode;
  250     struct stat stbuf;
  251     char *path, try_path[DDLPA_MAX_STD_LEN+1];
  252     int t_bus, t_host, t_channel, t_id, t_lun;
  253     
  254     if (o->ddlpa_flags & DDLPA_OPEN_GIVEN_PATH)
  255         path = o->path;
  256     else
  257         path = o->std_path;
  258     if (path[0] == 0 || o->num_siblings != 0)
  259         return EFAULT;
  260     if (!ddlpa_is_scsi(o, o->std_path))
  261         return EFAULT;
  262 
  263     if (stat(path, &stbuf) == -1)
  264         return errno;
  265     path_inode = stbuf.st_ino;
  266     path_dev = stbuf.st_dev;
  267     o->rdev = stbuf.st_rdev;
  268     o->dev = stbuf.st_dev;
  269     o->ino = stbuf.st_ino;
  270     ret = ddlpa_obtain_scsi_adr(o, path,
  271                 &(o->bus), &(o->host), &(o->channel),
  272                 &(o->id), &(o->lun));
  273     if (ret) {
  274         o->errmsg = strdup(
  275             "Cannot obtain SCSI parameters host,channel,id,lun");
  276         return ret;
  277     }
  278     o->hcilb_is_valid = 1;
  279 
  280     while (ddlpa_enumerate(o, &idx, try_path) == 0) {
  281         if (!ddlpa_is_scsi(o, try_path))
  282             continue;
  283         if (stat(try_path, &stbuf) == -1)
  284             continue;
  285         ret = ddlpa_obtain_scsi_adr(o, try_path, 
  286                 &t_bus, &t_host, &t_channel, &t_id, &t_lun);
  287         if (ret) {
  288 
  289             /* >>> interpret error, memorize busy, no permission */
  290 
  291             continue;
  292         }
  293         if (t_host != o->host || t_channel != o->channel ||
  294             t_id != o->id     || t_lun != o->lun)
  295             continue;
  296 
  297         if (o->num_siblings >= DDLPA_MAX_SIBLINGS) {
  298             o->errmsg = 
  299                 strdup("Too many matching device files found");
  300             return ERANGE;
  301         }
  302         if (ddlpa_is_sg(o, try_path))
  303             have_sg = 1;
  304         else if (ddlpa_is_sr(o, try_path))
  305                     have_sr = 1;
  306         else if (ddlpa_is_scd(o, try_path))
  307                     have_scd = 1;
  308         strcpy(o->sibling_paths[o->num_siblings], try_path);
  309         o->sibling_rdevs[o->num_siblings] = stbuf.st_rdev;
  310         o->sibling_devs[o->num_siblings] = stbuf.st_dev;
  311         o->sibling_inodes[o->num_siblings] = stbuf.st_ino;
  312 
  313         if (ddlpa_debug_mode)
  314             fprintf(stderr,
  315         "DDLPA_DEBUG: ddlpa_collect_siblings() found   \"%s\"\n",
  316             try_path);
  317 
  318         (o->num_siblings)++;
  319     }
  320     if (have_sg && have_sr && have_scd)
  321         return 0;
  322     if (o->ddlpa_flags & DDLPA_ALLOW_MISSING_SGRCD)
  323         return 0;
  324 
  325     o->errmsg = strdup("Did not find enough siblings");
  326 
  327     /* >>> add more info about busy and forbidden paths */
  328 
  329     return EBUSY;
  330 }
  331 
  332 
  333 static int ddlpa_std_by_btl(struct ddlpa_lock *o)
  334 {
  335     int idx = 0, ret;
  336     char try_path[DDLPA_MAX_STD_LEN+1];
  337     int t_bus, t_host, t_channel, t_id, t_lun;
  338 
  339     if (!o->inbtl_is_valid)
  340         return EFAULT;
  341 
  342     while (ddlpa_enumerate(o, &idx, try_path) == 0) {
  343         if (!ddlpa_is_sr(o, try_path))
  344             continue;
  345         ret = ddlpa_obtain_scsi_adr(o, try_path, 
  346                 &t_bus, &t_host, &t_channel, &t_id, &t_lun);
  347         if (ret) {
  348 
  349             /* >>> interpret error, memorize busy, no permission */
  350 
  351             continue;
  352         }
  353         if (t_bus != o->in_bus || t_id != o->in_target ||
  354             t_lun != o->in_lun)
  355             continue;
  356         strcpy(o->std_path, try_path);
  357 
  358         if (ddlpa_debug_mode)
  359             fprintf(stderr,
  360              "DDLPA_DEBUG: ddlpa_std_by_btl(%d,%d,%d) = \"%s\"\n",
  361              t_bus, t_id, t_lun, o->std_path);
  362 
  363         return 0;
  364     }
  365 
  366     /* >>> add more info about busy and forbidden paths */
  367 
  368     return ENOENT;
  369 }
  370 
  371 
  372 static int ddlpa_open_all(struct ddlpa_lock *o)
  373 {
  374     int i, j, ret, no_o_excl;
  375 
  376     if (ddlpa_is_scsi(o, o->std_path)) {
  377         ret = ddlpa_collect_siblings(o);
  378         if (ret)
  379             return ret;
  380         for (i = 0; i < o->num_siblings; i++) {
  381 
  382             /* Watch out for the main personality of the drive. */
  383             /* No need to occupy identical path or softlink path */
  384             if (o->sibling_devs[i] == o->dev &&
  385                 o->sibling_inodes[i] == o->ino)
  386                     continue;
  387             /* There may be the same rdev but different inode. */
  388             no_o_excl = (o->sibling_rdevs[i] == o->rdev);
  389         
  390             /* Look for multiply registered device drivers with
  391                distinct inodes. */  
  392             for (j = 0; j < i; j++) {
  393                 if (o->sibling_devs[j] == o->sibling_devs[i] &&
  394                   o->sibling_inodes[j] == o->sibling_inodes[i])
  395                     break;
  396                 if (o->sibling_rdevs[j] == o->sibling_rdevs[i])
  397                     no_o_excl = 1;
  398             }
  399             if (j < i)
  400                 continue;   /* inode is already occupied */
  401 
  402             ret = ddlpa_occupy(o, o->sibling_paths[i],
  403                        &(o->sibling_fds[i]), no_o_excl);
  404             if (ret)
  405                 return ret;
  406         }
  407     }
  408 
  409     if (o->ddlpa_flags & DDLPA_OPEN_GIVEN_PATH)
  410         ret = ddlpa_occupy(o, o->path, &(o->fd), 0);
  411     else
  412         ret = ddlpa_occupy(o, o->std_path, &(o->fd), 0);
  413     if (ret)
  414         return ret; 
  415 
  416     /* >>> use fcntl() to adjust O_NONBLOCK */;
  417 
  418     return 0;
  419 }
  420 
  421 
  422 /* ----------------------- public -------------------- */
  423 
  424 
  425 int ddlpa_destroy(struct ddlpa_lock **lockbundle)
  426 {
  427     struct ddlpa_lock *o;
  428     int i;
  429 
  430     o= *lockbundle;
  431     if (o == NULL)
  432         return 0;
  433     for (i = 0; i < o->num_siblings; i++)
  434         if (o->sibling_fds[i] != -1)
  435             close(o->sibling_fds[i]);
  436     if(o->fd != -1)
  437         close(o->fd);
  438     if (o->path != NULL)
  439         free(o->path);
  440     if (o->errmsg != NULL)
  441         free(o->errmsg);
  442     free((char *) o);
  443     *lockbundle = NULL;
  444     return 0;
  445 }
  446 
  447 
  448 int ddlpa_lock_path(char *path, int o_flags, int ddlpa_flags,
  449             struct ddlpa_lock **lockbundle, char **errmsg)
  450 {
  451     struct ddlpa_lock *o;
  452     int ret;
  453 
  454     *errmsg = NULL;
  455     if (ddlpa_new(&o, o_flags, ddlpa_flags))
  456         return ENOMEM;
  457     *lockbundle = o;
  458 
  459     o->path = strdup(path);
  460     if (o->path == NULL)
  461         return ENOMEM;
  462     o->path_is_valid = 1;
  463 
  464     ret = ddlpa_std_by_rdev(o);
  465     if (ret) {
  466         *errmsg = strdup(
  467           "Cannot find equivalent of given path among standard paths"); 
  468         return ret;
  469     }
  470         ret = ddlpa_open_all(o);
  471     if (ret) {
  472         *errmsg = o->errmsg;
  473         o->errmsg = NULL;
  474         ddlpa_destroy(&o);
  475     }
  476     return ret;
  477 }
  478 
  479 
  480 int ddlpa_lock_btl(int bus, int target, int lun,
  481             int  o_flags, int ddlpa_flags,
  482             struct ddlpa_lock **lockbundle, char **errmsg)
  483 {
  484     struct ddlpa_lock *o;
  485     int ret;
  486 
  487     *errmsg = NULL;
  488     ddlpa_flags &= ~DDLPA_OPEN_GIVEN_PATH;
  489     if (ddlpa_new(&o, o_flags, ddlpa_flags))
  490         return ENOMEM;
  491     *lockbundle = o;
  492     
  493     o->in_bus = bus;
  494     o->in_target = target;
  495     o->in_lun = lun;
  496     o->inbtl_is_valid = 1;
  497     ret = ddlpa_std_by_btl(o);
  498     if (ret) {
  499         *errmsg = strdup(
  500           "Cannot find /dev/sr* with given Bus,Target,Lun");    
  501         return ret;
  502     }
  503         ret = ddlpa_open_all(o);
  504     if (ret) {
  505         *errmsg = o->errmsg;
  506         o->errmsg = NULL;
  507         ddlpa_destroy(&o);
  508         return ret;
  509     }
  510     return 0;
  511 }
  512 
  513 
  514 #ifdef DDLPA_C_STANDALONE
  515 
  516 /* ----------------------------- Test / Demo -------------------------- */
  517 
  518 
  519 int main(int argc, char **argv)
  520 {
  521     struct ddlpa_lock *lck = NULL;
  522     char *errmsg = NULL, *opened_path = NULL, *my_path = NULL;
  523     int i, ret, fd = -1, duration = -1, bus = -1, target = -1, lun = -1;
  524 
  525     if (argc < 3) {
  526 usage:;
  527         fprintf(stderr, "usage: %s  device_path  duration\n", argv[0]);
  528         exit(1);
  529     }
  530     my_path = argv[1];
  531     sscanf(argv[2], "%d", &duration);
  532     if (duration < 0)
  533         goto usage;
  534 
  535 
  536     /* For our purpose, only O_RDWR is a suitable access mode.
  537        But in order to allow experiments, o_flags are freely adjustable.
  538 
  539        Warning: Do _not_ set an own O_EXCL flag with the following calls !
  540 
  541        (This freedom to fail may get removed in a final version.)
  542     */
  543     if (my_path[0] != '/' && my_path[0] != '.' &&
  544         strchr(my_path, ',') != NULL) {
  545         /* 
  546            cdrecord style dev=Bus,Target,Lun
  547         */
  548 
  549         sscanf(my_path, "%d,%d,%d", &bus, &target, &lun);
  550         ret = ddlpa_lock_btl(bus, target, lun, O_RDWR | O_LARGEFILE,
  551                      0, &lck, &errmsg);
  552     } else {
  553         /*
  554            This substitutes for:
  555             fd = open(my_path,
  556                       O_RDWR | O_EXCL | O_LARGEFILE | O_BINARY);
  557 
  558         */
  559 
  560         ret = ddlpa_lock_path(my_path, O_RDWR | O_LARGEFILE,
  561                      0, &lck, &errmsg);
  562     }
  563     if (ret) {
  564         fprintf(stderr, "Cannot exclusively open '%s'\n", my_path);
  565         if (errmsg != NULL)
  566             fprintf(stderr, "Reason given    : %s\n",
  567                 errmsg);
  568         free(errmsg);
  569         fprintf(stderr, "Error condition : %d '%s'\n",
  570             ret, strerror(ret));
  571         exit(2);
  572     }
  573     fd = lck->fd;
  574 
  575     printf("---------------------------------------------- Lock gained\n");
  576 
  577 
  578     /* Use fd for the usual operations on the device depicted by my_path.
  579     */
  580 
  581 
  582     /* This prints an overview of the impact of the lock */
  583     if (lck->ddlpa_flags & DDLPA_OPEN_GIVEN_PATH)
  584         opened_path = lck->path;
  585     else
  586             opened_path = lck->std_path;
  587     printf("ddlpa: opened           %s", opened_path);
  588 
  589     if (strcmp(opened_path, lck->std_path) != 0)
  590         printf(" (an alias of '%s')", lck->std_path);
  591     printf("\n");
  592     if (lck->num_siblings > 0) {
  593         printf("ddlpa: opened siblings:");
  594         for (i = 0; i < lck->num_siblings; i++)
  595             if (lck->sibling_fds[i] != -1)
  596                 printf(" %s", lck->sibling_paths[i]);
  597         printf("\n");
  598     }
  599 
  600 
  601     /* This example waits a while. So other lock candidates can collide. */
  602     for (i = 0; i < duration; i++) {
  603         sleep(1);
  604         fprintf(stderr, "\rslept %d seconds of %d", i + 1, duration);
  605     }
  606     fprintf(stderr, "\n");
  607 
  608 
  609     /* When finally done with the drive, this substitutes for:
  610         close(fd);
  611     */
  612     if (ddlpa_destroy(&lck)) {
  613         /* Well, man 2 close says it can fail. */
  614         exit(3);
  615     }
  616     exit(0);
  617 }
  618 
  619 
  620 #endif /* DDLPA_C_STANDALONE */
  621