Esempio n. 1
0
/*
 * Fill out the disk parameter structure. Return SDGP_RESULT_OK if the
 * structure is correctly filled in, SDGP_RESULT_OFFLINE otherwise. The caller
 * is responsible for clearing the SDEV_MEDIA_LOADED flag if the structure
 * cannot be completed.
 */
int
sd_get_parms(struct sd_softc *sc, struct disk_parms *dp, int flags)
{
	union scsi_mode_sense_buf *buf = NULL;
	struct page_rigid_geometry *rigid = NULL;
	struct page_flex_geometry *flex = NULL;
	struct page_reduced_geometry *reduced = NULL;
	u_char *page0 = NULL;
	u_int32_t heads = 0, sectors = 0, cyls = 0, secsize = 0;
	int err = 0, big;

	if (sd_size(sc, flags) != 0)
		return (SDGP_RESULT_OFFLINE);

	if (ISSET(sc->flags, SDF_THIN) && sd_thin_params(sc, flags) != 0) {
		/* we dont know the unmap limits, so we cant use thin shizz */
		CLR(sc->flags, SDF_THIN);
	}

	buf = dma_alloc(sizeof(*buf), PR_NOWAIT);
	if (buf == NULL)
		goto validate;

	/*
	 * Ask for page 0 (vendor specific) mode sense data to find
	 * READONLY info. The only thing USB devices will ask for. 
	 */
	err = scsi_do_mode_sense(sc->sc_link, 0, buf, (void **)&page0,
	    NULL, NULL, NULL, 1, flags | SCSI_SILENT, &big);
	if (err == 0) {
		if (big && buf->hdr_big.dev_spec & SMH_DSP_WRITE_PROT)
			SET(sc->sc_link->flags, SDEV_READONLY);
		else if (!big && buf->hdr.dev_spec & SMH_DSP_WRITE_PROT)
			SET(sc->sc_link->flags, SDEV_READONLY);
		else
			CLR(sc->sc_link->flags, SDEV_READONLY);
	}

	/*
	 * Many UMASS devices choke when asked about their geometry. Most
	 * don't have a meaningful geometry anyway, so just fake it if
	 * scsi_size() worked.
	 */
	if ((sc->sc_link->flags & SDEV_UMASS) && (dp->disksize > 0))
		goto validate;

	switch (sc->sc_link->inqdata.device & SID_TYPE) {
	case T_OPTICAL:
		/* No more information needed or available. */
		break;

	case T_RDIRECT:
		/* T_RDIRECT supports only PAGE_REDUCED_GEOMETRY (6). */
		err = scsi_do_mode_sense(sc->sc_link, PAGE_REDUCED_GEOMETRY,
		    buf, (void **)&reduced, NULL, NULL, &secsize,
		    sizeof(*reduced), flags | SCSI_SILENT, NULL);
		if (!err && reduced &&
		    DISK_PGCODE(reduced, PAGE_REDUCED_GEOMETRY)) {
			if (dp->disksize == 0)
				dp->disksize = _5btol(reduced->sectors);
			if (secsize == 0)
				secsize = _2btol(reduced->bytes_s);
		}
		break;

	default:
		/*
		 * NOTE: Some devices leave off the last four bytes of 
		 * PAGE_RIGID_GEOMETRY and PAGE_FLEX_GEOMETRY mode sense pages.
		 * The only information in those four bytes is RPM information
		 * so accept the page. The extra bytes will be zero and RPM will
		 * end up with the default value of 3600.
		 */
		if (((sc->sc_link->flags & SDEV_ATAPI) == 0) ||
		    ((sc->sc_link->flags & SDEV_REMOVABLE) == 0))
			err = scsi_do_mode_sense(sc->sc_link,
			    PAGE_RIGID_GEOMETRY, buf, (void **)&rigid, NULL,
			    NULL, &secsize, sizeof(*rigid) - 4,
			    flags | SCSI_SILENT, NULL);
		if (!err && rigid && DISK_PGCODE(rigid, PAGE_RIGID_GEOMETRY)) {
			heads = rigid->nheads;
			cyls = _3btol(rigid->ncyl);
			if (heads * cyls > 0)
				sectors = dp->disksize / (heads * cyls);
		} else {
			err = scsi_do_mode_sense(sc->sc_link,
			    PAGE_FLEX_GEOMETRY, buf, (void **)&flex, NULL, NULL,
			    &secsize, sizeof(*flex) - 4,
			    flags | SCSI_SILENT, NULL);
			if (!err && flex &&
			    DISK_PGCODE(flex, PAGE_FLEX_GEOMETRY)) {
				sectors = flex->ph_sec_tr;
				heads = flex->nheads;
				cyls = _2btol(flex->ncyl);
				if (secsize == 0)
					secsize = _2btol(flex->bytes_s);
				if (dp->disksize == 0)
					dp->disksize = heads * cyls * sectors;
			}
		}
		break;
	}

validate:
	if (buf)
		dma_free(buf, sizeof(*buf));

	if (dp->disksize == 0)
		return (SDGP_RESULT_OFFLINE);

	if (dp->secsize == 0)
		dp->secsize = (secsize == 0) ? 512 : secsize;

	/*
	 * Restrict secsize values to powers of two between 512 and 64k.
	 */
	switch (dp->secsize) {
	case 0x200:	/* == 512, == DEV_BSIZE on all architectures. */
	case 0x400:
	case 0x800:
	case 0x1000:
	case 0x2000:
	case 0x4000:
	case 0x8000:
	case 0x10000:
		break;
	default:
		SC_DEBUG(sc->sc_link, SDEV_DB1,
		    ("sd_get_parms: bad secsize: %#x\n", dp->secsize));
		return (SDGP_RESULT_OFFLINE);
	}

	/*
	 * XXX THINK ABOUT THIS!!  Using values such that sectors * heads *
	 * cyls is <= disk_size can lead to wasted space. We need a more
	 * careful calculation/validation to make everything work out
	 * optimally.
	 */
	if (dp->disksize > 0xffffffff && (dp->heads * dp->sectors) < 0xffff) {
		dp->heads = 511;
		dp->sectors = 255;
		cyls = 0;
	} else {
		/*
		 * Use standard geometry values for anything we still don't
		 * know.
		 */
		dp->heads = (heads == 0) ? 255 : heads;
		dp->sectors = (sectors == 0) ? 63 : sectors;
	}

	dp->cyls = (cyls == 0) ? dp->disksize / (dp->heads * dp->sectors) :
	    cyls;

	if (dp->cyls == 0) {
		dp->heads = dp->cyls = 1;
		dp->sectors = dp->disksize;
	}

	return (SDGP_RESULT_OK);
}
Esempio n. 2
0
/* preconditions: reiserfs_read_super already executed, therefore
 *   INFO block is valid
 * returns: 0 if error, nonzero iff we were able to find the file successfully
 * postconditions: on a nonzero return, INFO->fileinfo contains the info
 *   of the file we were trying to look up, filepos is 0 and filemax is
 *   the size of the file.
 */
