"Fossies" - the Fresh Open Source Software Archive

Member "xorriso-1.5.4/libburn/sg-freebsd-port.c" (30 Jan 2021, 23296 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 "sg-freebsd-port.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 /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
    2 
    3 /*
    4    Copyright (c) 2006 - 2011 Thomas Schmitt <scdbackup@gmx.net>
    5    Provided under GPL version 2 or later.
    6 */
    7 
    8 
    9                   /* THIS CODE IS NOT FUNCTIONAL YET !!! */
   10 
   11 
   12 /*
   13 
   14 This is the main operating system dependent SCSI part of libburn. It implements
   15 the transport level aspects of SCSI control and command i/o.
   16 
   17 Present implementation: FreeBSD CAM (untested)
   18 
   19 
   20 PORTING:
   21 
   22 Porting libburn typically will consist of adding a new operating system case
   23 to the following switcher files:
   24   os.h    Operating system specific libburn definitions and declarations.
   25   sg.c    Operating system dependent transport level modules.
   26 and of deriving the following system specific files from existing examples:
   27   os-*.h  Included by os.h. You will need some general system knowledge
   28           about signals and knowledge about the storage object needs of your
   29           transport level module sg-*.c.
   30 
   31   sg-*.c  This source module. You will need special system knowledge about
   32           how to detect all potentially available drives, how to open them,
   33           eventually how to exclusively reserve them, how to perform
   34           SCSI transactions, how to inquire the (pseudo-)SCSI driver.
   35           You will not need to care about CD burning, MMC or other high-level
   36           SCSI aspects.
   37 
   38 Said sg-*.c operations are defined by a public function interface, which has
   39 to be implemented in a way that provides libburn with the desired services:
   40  
   41 sg_id_string()          returns an id string of the SCSI transport adapter.
   42                         It may be called before initialization but then may
   43                         return only a preliminary id.
   44 
   45 sg_initialize()         performs global initialization of the SCSI transport
   46                         adapter and eventually needed operating system
   47                         facilities. Checks for compatibility of supporting
   48                         software components.
   49 
   50 sg_shutdown()           performs global finalizations and releases globally
   51                         acquired resources.
   52 
   53 sg_give_next_adr()      iterates over the set of potentially useful drive 
   54                         address strings.
   55 
   56 scsi_enumerate_drives() brings all available, not-whitelist-banned, and
   57                         accessible drives into libburn's list of drives.
   58 
   59 sg_dispose_drive()      finalizes adapter specifics of struct burn_drive
   60                         on destruction. Releases resources which were acquired
   61                         underneath scsi_enumerate_drives().
   62 
   63 sg_drive_is_open()      tells whether libburn has the given drive in use.
   64 
   65 sg_grab()               opens the drive for SCSI commands and ensures
   66                         undisturbed access.
   67 
   68 sg_release()            closes a drive opened by sg_grab()
   69 
   70 sg_issue_command()      sends a SCSI command to the drive, receives reply,
   71                         and evaluates whether the command succeeded or shall
   72                         be retried or finally failed.
   73 
   74 sg_obtain_scsi_adr()    tries to obtain SCSI address parameters.
   75 
   76 
   77 burn_os_is_2k_seekrw()  tells whether the given path leads to a file object
   78                         that can be used in 2 kB granularity by lseek(2),
   79                         read(2), and possibly write(2) if not read-only.
   80                         E.g. a USB stick or a hard disk.
   81 
   82 burn_os_stdio_capacity()  estimates the emulated media space of stdio-drives.
   83 
   84 burn_os_open_track_src()  opens a disk file in a way that offers best
   85                         throughput with file reading and/or SCSI write command
   86                         transmission.
   87 
   88 burn_os_alloc_buffer()  allocates a memory area that is suitable for file
   89                         descriptors issued by burn_os_open_track_src().
   90                         The buffer size may be rounded up for alignment
   91                         reasons.
   92 
   93 burn_os_free_buffer()   delete a buffer obtained by burn_os_alloc_buffer().
   94 
   95 
   96 Porting hints are marked by the text "PORTING:".
   97 Send feedback to libburn-hackers@pykix.org .
   98 
   99 */
  100 
  101 #ifdef HAVE_CONFIG_H
  102 #include "../config.h"
  103 #endif
  104 
  105 
  106 /** PORTING : ------- OS dependent headers and definitions ------ */
  107 
  108 #include <errno.h>
  109 #include <unistd.h>
  110 #include <stdio.h>
  111 #include <sys/types.h>
  112 #include <sys/stat.h>
  113 #include <fcntl.h>
  114 #include <sys/ioctl.h>
  115 #include <stdlib.h>
  116 #include <string.h>
  117 #include <sys/poll.h>
  118 #include <camlib.h>
  119 #include <cam/scsi/scsi_message.h>
  120 #include <cam/scsi/scsi_pass.h>
  121 
  122 #include <err.h> /* XXX */
  123 
  124 
  125 /* ts A70909 */
  126 #include <sys/statvfs.h>
  127 
  128 
  129 /** PORTING : ------ libburn portable headers and definitions ----- */
  130 
  131 #include "transport.h"
  132 #include "drive.h"
  133 #include "sg.h"
  134 #include "spc.h"
  135 #include "mmc.h"
  136 #include "sbc.h"
  137 #include "debug.h"
  138 #include "toc.h"
  139 #include "util.h"
  140 
  141 #include "libdax_msgs.h"
  142 extern struct libdax_msgs *libdax_messenger;
  143 
  144 
  145 /* is in portable part of libburn */
  146 int burn_drive_is_banned(char *device_address);
  147 
  148 
  149 
  150 /* ------------------------------------------------------------------------ */
  151 /* ts A61115:  Private functions. Port only if needed by public functions   */
  152 /*            (Public functions are listed below)                           */
  153 /* ------------------------------------------------------------------------ */
  154 
  155 
  156 /* Helper function for scsi_give_next_adr() */
  157 static int sg_init_enumerator(burn_drive_enumerator_t *idx)
  158 {
  159     idx->skip_device = 0;
  160 
  161     if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) {
  162         warn("couldn't open %s", XPT_DEVICE);
  163         return -1;
  164     }
  165 
  166     memset(&(idx->ccb), 0, sizeof(union ccb));
  167 
  168     idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
  169     idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
  170     idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
  171 
  172     idx->ccb.ccb_h.func_code = XPT_DEV_MATCH;
  173     idx->bufsize = sizeof(struct dev_match_result) * 100;
  174     idx->ccb.cdm.match_buf_len = idx->bufsize;
  175     idx->ccb.cdm.matches = (struct dev_match_result *)
  176                         calloc(1, idx->bufsize);
  177     if (idx->ccb.cdm.matches == NULL) {
  178         warnx("cannot allocate memory for matches");
  179         close(idx->fd);
  180         return -1;
  181     }
  182     idx->ccb.cdm.num_matches = 0;
  183     idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */
  184 
  185     /*
  186      * We fetch all nodes, since we display most of them in the default
  187      * case, and all in the verbose case.
  188      */
  189     idx->ccb.cdm.num_patterns = 0;
  190     idx->ccb.cdm.pattern_buf_len = 0;
  191 
  192     return 1;
  193 }
  194 
  195 
  196 /* Helper function for scsi_give_next_adr() */
  197 static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx)
  198 {
  199     /*
  200      * We do the ioctl multiple times if necessary, in case there are
  201      * more than 100 nodes in the EDT.
  202      */
  203     if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) {
  204         warn("error sending CAMIOCOMMAND ioctl");
  205         return -1;
  206     }
  207 
  208     if ((idx->ccb.ccb_h.status != CAM_REQ_CMP)
  209         || ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST)
  210         && (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
  211         warnx("got CAM error %#x, CDM error %d\n",
  212               idx->ccb.ccb_h.status, idx->ccb.cdm.status);
  213         return -1;
  214     }
  215     return 1;
  216 }
  217 
  218 
  219 static int sg_close_drive(struct burn_drive * d)
  220 {
  221     if (d->cam != NULL) {
  222         cam_close_device(d->cam);
  223         d->cam = NULL;
  224     }
  225     return 0;
  226 }
  227 
  228 
  229 /* ----------------------------------------------------------------------- */
  230 /* PORTING: Private functions which contain publicly needed functionality. */
  231 /*          Their portable part must be performed. So it is probably best  */
  232 /*          to replace the non-portable part and to call these functions   */
  233 /*          in your port, too.                                             */
  234 /* ----------------------------------------------------------------------- */
  235 
  236 
  237 /** Wraps a detected drive into libburn structures and hands it over to
  238     libburn drive list.
  239 */
  240 static void enumerate_common(char *fname, int bus_no, int host_no,
  241                  int channel_no, int target_no, int lun_no)
  242 {
  243     int ret;
  244     struct burn_drive out;
  245 
  246     /* General libburn drive setup */
  247     burn_setup_drive(&out, fname);
  248 
  249     /* This transport adapter uses SCSI-family commands and models
  250        (seems the adapter would know better than its boss, if ever) */
  251     ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no,
  252                                  target_no, lun_no, 0);
  253         if (ret<=0)
  254                 return;
  255 
  256     /* PORTING: ------------------- non portable part --------------- */
  257 
  258     /* Operating system adapter is CAM */
  259     /* Adapter specific handles and data */
  260     out.cam = NULL;
  261 
  262     /* PORTING: ---------------- end of non portable part ------------ */
  263 
  264     /* Adapter specific functions with standardized names */
  265     out.grab = sg_grab;
  266     out.release = sg_release;
  267     out.drive_is_open = sg_drive_is_open;
  268     out.issue_command = sg_issue_command;
  269     /* Finally register drive and inquire drive information */
  270     burn_drive_finish_enum(&out);
  271 }
  272 
  273 
  274 /* ts A61115 */
  275 /* ------------------------------------------------------------------------ */
  276 /* PORTING:           Public functions. These MUST be ported.               */
  277 /* ------------------------------------------------------------------------ */
  278 
  279 
  280 /** Returns the id string  of the SCSI transport adapter and eventually
  281     needed operating system facilities.
  282     This call is usable even if sg_initialize() was not called yet. In that
  283     case a preliminary constant message might be issued if detailed info is
  284     not available yet.
  285     @param msg   returns id string
  286     @param flag  unused yet, submit 0
  287     @return      1 = success, <=0 = failure
  288 */
  289 int sg_id_string(char msg[1024], int flag)
  290 {
  291     strcpy(msg, "internal FreeBSD CAM adapter sg-freebsd-port");
  292     return 1;
  293 }
  294 
  295 
  296 /** Performs global initialization of the SCSI transport adapter and eventually
  297     needed operating system facilities. Checks for compatibility supporting
  298     software components.
  299     @param msg   returns ids and/or error messages of eventual helpers
  300     @param flag  unused yet, submit 0
  301     @return      1 = success, <=0 = failure
  302 */
  303 int sg_initialize(char msg[1024], int flag)
  304 {
  305     return sg_id_string(msg, 0);
  306 }
  307 
  308 
  309 /** Performs global finalization of the SCSI transport adapter and eventually
  310     needed operating system facilities. Releases globally acquired resources.
  311     @param flag  unused yet, submit 0
  312     @return      1 = success, <=0 = failure
  313 */  
  314 int sg_shutdown(int flag)
  315 {
  316     return 1;
  317 }
  318 
  319 
  320 /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of
  321     struct burn_drive which are defined in os-*.h.
  322     The eventual initialization of those components was made underneath
  323     scsi_enumerate_drives().
  324     This will be called when a burn_drive gets disposed.
  325     @param d     the drive to be finalized
  326     @param flag  unused yet, submit 0
  327     @return      1 = success, <=0 = failure
  328 */
  329 int sg_dispose_drive(struct burn_drive *d, int flag)
  330 {
  331         return 1;
  332 }
  333 
  334 
  335 /** Returns the next index number and the next enumerated drive address.
  336     The enumeration has to cover all available and accessible drives. It is
  337     allowed to return addresses of drives which are not available but under
  338     some (even exotic) circumstances could be available. It is on the other
  339     hand allowed, only to hand out addresses which can really be used right
  340     in the moment of this call. (This implementation chooses the latter.)
  341     @param idx An opaque handle. Make no own theories about it.
  342     @param adr Takes the reply
  343     @param adr_size Gives maximum size of reply including final 0
  344     @param initialize  1 = start new,
  345                        0 = continue, use no other values for now
  346                       -1 = finish
  347     @return 1 = reply is a valid address , 0 = no further address available
  348            -1 = severe error (e.g. adr_size too small)
  349 */
  350 int sg_give_next_adr(burn_drive_enumerator_t *idx,
  351              char adr[], int adr_size, int initialize)
  352 {
  353     int ret;
  354 
  355     if (initialize == 1) {
  356         ret = sg_init_enumerator(idx);
  357         if (ret<=0)
  358             return ret;
  359     } else if (initialize == -1) {
  360         if(idx->fd != -1)
  361             close(idx->fd);
  362         idx->fd = -1;
  363         return 0;
  364     }
  365 
  366 
  367 try_item:; /* This spaghetti loop keeps the number of tabs small  */
  368 
  369     /* Loop content from old scsi_enumerate_drives() */
  370 
  371     while (idx->i >= idx->ccb.cdm.num_matches) {
  372         ret = sg_next_enumeration_buffer(idx);
  373         if (ret<=0)
  374             return -1;
  375         if (!((idx->ccb.ccb_h.status == CAM_REQ_CMP)
  376             && (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)) )
  377             return 0;
  378         idx->i = 0;
  379     }
  380 
  381     switch (idx->ccb.cdm.matches[idx->i].type) {
  382     case DEV_MATCH_BUS:
  383         break;
  384     case DEV_MATCH_DEVICE: {
  385         struct device_match_result* result;
  386 
  387         result = &(idx->ccb.cdm.matches[i].result.device_result);
  388         if (result->flags & DEV_RESULT_UNCONFIGURED)
  389             idx->skip_device = 1;
  390         else
  391             idx->skip_device = 0;
  392         break;
  393     }
  394     case DEV_MATCH_PERIPH: {
  395         struct periph_match_result* result;
  396         char buf[64];
  397 
  398         result = &(idx->ccb.cdm.matches[i].result.periph_result);
  399         if (idx->skip_device || 
  400             strcmp(result->periph_name, "pass") == 0)
  401             break;
  402         snprintf(buf, sizeof (buf), "/dev/%s%d",
  403              result->periph_name, result->unit_number);
  404         if(adr_size <= strlen(buf))
  405             return -1;
  406         strcpy(adr, buf);
  407 
  408         /* Found next enumerable address */
  409         return 1;
  410 
  411     }
  412     default:
  413         /* printf(stderr, "unknown match type\n"); */
  414         break;
  415     }
  416 
  417     (idx->i)++;
  418     goto try_item; /* Regular function exit is return 1 above  */
  419 }
  420 
  421 
  422 /** Brings all available, not-whitelist-banned, and accessible drives into
  423     libburn's list of drives.
  424 */
  425 int scsi_enumerate_drives(void)
  426 {
  427     burn_drive_enumerator_t idx;
  428     int initialize = 1, ret;
  429     char buf[64];
  430 
  431     while(1) {
  432         ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
  433         initialize = 0;
  434         if (ret <= 0)
  435     break;
  436         if (burn_drive_is_banned(buf))
  437     continue; 
  438         enumerate_common(buf, idx.result->path_id, idx.result->path_id,
  439                 0, idx.result->target_id, 
  440                 idx.result->target_lun);
  441     }
  442     sg_give_next_adr(&idx, buf, sizeof(buf), -1);
  443     return 1;
  444 }
  445 
  446 
  447 /** Tells whether libburn has the given drive in use or exclusively reserved.
  448     If it is "open" then libburn will eventually call sg_release() on it when
  449     it is time to give up usage and reservation.
  450 */
  451 /** Published as burn_drive.drive_is_open() */
  452 int sg_drive_is_open(struct burn_drive * d)
  453 {
  454     return (d->cam != NULL);
  455 }
  456 
  457 
  458 /** Opens the drive for SCSI commands and - if burn activities are prone
  459     to external interference on your system - obtains an exclusive access lock
  460     on the drive. (Note: this is not physical tray locking.)
  461     A drive that has been opened with sg_grab() will eventually be handed
  462     over to sg_release() for closing and unreserving. 
  463 */  
  464 int sg_grab(struct burn_drive *d)
  465 {
  466     struct cam_device *cam;
  467 
  468     if(d->cam != NULL) {
  469         d->released = 0;
  470         return 1;
  471     }
  472 
  473     cam = cam_open_device(d->devname, O_RDWR);
  474     if (cam == NULL) {
  475         libdax_msgs_submit(libdax_messenger, d->global_index,
  476         0x00020003,
  477         LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
  478         "Could not grab drive", 0/*os_errno*/, 0);
  479         return 0;
  480     }
  481     d->cam = cam;
  482     fcntl(cam->fd, F_SETOWN, getpid());
  483     d->released = 0;
  484     return 1;
  485 }
  486 
  487 
  488 /** PORTING: Is mainly about the call to sg_close_drive() and whether it
  489              implements the demanded functionality.
  490 */
  491 /** Gives up the drive for SCSI commands and releases eventual access locks.
  492     (Note: this is not physical tray locking.) 
  493 */
  494 int sg_release(struct burn_drive *d)
  495 {
  496     if (d->cam == NULL)
  497         return 0;
  498     sg_close_drive(d);
  499     return 0;
  500 }
  501 
  502 
  503 /** Sends a SCSI command to the drive, receives reply and evaluates whether
  504     the command succeeded or shall be retried or finally failed.
  505     Returned SCSI errors shall not lead to a return value indicating failure.
  506     The callers get notified by c->error. An SCSI failure which leads not to
  507     a retry shall be notified via scsi_notify_error().
  508     The Libburn_log_sg_commandS facility might be of help when problems with
  509     a drive have to be examined. It shall stay disabled for normal use.
  510     @return: 1 success , <=0 failure
  511 */
  512 int sg_issue_command(struct burn_drive *d, struct command *c)
  513 {
  514     int done = 0;
  515     int err;
  516     union ccb *ccb;
  517 
  518     if (d->cam == NULL) {
  519         c->error = 0;
  520         return 0;
  521     }
  522 
  523     c->error = 0;
  524 
  525     ccb = cam_getccb(d->cam);
  526     cam_fill_csio(&ccb->csio,
  527                   1,                              /* retries */
  528                   NULL,                           /* cbfncp */
  529                   CAM_DEV_QFRZDIS,                /* flags */
  530                   MSG_SIMPLE_Q_TAG,               /* tag_action */
  531                   NULL,                           /* data_ptr */
  532                   0,                              /* dxfer_len */
  533                   sizeof (ccb->csio.sense_data),  /* sense_len */
  534                   0,                              /* cdb_len */
  535                   30*1000);                       /* timeout */
  536     switch (c->dir) {
  537     case TO_DRIVE:
  538         ccb->csio.ccb_h.flags |= CAM_DIR_OUT;
  539         break;
  540     case FROM_DRIVE:
  541         ccb->csio.ccb_h.flags |= CAM_DIR_IN;
  542         break;
  543     case NO_TRANSFER:
  544         ccb->csio.ccb_h.flags |= CAM_DIR_NONE;
  545         break;
  546     }
  547 
  548     ccb->csio.cdb_len = c->oplen;
  549     memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen);
  550     
  551     if (c->page) {
  552         ccb->csio.data_ptr  = c->page->data;
  553         if (c->dir == FROM_DRIVE) {
  554             ccb->csio.dxfer_len = BUFFER_SIZE;
  555 /* touch page so we can use valgrind */
  556             memset(c->page->data, 0, BUFFER_SIZE);
  557         } else {
  558 
  559             /* ts A61115: removed a ssert() */
  560             if(c->page->bytes <= 0)
  561                 return 0;
  562 
  563             ccb->csio.dxfer_len = c->page->bytes;
  564         }
  565     } else {
  566         ccb->csio.data_ptr  = NULL;
  567         ccb->csio.dxfer_len = 0;
  568     }
  569 
  570     /* ts B90523 : Record effective transfer length request for debugging*/
  571     c->dxfer_len = ccb->csio.dxfer_len;
  572 
  573     do {
  574         memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data));
  575         err = cam_send_ccb(d->cam, ccb);
  576         if (err == -1) {
  577             libdax_msgs_submit(libdax_messenger,
  578                 d->global_index, 0x0002010c,
  579                 LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
  580                 "Failed to transfer command to drive",
  581                 errno, 0);
  582             cam_freeccb(ccb);
  583             sg_close_drive(d);
  584             d->released = 1;
  585             d->busy = BURN_DRIVE_IDLE;
  586             c->error = 1;
  587             return -1;
  588         }
  589         /* XXX */
  590         memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len);
  591         c->sense_len = ccb->csio.sense_len;
  592         if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
  593             if (!c->retry) {
  594                 c->error = 1;
  595                 cam_freeccb(ccb);
  596                 return 1;
  597             }
  598             switch (scsi_error(d, c->sense, 0)) {
  599             case RETRY:
  600                 done = 0;
  601                 break;
  602             case FAIL:
  603                 done = 1;
  604                 c->error = 1;
  605                 break;
  606             }
  607         } else {
  608             done = 1;
  609         }
  610         if (!done)
  611             spc_register_retry(c);
  612     } while (!done);
  613     cam_freeccb(ccb);
  614     return 1;
  615 }
  616 
  617 
  618 /** Tries to obtain SCSI address parameters.
  619     @return  1 is success , 0 is failure
  620 */
  621 int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
  622                        int *target_no, int *lun_no)
  623 {
  624     burn_drive_enumerator_t idx;
  625     int initialize = 1, ret;
  626     char buf[64];
  627     struct periph_match_result* result;
  628 
  629     while(1) {
  630         ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
  631         initialize = 0;
  632         if (ret <= 0)
  633     break;
  634         if (strcmp(path, buf) != 0)
  635     continue;
  636         result = &(idx->ccb.cdm.matches[i].result.periph_result);
  637         *bus_no = result->path_id;
  638         *host_no = result->path_id;
  639         *channel_no = 0;
  640         *target_no = result->target_id
  641         *lun_no = result->target_lun;
  642         sg_give_next_adr(&idx, buf, sizeof(buf), -1);
  643         return 1;
  644     }
  645     sg_give_next_adr(&idx, buf, sizeof(buf), -1);
  646     return (0);
  647 }
  648 
  649 
  650 /** Tells whether a text is a persistent address as listed by the enumeration
  651     functions.
  652 */
  653 int sg_is_enumerable_adr(char* adr)
  654 {
  655     burn_drive_enumerator_t idx;
  656     int initialize = 1, ret;
  657     char buf[64];
  658 
  659     while(1) {
  660         ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
  661         initialize = 0;
  662         if (ret <= 0)
  663     break;
  664         if (strcmp(adr, buf) == 0) {
  665             sg_give_next_adr(&idx, buf, sizeof(buf), -1);
  666             return 1;
  667         }
  668     }
  669     sg_give_next_adr(&idx, buf, sizeof(buf), -1);
  670     return (0);
  671 }
  672 
  673 
  674 /* ts B00115 */
  675 /* Return 1 if the given path leads to a regular file or a device that can be
  676    seeked, written, and read with 2 kB granularity.
  677 */
  678 int burn_os_is_2k_seekrw(char *path, int flag)
  679 {
  680         struct stat stbuf;
  681     char *spt;
  682     int i, e;
  683 
  684         if (stat(path, &stbuf) == -1)
  685                 return 0;
  686         if (S_ISREG(stbuf.st_mode))
  687                 return 1;
  688     if (!S_ISCHR(stbuf.st_mode))
  689         return 0;
  690     spt = strrchr(path, '/');
  691     if (spt == NULL)
  692             spt = path;
  693     else
  694             spt++;
  695     e = strlen(spt);
  696     for (i = strlen(spt) - 1; i > 0; i--)
  697         if (spt[i] >= '0' && spt[i] <= '9')
  698             e = i;
  699     if (strncmp(spt, "da", e) == 0) /* SCSI disk. E.g. USB stick. */
  700         return 1;
  701     if (strncmp(spt, "cd", e) == 0) /* SCSI CD drive might be writeable. */
  702         return 1;
  703     if (strncmp(spt, "ad", e) == 0) /* IDE hard drive */
  704         return 1;
  705     if (strncmp(spt, "acd", e) == 0) /* IDE CD drive might be writeable */
  706         return 1;
  707     if (strncmp(spt, "fd", e) == 0) /* Floppy disk */
  708         return 1;
  709     if (strncmp(spt, "fla", e) == 0) /* Flash drive */
  710         return 1;
  711     return 0;
  712 }
  713 
  714 
  715 /* ts A70909 */
  716 /** Estimate the potential payload capacity of a file address.
  717     @param path  The address of the file to be examined. If it does not
  718                  exist yet, then the directory will be inquired.
  719     @param bytes This value gets modified if an estimation is possible
  720     @return      -2 = cannot perform necessary operations on file object
  721                  -1 = neither path nor dirname of path exist
  722                   0 = could not estimate size capacity of file object
  723                   1 = estimation has been made, bytes was set
  724 */
  725 int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes)
  726 {
  727     struct stat stbuf;
  728     struct statvfs vfsbuf;
  729     char *testpath = NULL, *cpt;
  730     off_t add_size = 0;
  731     int fd, ret;
  732 
  733     BURN_ALLOC_MEM(testpath, char, 4096);
  734     testpath[0] = 0;
  735     if (stat(path, &stbuf) == -1) {
  736         strcpy(testpath, path);
  737         cpt = strrchr(testpath, '/');
  738         if(cpt == NULL)
  739             strcpy(testpath, ".");
  740         else if(cpt == testpath)
  741             testpath[1] = 0;
  742         else
  743             *cpt = 0;
  744         if (stat(testpath, &stbuf) == -1)
  745             {ret = -1; goto ex;}
  746 
  747 #ifdef Libburn_if_this_was_linuX
  748 
  749     } else if(S_ISBLK(stbuf.st_mode)) {
  750         int open_mode = O_RDWR, fd, ret;
  751         long blocks;
  752 
  753         blocks = *bytes / 512;
  754         if(burn_sg_open_o_excl)
  755             open_mode |= O_EXCL;
  756         fd = open(path, open_mode);
  757         if (fd == -1)
  758             {ret = -2; goto ex;}
  759         ret = ioctl(fd, BLKGETSIZE, &blocks);
  760         close(fd);
  761         if (ret == -1)
  762             {ret = -2; goto ex;}
  763         *bytes = ((off_t) blocks) * (off_t) 512;
  764 
  765 #endif /* Libburn_if_this_was_linuX */
  766 
  767 
  768     } else if(S_ISCHR(stbuf.st_mode)) {
  769         fd = open(path, O_RDONLY);
  770         if (fd == -1)
  771             {ret = -2; goto ex;}
  772         ret = ioctl(fd, DIOCGMEDIASIZE, &add_size);
  773         close(fd);
  774         if (ret == -1)
  775             {ret = -2; goto ex;}
  776         *bytes = add_size;
  777     } else if(S_ISREG(stbuf.st_mode)) {
  778         add_size = burn_sparse_file_addsize(write_start, &stbuf);
  779         strcpy(testpath, path);
  780     } else
  781         {ret = 0; goto ex;}
  782 
  783     if (testpath[0]) {  
  784         if (statvfs(testpath, &vfsbuf) == -1)
  785             {ret = -2; goto ex;}
  786         *bytes = add_size + ((off_t) vfsbuf.f_frsize) *
  787                         (off_t) vfsbuf.f_bavail;
  788     }
  789     ret = 1;
  790 ex:
  791     BURN_FREE_MEM(testpath);
  792     return ret;
  793 }
  794 
  795 
  796 /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */
  797 
  798 #ifdef Libburn_read_o_direcT
  799 
  800     /* No special O_DIRECT-like precautions are implemented here */
  801 
  802 #endif /* Libburn_read_o_direcT */
  803 
  804 
  805 int burn_os_open_track_src(char *path, int open_flags, int flag)
  806 {
  807     int fd;
  808 
  809     fd = open(path, open_flags);
  810     return fd;
  811 }
  812 
  813 
  814 void *burn_os_alloc_buffer(size_t amount, int flag)
  815 {
  816     void *buf = NULL;
  817 
  818     buf = calloc(1, amount);
  819     return buf;
  820 }
  821 
  822 
  823 int burn_os_free_buffer(void *buffer, size_t amount, int flag)
  824 {
  825     if (buffer == NULL)
  826         return 0;
  827     free(buffer);
  828     return 1;
  829 }
  830