static int
reiserfs_open_file( char *dirname )
{
     struct reiserfs_de_head *de_head;
     char *rest, ch;
     __u32 dir_id, objectid, parent_dir_id = 0, parent_objectid = 0;

     char linkbuf[PATH_MAX];	/* buffer for following symbolic links */
     int link_count = 0;
     int mode;
     errnum = 0;

     dir_id = cpu_to_le32(REISERFS_ROOT_PARENT_OBJECTID);
     objectid = cpu_to_le32(REISERFS_ROOT_OBJECTID);

     while ( 1 )
     {

	  DEBUG_F( "dirname=%s\n", dirname );

	  /* Search for the stat info first. */
	  if ( !search_stat( dir_id, objectid ) )
	       return 0;


	  DEBUG_F( "sd_mode=0%o sd_size=%Lu\n",
		   sd_mode((struct stat_data *) INFO->current_item ),
		   sd_size(INFO->current_ih, INFO->current_item ));


	  mode = sd_mode((struct stat_data *)INFO->current_item);

	  /* If we've got a symbolic link, then chase it. */
	  if ( S_ISLNK( mode ) )
	  {
	       int len = 0;

	       DEBUG_F("link count = %d\n", link_count);
	       DEBUG_SLEEP;
	       if ( ++link_count > MAX_LINK_COUNT )
	       {
		    DEBUG_F("Symlink loop\n");
		    errnum = FILE_ERR_SYMLINK_LOOP;
		    return 0;
	       }

	       /* Get the symlink size. */
	       INFO->file->len = sd_size(INFO->current_ih, INFO->current_item);

	       /* Find out how long our remaining name is. */
	       while ( dirname[len] && !isspace( dirname[len] ) )
		    len++;

	       if ( INFO->file->len + len > sizeof ( linkbuf ) - 1 )
	       {
		    errnum = FILE_ERR_LENGTH;
		    return 0;
	       }

	       /* Copy the remaining name to the end of the symlink data. Note *
		* that DIRNAME and LINKBUF may overlap! */
	       memmove( linkbuf + INFO->file->len, dirname, len + 1 );

	       INFO->fileinfo.k_dir_id = dir_id;
	       INFO->fileinfo.k_objectid = objectid;
	       INFO->file->pos = 0;
	       if ( !next_key()
		    || reiserfs_read_data( linkbuf, INFO->file->len ) != INFO->file->len ) {
		    DEBUG_F("reiserfs_open_file - if !next_key || reiserfs_read_data\n");
		    DEBUG_SLEEP;
		    errnum = FILE_IOERR;
		    return 0;
	       }


	       DEBUG_F( "symlink=%s\n", linkbuf );
	       DEBUG_SLEEP;

	       dirname = linkbuf;
	       if ( *dirname == '/' )
	       {
		    /* It's an absolute link, so look it up in root. */
		    dir_id = cpu_to_le32(REISERFS_ROOT_PARENT_OBJECTID);
		    objectid = cpu_to_le32(REISERFS_ROOT_OBJECTID);
	       }
	       else
	       {
		    /* Relative, so look it up in our parent directory. */
		    dir_id = parent_dir_id;
		    objectid = parent_objectid;
	       }

	       /* Now lookup the new name. */
	       continue;
	  }

	  /* if we have a real file (and we're not just printing *
	   * possibilities), then this is where we want to exit */

	  if ( !*dirname || isspace( *dirname ) )
	  {
	       if ( !S_ISREG( mode ) )
	       {
		    errnum = FILE_ERR_BAD_TYPE;
		    return 0;
	       }

	       INFO->file->pos = 0;
	       INFO->file->len = sd_size(INFO->current_ih, INFO->current_item);

	       INFO->fileinfo.k_dir_id = dir_id;
	       INFO->fileinfo.k_objectid = objectid;
	       return next_key();
	  }

	  /* continue with the file/directory name interpretation */
	  while ( *dirname == '/' )
	       dirname++;
	  if ( !S_ISDIR( mode ) )
	  {
	       errnum = FILE_ERR_NOTDIR;
	       return 0;
	  }
	  for ( rest = dirname; ( ch = *rest ) && !isspace( ch ) && ch != '/';
		rest++ ) ;
	  *rest = 0;

	  while ( 1 )
	  {
	       char *name_end;
	       int num_entries;

	       if ( !next_key() )
		    return 0;

	       if ( INFO->current_ih->ih_key.k_objectid != objectid )
		    break;

	       name_end = INFO->current_item + ih_item_len(INFO->current_ih);
	       de_head = ( struct reiserfs_de_head * ) INFO->current_item;
	       num_entries = ih_entry_count(INFO->current_ih);
	       while ( num_entries > 0 )
	       {
		    char *filename = INFO->current_item + deh_location(de_head);
		    char tmp = *name_end;

		    if( deh_state(de_head) & (1 << DEH_Visible))
		    {
			 int cmp;

			 /* Directory names in ReiserFS are not null * terminated.
			  * We write a temporary 0 behind it. * NOTE: that this
			  * may overwrite the first block in * the tree cache.
			  * That doesn't hurt as long as we * don't call next_key
			  * () in between. */
			 *name_end = 0;
			 cmp = strcmp( dirname, filename );
			 *name_end = tmp;
			 if ( cmp == 0 )
			      goto found;
		    }
		    /* The beginning of this name marks the end of the next name.
		     */
		    name_end = filename;
		    de_head++;
		    num_entries--;
	       }
	  }

	  errnum = FILE_ERR_NOTFOUND;
	  *rest = ch;
	  return 0;

     found:
	  *rest = ch;
	  dirname = rest;

	  parent_dir_id = dir_id;
	  parent_objectid = objectid;
	  dir_id = de_head->deh_dir_id; /* LE */
	  objectid = de_head->deh_objectid; /* LE */
     }
}