WandPrivate MagickBooleanType CLIListOperatorImages(MagickCLI *cli_wand,
  const char *option,const char *arg1n,const char *arg2n)
{
  const char    /* percent escaped versions of the args */
    *arg1,
    *arg2;

  Image
    *new_images;

  MagickStatusType
    status;

  ssize_t
    parse;

#define _image_info     (cli_wand->wand.image_info)
#define _images         (cli_wand->wand.images)
#define _exception      (cli_wand->wand.exception)
#define _draw_info      (cli_wand->draw_info)
#define _quantize_info  (cli_wand->quantize_info)
#define _process_flags  (cli_wand->process_flags)
#define _option_type    ((CommandOptionFlags) cli_wand->command->flags)
#define IfNormalOp      (*option=='-')
#define IfPlusOp        (*option!='-')
#define IsNormalOp      IfNormalOp ? MagickTrue : MagickFalse

  assert(cli_wand != (MagickCLI *) NULL);
  assert(cli_wand->signature == MagickWandSignature);
  assert(cli_wand->wand.signature == MagickWandSignature);
  assert(_images != (Image *) NULL);             /* _images must be present */

  if (cli_wand->wand.debug != MagickFalse)
    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
       "- List Operator: %s \"%s\" \"%s\"", option,
       arg1n == (const char *) NULL ? "null" : arg1n,
       arg2n == (const char *) NULL ? "null" : arg2n);

  arg1 = arg1n;
  arg2 = arg2n;

  /* Interpret Percent Escapes in Arguments - using first image */
  if ( (((_process_flags & ProcessInterpretProperities) != 0 )
        || ((_option_type & AlwaysInterpretArgsFlag) != 0)
       )  && ((_option_type & NeverInterpretArgsFlag) == 0) ) {
    /* Interpret Percent escapes in argument 1 */
    if (arg1n != (char *) NULL) {
      arg1=InterpretImageProperties(_image_info,_images,arg1n,_exception);
      if (arg1 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg1=arg1n;  /* use the given argument as is */
      }
    }
    if (arg2n != (char *) NULL) {
      arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
      if (arg2 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg2=arg2n;  /* use the given argument as is */
      }
    }
  }
#undef _process_flags
#undef _option_type

  status=MagickTrue;
  new_images=NewImageList();

  switch (*(option+1))
  {
    case 'a':
    {
      if (LocaleCompare("append",option+1) == 0)
        {
          new_images=AppendImages(_images,IsNormalOp,_exception);
          break;
        }
      if (LocaleCompare("average",option+1) == 0)
        {
          CLIWandWarnReplaced("-evaluate-sequence Mean");
          (void) CLIListOperatorImages(cli_wand,"-evaluate-sequence","Mean",
            NULL);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'c':
    {
      if (LocaleCompare("channel-fx",option+1) == 0)
        {
          new_images=ChannelFxImage(_images,arg1,_exception);
          break;
        }
      if (LocaleCompare("clut",option+1) == 0)
        {
          Image
            *clut_image;

          /* FUTURE - make this a compose option, and thus can be used
             with layers compose or even compose last image over all other
             _images.
          */
          new_images=RemoveFirstImageFromList(&_images);
          clut_image=RemoveFirstImageFromList(&_images);
          /* FUTURE - produce Exception, rather than silent fail */
          if (clut_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              new_images=DestroyImage(new_images);
              status=MagickFalse;
              break;
            }
          (void) ClutImage(new_images,clut_image,new_images->interpolate,
            _exception);
          clut_image=DestroyImage(clut_image);
          break;
        }
      if (LocaleCompare("coalesce",option+1) == 0)
        {
          new_images=CoalesceImages(_images,_exception);
          break;
        }
      if (LocaleCompare("combine",option+1) == 0)
        {
          parse=(ssize_t) _images->colorspace;
          if (_images->number_channels < GetImageListLength(_images))
            parse=sRGBColorspace;
          if ( IfPlusOp )
            parse=ParseCommandOption(MagickColorspaceOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedColorspace",option,
              arg1);
          new_images=CombineImages(_images,(ColorspaceType) parse,_exception);
          break;
        }
      if (LocaleCompare("compare",option+1) == 0)
        {
          double
            distortion;

          Image
            *image,
            *reconstruct_image;

          MetricType
            metric;

          /*
            Mathematically and visually annotate the difference between an
            image and its reconstruction.
          */
          image=RemoveFirstImageFromList(&_images);
          reconstruct_image=RemoveFirstImageFromList(&_images);
          /* FUTURE - produce Exception, rather than silent fail */
          if (reconstruct_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              image=DestroyImage(image);
              status=MagickFalse;
              break;
            }
          metric=UndefinedErrorMetric;
          option=GetImageOption(_image_info,"metric");
          if (option != (const char *) NULL)
            metric=(MetricType) ParseCommandOption(MagickMetricOptions,
              MagickFalse,option);
          new_images=CompareImages(image,reconstruct_image,metric,&distortion,
            _exception);
          (void) distortion;
          reconstruct_image=DestroyImage(reconstruct_image);
          image=DestroyImage(image);
          break;
        }
      if (LocaleCompare("complex",option+1) == 0)
        {
          parse=ParseCommandOption(MagickComplexOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedEvaluateOperator",
              option,arg1);
          new_images=ComplexImages(_images,(ComplexOperator) parse,_exception);
          break;
        }
      if (LocaleCompare("composite",option+1) == 0)
        {
          CompositeOperator
            compose;

          const char*
            value;

          MagickBooleanType
            clip_to_self;

          Image
            *mask_image,
            *source_image;

          RectangleInfo
            geometry;

          /* Compose value from "-compose" option only */
          value=GetImageOption(_image_info,"compose");
          if (value == (const char *) NULL)
            compose=OverCompositeOp;  /* use Over not source_image->compose */
          else
            compose=(CompositeOperator) ParseCommandOption(MagickComposeOptions,
              MagickFalse,value);

          /* Get "clip-to-self" expert setting (false is normal) */
          clip_to_self=GetCompositeClipToSelf(compose);
          value=GetImageOption(_image_info,"compose:clip-to-self");
          if (value != (const char *) NULL)
            clip_to_self=IsStringTrue(value);
          value=GetImageOption(_image_info,"compose:outside-overlay");
          if (value != (const char *) NULL)
            clip_to_self=IsStringFalse(value);  /* deprecated */

          new_images=RemoveFirstImageFromList(&_images);
          source_image=RemoveFirstImageFromList(&_images);
          if (source_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              new_images=DestroyImage(new_images);
              status=MagickFalse;
              break;
            }

          /* FUTURE - this should not be here! - should be part of -geometry */
          if (source_image->geometry != (char *) NULL)
            {
              RectangleInfo
                resize_geometry;

              (void) ParseRegionGeometry(source_image,source_image->geometry,
                &resize_geometry,_exception);
              if ((source_image->columns != resize_geometry.width) ||
                  (source_image->rows != resize_geometry.height))
                {
                  Image
                    *resize_image;

                  resize_image=ResizeImage(source_image,resize_geometry.width,
                    resize_geometry.height,source_image->filter,_exception);
                  if (resize_image != (Image *) NULL)
                    {
                      source_image=DestroyImage(source_image);
                      source_image=resize_image;
                    }
                }
            }
          SetGeometry(source_image,&geometry);
          (void) ParseAbsoluteGeometry(source_image->geometry,&geometry);
          GravityAdjustGeometry(new_images->columns,new_images->rows,
            new_images->gravity, &geometry);
          mask_image=RemoveFirstImageFromList(&_images);
          if (mask_image == (Image *) NULL)
            status&=CompositeImage(new_images,source_image,compose,clip_to_self,
              geometry.x,geometry.y,_exception);
          else
            {
              Image
                *canvas_images;

              canvas_images=CloneImage(new_images,0,0,MagickTrue,_exception);
              if (canvas_images == (Image *) NULL)
                break;
              switch (compose)
              {
                case BlendCompositeOp:
                {
                  status&=CompositeImage(new_images,source_image,compose,
                    clip_to_self,geometry.x,geometry.y,_exception);
                  status&=CompositeImage(new_images,mask_image,
                    CopyAlphaCompositeOp,MagickTrue,0,0,_exception);
                  break;
                }
                case DisplaceCompositeOp:
                case DistortCompositeOp:
                {
                  status&=CompositeImage(source_image,mask_image,
                    CopyGreenCompositeOp,MagickTrue,0,0,_exception);
                  status&=CompositeImage(new_images,source_image,compose,
                    clip_to_self,geometry.x,geometry.y,_exception);
                  break;
                }
                case SaliencyBlendCompositeOp:
                case SeamlessBlendCompositeOp:
                {
                  status&=CompositeImage(source_image,mask_image,
                    CopyAlphaCompositeOp,MagickTrue,0,0,_exception);
                  status&=CompositeImage(new_images,source_image,compose,
                    clip_to_self,geometry.x,geometry.y,_exception);
                  break;
                }
                default:
                {
                  Image
                    *clone_image;

                  clone_image=CloneImage(new_images,0,0,MagickTrue,_exception);
                  if (clone_image == (Image *) NULL)
                    break;
                  status&=CompositeImage(new_images,source_image,compose,
                    clip_to_self,geometry.x,geometry.y,_exception);
                  status&=CompositeImage(new_images,mask_image,
                    CopyAlphaCompositeOp,MagickTrue,0,0,_exception);
                  status&=CompositeImage(clone_image,new_images,OverCompositeOp,
                    clip_to_self,0,0,_exception);
                  new_images=DestroyImageList(new_images);
                  new_images=clone_image;
                  break;
                }
              }
              status&=CompositeImage(canvas_images,new_images,OverCompositeOp,
                clip_to_self,0,0,_exception);
              new_images=DestroyImageList(new_images);
              new_images=canvas_images;
              mask_image=DestroyImage(mask_image);
            }
          source_image=DestroyImage(source_image);
          break;
        }
        if (LocaleCompare("copy",option+1) == 0)
          {
            Image
              *source_image;

            OffsetInfo
              offset;

            RectangleInfo
              geometry;

            /*
              Copy image pixels.
            */
            if (IsGeometry(arg1) == MagickFalse)
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
            if (IsGeometry(arg2) == MagickFalse)
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
            (void) ParsePageGeometry(_images,arg2,&geometry,_exception);
            offset.x=geometry.x;
            offset.y=geometry.y;
            source_image=_images;
            if (source_image->next != (Image *) NULL)
              source_image=source_image->next;
            (void) ParsePageGeometry(source_image,arg1,&geometry,_exception);
            (void) CopyImagePixels(_images,source_image,&geometry,&offset,
              _exception);
            break;
          }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'd':
    {
      if (LocaleCompare("deconstruct",option+1) == 0)
        {
          CLIWandWarnReplaced("-layers CompareAny");
          (void) CLIListOperatorImages(cli_wand,"-layers","CompareAny",NULL);
          break;
        }
      if (LocaleCompare("delete",option+1) == 0)
        {
          if (IfNormalOp)
            DeleteImages(&_images,arg1,_exception);
          else
            DeleteImages(&_images,"-1",_exception);
          break;
        }
      if (LocaleCompare("duplicate",option+1) == 0)
        {
          if (!IfNormalOp)
            new_images=DuplicateImages(_images,1,"-1",_exception);
          else
            {
              const char
                *p;

              size_t
                number_duplicates;

              if (IsGeometry(arg1) == MagickFalse)
                CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,
                  arg1);
              number_duplicates=(size_t) StringToLong(arg1);
              p=strchr(arg1,',');
              if (p == (const char *) NULL)
                new_images=DuplicateImages(_images,number_duplicates,"-1",
                  _exception);
              else
                new_images=DuplicateImages(_images,number_duplicates,p+1,
                  _exception);
            }
          AppendImageToList(&_images, new_images);
          new_images=(Image *) NULL;
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'e':
    {
      if (LocaleCompare("evaluate-sequence",option+1) == 0)
        {
          parse=ParseCommandOption(MagickEvaluateOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedEvaluateOperator",
              option,arg1);
          new_images=EvaluateImages(_images,(MagickEvaluateOperator) parse,
            _exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'f':
    {
      if (LocaleCompare("fft",option+1) == 0)
        {
          new_images=ForwardFourierTransformImage(_images,IsNormalOp,
           _exception);
          break;
        }
      if (LocaleCompare("flatten",option+1) == 0)
        {
          /* REDIRECTED to use -layers flatten instead */
          (void) CLIListOperatorImages(cli_wand,"-layers",option+1,NULL);
          break;
        }
      if (LocaleCompare("fx",option+1) == 0)
        {
          new_images=FxImage(_images,arg1,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'h':
    {
      if (LocaleCompare("hald-clut",option+1) == 0)
        {
          /* FUTURE - make this a compose option (and thus layers compose )
             or perhaps compose last image over all other _images.
          */
          Image
            *hald_image;

          new_images=RemoveFirstImageFromList(&_images);
          hald_image=RemoveLastImageFromList(&_images);
          if (hald_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              new_images=DestroyImage(new_images);
              status=MagickFalse;
              break;
            }
          (void) HaldClutImage(new_images,hald_image,_exception);
          hald_image=DestroyImage(hald_image);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'i':
    {
      if (LocaleCompare("ift",option+1) == 0)
        {
          Image
            *magnitude_image,
            *phase_image;

          magnitude_image=RemoveFirstImageFromList(&_images);
          phase_image=RemoveFirstImageFromList(&_images);
          if (phase_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              magnitude_image=DestroyImage(magnitude_image);
              status=MagickFalse;
              break;
            }
          new_images=InverseFourierTransformImage(magnitude_image,phase_image,
            IsNormalOp,_exception);
          magnitude_image=DestroyImage(magnitude_image);
          phase_image=DestroyImage(phase_image);
          break;
        }
      if (LocaleCompare("insert",option+1) == 0)
        {
          Image
            *insert_image,
            *index_image;

          ssize_t
            index;

          if (IfNormalOp && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          index=0;
          insert_image=RemoveLastImageFromList(&_images);
          if (IfNormalOp)
            index=(ssize_t) StringToLong(arg1);
          index_image=insert_image;
          if (index == 0)
            PrependImageToList(&_images,insert_image);
          else if (index == (ssize_t) GetImageListLength(_images))
            AppendImageToList(&_images,insert_image);
          else
            {
               index_image=GetImageFromList(_images,index-1);
               if (index_image == (Image *) NULL)
                 {
                   insert_image=DestroyImage(insert_image);
                   CLIWandExceptArgBreak(OptionError,"NoSuchImage",option,arg1);
                 }
              InsertImageInList(&index_image,insert_image);
            }
          _images=GetFirstImageInList(index_image);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'l':
    {
      if (LocaleCompare("layers",option+1) == 0)
        {
          parse=ParseCommandOption(MagickLayerOptions,MagickFalse,arg1);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedLayerMethod",
                 option,arg1);
          switch ((LayerMethod) parse)
          {
            case CoalesceLayer:
            {
              new_images=CoalesceImages(_images,_exception);
              break;
            }
            case CompareAnyLayer:
            case CompareClearLayer:
            case CompareOverlayLayer:
            default:
            {
              new_images=CompareImagesLayers(_images,(LayerMethod) parse,
                   _exception);
              break;
            }
            case MergeLayer:
            case FlattenLayer:
            case MosaicLayer:
            case TrimBoundsLayer:
            {
              new_images=MergeImageLayers(_images,(LayerMethod) parse,
                _exception);
              break;
            }
            case DisposeLayer:
            {
              new_images=DisposeImages(_images,_exception);
              break;
            }
            case OptimizeImageLayer:
            {
              new_images=OptimizeImageLayers(_images,_exception);
              break;
            }
            case OptimizePlusLayer:
            {
              new_images=OptimizePlusImageLayers(_images,_exception);
              break;
            }
            case OptimizeTransLayer:
            {
              OptimizeImageTransparency(_images,_exception);
              break;
            }
            case RemoveDupsLayer:
            {
              RemoveDuplicateLayers(&_images,_exception);
              break;
            }
            case RemoveZeroLayer:
            {
              RemoveZeroDelayLayers(&_images,_exception);
              break;
            }
            case OptimizeLayer:
            { /* General Purpose, GIF Animation Optimizer.  */
              new_images=CoalesceImages(_images,_exception);
              if (new_images == (Image *) NULL)
                break;
              _images=DestroyImageList(_images);
              _images=OptimizeImageLayers(new_images,_exception);
              if (_images == (Image *) NULL)
                break;
              new_images=DestroyImageList(new_images);
              OptimizeImageTransparency(_images,_exception);
              (void) RemapImages(_quantize_info,_images,(Image *) NULL,
                _exception);
              break;
            }
            case CompositeLayer:
            {
              Image
                *source;

              RectangleInfo
                geometry;

              CompositeOperator
                compose;

              const char*
                value;

              value=GetImageOption(_image_info,"compose");
              compose=OverCompositeOp;  /* Default to Over */
              if (value != (const char *) NULL)
                compose=(CompositeOperator) ParseCommandOption(
                      MagickComposeOptions,MagickFalse,value);

              /* Split image sequence at the first 'NULL:' image. */
              source=_images;
              while (source != (Image *) NULL)
              {
                source=GetNextImageInList(source);
                if ((source != (Image *) NULL) &&
                    (LocaleCompare(source->magick,"NULL") == 0))
                  break;
              }
              if (source != (Image *) NULL)
                {
                  if ((GetPreviousImageInList(source) == (Image *) NULL) ||
                      (GetNextImageInList(source) == (Image *) NULL))
                    source=(Image *) NULL;
                  else
                    { /* Separate the two lists, junk the null: image.  */
                      source=SplitImageList(source->previous);
                      DeleteImageFromList(&source);
                    }
                }
              if (source == (Image *) NULL)
                {
                  (void) ThrowMagickException(_exception,GetMagickModule(),
                    OptionError,"MissingNullSeparator","layers Composite");
                  break;
                }
              /* Adjust offset with gravity and virtual canvas.  */
              SetGeometry(_images,&geometry);
              (void) ParseAbsoluteGeometry(_images->geometry,&geometry);
              geometry.width=source->page.width != 0 ?
                source->page.width : source->columns;
              geometry.height=source->page.height != 0 ?
               source->page.height : source->rows;
              GravityAdjustGeometry(_images->page.width != 0 ?
                _images->page.width : _images->columns,
                _images->page.height != 0 ? _images->page.height :
                _images->rows,_images->gravity,&geometry);

              /* Compose the two image sequences together */
              CompositeLayers(_images,compose,source,geometry.x,geometry.y,
                _exception);
              source=DestroyImageList(source);
              break;
            }
          }
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'm':
    {
      if (LocaleCompare("map",option+1) == 0)
        {
          CLIWandWarnReplaced("+remap");
          (void) RemapImages(_quantize_info,_images,(Image *) NULL,_exception);
          break;
        }
      if (LocaleCompare("metric",option+1) == 0)
        {
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("morph",option+1) == 0)
        {
          Image
            *morph_image;

          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          morph_image=MorphImages(_images,StringToUnsignedLong(arg1),
            _exception);
          if (morph_image == (Image *) NULL)
            break;
          _images=DestroyImageList(_images);
          _images=morph_image;
          break;
        }
      if (LocaleCompare("mosaic",option+1) == 0)
        {
          /* REDIRECTED to use -layers mosaic instead */
          (void) CLIListOperatorImages(cli_wand,"-layers",option+1,NULL);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'p':
    {
      if (LocaleCompare("poly",option+1) == 0)
        {
          double
            *args;

          ssize_t
            count;

          /* convert argument string into an array of doubles */
          args = StringToArrayOfDoubles(arg1,&count,_exception);
          if (args == (double *) NULL )
            CLIWandExceptArgBreak(OptionError,"InvalidNumberList",option,arg1);
          new_images=PolynomialImage(_images,(size_t) (count >> 1),args,
           _exception);
          args=(double *) RelinquishMagickMemory(args);
          break;
        }
      if (LocaleCompare("process",option+1) == 0)
        {
          /* FUTURE: better parsing using ScriptToken() from string ??? */
          char
            **arguments;

          int
            j,
            number_arguments;

          arguments=StringToArgv(arg1,&number_arguments);
          if (arguments == (char **) NULL)
            break;
          if (strchr(arguments[1],'=') != (char *) NULL)
            {
              char
                breaker,
                quote,
                *token;

              const char
                *p;

              int
                next,
                tokenizer_status;

              size_t
                length;

              TokenInfo
                *token_info;

              /*
                Support old style syntax, filter="-option arg1".
              */
              assert(arg1 != (const char *) NULL);
              length=strlen(arg1);
              token=(char *) NULL;
              if (~length >= (MagickPathExtent-1))
                token=(char *) AcquireQuantumMemory(length+MagickPathExtent,
                  sizeof(*token));
              if (token == (char *) NULL)
                break;
              next=0;
              p=arg1;
              token_info=AcquireTokenInfo();
              tokenizer_status=Tokenizer(token_info,0,token,length,p,"","=",
                "\"",'\0',&breaker,&next,&quote);
              token_info=DestroyTokenInfo(token_info);
              if (tokenizer_status == 0)
                {
                  const char
                    *argv;

                  argv=(&(p[next]));
                  (void) InvokeDynamicImageFilter(token,&_images,1,&argv,
                    _exception);
                }
              token=DestroyString(token);
              break;
            }
          (void) SubstituteString(&arguments[1],"-","");
          (void) InvokeDynamicImageFilter(arguments[1],&_images,
            number_arguments-2,(const char **) arguments+2,_exception);
          for (j=0; j < number_arguments; j++)
            arguments[j]=DestroyString(arguments[j]);
          arguments=(char **) RelinquishMagickMemory(arguments);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'r':
    {
      if (LocaleCompare("remap",option+1) == 0)
        {
          (void) RemapImages(_quantize_info,_images,(Image *) NULL,_exception);
          break;
        }
      if (LocaleCompare("reverse",option+1) == 0)
        {
          ReverseImageList(&_images);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 's':
    {
      if (LocaleCompare("smush",option+1) == 0)
        {
          /* FUTURE: this option needs more work to make better */
          ssize_t
            offset;

          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          offset=(ssize_t) StringToLong(arg1);
          new_images=SmushImages(_images,IsNormalOp,offset,_exception);
          break;
        }
      if (LocaleCompare("subimage",option+1) == 0)
        {
          Image
            *base_image,
            *compare_image;

          const char
            *value;

          MetricType
            metric;

          double
            similarity;

          RectangleInfo
            offset;

          base_image=GetImageFromList(_images,0);
          compare_image=GetImageFromList(_images,1);

          /* Comparision Metric */
          metric=UndefinedErrorMetric;
          value=GetImageOption(_image_info,"metric");
          if (value != (const char *) NULL)
            metric=(MetricType) ParseCommandOption(MagickMetricOptions,
              MagickFalse,value);

          new_images=SimilarityImage(base_image,compare_image,metric,0.0,
            &offset,&similarity,_exception);

          if (new_images != (Image *) NULL)
            {
              (void) FormatImageProperty(new_images,"subimage:similarity",
                "%.*g",GetMagickPrecision(),similarity);
              (void) FormatImageProperty(new_images,"subimage:x","%+ld",(long)
                offset.x);
              (void) FormatImageProperty(new_images,"subimage:y","%+ld",(long)
                offset.y);
              (void) FormatImageProperty(new_images,"subimage:offset",
                "%lux%lu%+ld%+ld",(unsigned long) offset.width,(unsigned long)
                offset.height,(long) offset.x,(long) offset.y);
            }
          break;
        }
      if (LocaleCompare("swap",option+1) == 0)
        {
        Image
          *p,
          *q,
          *swap;

        ssize_t
          index,
          swap_index;

        index=(-1);
        swap_index=(-2);
        if (IfNormalOp) {
          GeometryInfo
            geometry_info;

          MagickStatusType
            flags;

          swap_index=(-1);
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          index=(ssize_t) geometry_info.rho;
          if ((flags & SigmaValue) != 0)
            swap_index=(ssize_t) geometry_info.sigma;
        }
        p=GetImageFromList(_images,index);
        q=GetImageFromList(_images,swap_index);
        if ((p == (Image *) NULL) || (q == (Image *) NULL)) {
          if (IfNormalOp)
            CLIWandExceptArgBreak(OptionError,"InvalidImageIndex",option,arg1)
          else
            CLIWandExceptionBreak(OptionError,"TwoOrMoreImagesRequired",option);
        }
        if (p == q)
          CLIWandExceptArgBreak(OptionError,"InvalidImageIndex",option,arg1);
        swap=CloneImage(p,0,0,MagickTrue,_exception);
        if (swap == (Image *) NULL)
          CLIWandExceptArgBreak(ResourceLimitError,"MemoryAllocationFailed",
            option,GetExceptionMessage(errno));
        ReplaceImageInList(&p,CloneImage(q,0,0,MagickTrue,_exception));
        ReplaceImageInList(&q,swap);
        _images=GetFirstImageInList(q);
        break;
      }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    default:
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
  }

  /* clean up percent escape interpreted strings */
  if (arg1 != arg1n )
    arg1=DestroyString((char *)arg1);
  if (arg2 != arg2n )
    arg2=DestroyString((char *)arg2);

  /* if new image list generated, replace existing image list */
  if (new_images == (Image *) NULL)
    return(status == 0 ? MagickFalse : MagickTrue);
  _images=DestroyImageList(_images);
  _images=GetFirstImageInList(new_images);
  return(status == 0 ? MagickFalse : MagickTrue);

#undef _image_info
#undef _images
#undef _exception
#undef _draw_info
#undef _quantize_info
#undef IfNormalOp
#undef IfPlusOp
#undef IsNormalOp
}


static MagickBooleanType MonitorProgress(const char *text,
  const MagickOffsetType offset,const MagickSizeType extent,
  void *wand_unused(client_data))
{
  char
    message[MagickPathExtent],
    tag[MagickPathExtent];

  const char
    *locale_message;

  char
    *p;

  magick_unreferenced(client_data);

  if ((extent <= 1) || (offset < 0) || (offset >= (MagickOffsetType) extent))
    return(MagickTrue);
  if ((offset != (MagickOffsetType) (extent-1)) && ((offset % 50) != 0))
    return(MagickTrue);
  (void) CopyMagickString(tag,text,MagickPathExtent);
  p=strrchr(tag,'/');
  if (p != (char *) NULL)
    *p='\0';
  (void) FormatLocaleString(message,MagickPathExtent,"Monitor/%s",tag);
  locale_message=GetLocaleMessage(message);
  if (locale_message == message)
    locale_message=tag;
  if (p == (char *) NULL)
    (void) FormatLocaleFile(stderr,"%s: %ld of %lu, %02ld%% complete\r",
      locale_message,(long) offset,(unsigned long) extent,(long)
      (100L*offset/(extent-1)));
  else
    (void) FormatLocaleFile(stderr,"%s[%s]: %ld of %lu, %02ld%% complete\r",
      locale_message,p+1,(long) offset,(unsigned long) extent,(long)
      (100L*offset/(extent-1)));
  if (offset == (MagickOffsetType) (extent-1))
    (void) FormatLocaleFile(stderr,"\n");
  (void) fflush(stderr);
  return(MagickTrue);
}

do not attempt to write a null image list (thanks to Vinay Rohila)


static inline Quantum GetChannelValue(unsigned int word,unsigned char channel,
  TIM2ColorEncoding ce)
{
  switch(ce)
  {
    case RGBA16:
      /* Documentation specifies padding with zeros for converting from 5 to 8 bits. */
      return ScaleCharToQuantum((word>>channel*5 & ~(~0x0U<<5))<<3);
    case RGB24:
    case RGBA32:
      return ScaleCharToQuantum(word>>channel*8 & ~(~0x0U<<8));
    default:
      return QuantumRange;
  }
}


static inline Quantum GetAlpha(unsigned int word,TIM2ColorEncoding ce)
{
  switch(ce)
  {
    case RGBA16:
      return ScaleCharToQuantum((word>>3*5&0x1F)==0?0:0xFF);
    case RGBA32:
      /* 0x80 -> 1.0 alpha. Multiply by 2 and clamp to 0xFF */
      return ScaleCharToQuantum(MagickMin((word>>3*8&0xFF)<<1,0xFF));
    default:
      return 0xFF;
  }
}


static MagickBooleanType ReadTIM2ImageData(const ImageInfo *image_info,
  Image *image,TIM2ImageHeader *header,char clut_depth,char bits_per_pixel,
  ExceptionInfo *exception)
{
  MagickBooleanType
    status;

  ssize_t
    x;

  Quantum
    *q;

  unsigned char
    *p;

  size_t
    bits_per_line,
    bytes_per_line;

  ssize_t
    count,
    y;

  unsigned char
    *row_data;

  unsigned int
    word;

  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
    return(MagickFalse);
  /*
   * User data
   */
  status=DiscardBlobBytes(image,header->header_size-48);
  if (status == MagickFalse)
    return(MagickFalse);
  /*
   * Image data
   */
  bits_per_line=image->columns*bits_per_pixel;
  bytes_per_line=bits_per_line/8 + ((bits_per_line%8==0) ? 0 : 1);
  row_data=(unsigned char*) AcquireQuantumMemory(1,bytes_per_line);
  if (row_data == (unsigned char *) NULL)
    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
      image_info->filename);
  if (clut_depth != 0)
    {
      image->colors=header->clut_color_count;
      if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
        {
          row_data=(unsigned char *) RelinquishMagickMemory(row_data);
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
            image_info->filename);
        }
      switch (bits_per_pixel)
      {
        case 4:
        {
          for (y=0; y<(ssize_t) image->rows; y++)
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (Quantum *) NULL)
              break;
            count=ReadBlob(image,bytes_per_line,row_data);
            if (count != (ssize_t) bytes_per_line)
              {
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
                ThrowBinaryException(CorruptImageError,
                  "InsufficientImageDataInFile",image_info->filename);
              }
            p=row_data;
            for (x=0; x < ((ssize_t) image->columns-1); x+=2)
            {
              SetPixelIndex(image,(*p >> 0) & 0x0F,q);
              q+=GetPixelChannels(image);
              SetPixelIndex(image,(*p >> 4) & 0x0F,q);
              p++;
              q+=GetPixelChannels(image);
            }
            if ((image->columns % 2) != 0)
              {
                SetPixelIndex(image,(*p >> 4) & 0x0F,q);
                p++;
                q+=GetPixelChannels(image);
              }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            if (image->previous == (Image *) NULL)
              {
                status=SetImageProgress(image,LoadImageTag,
                  (MagickOffsetType) y,image->rows);
                if (status == MagickFalse)
                  break;
              }
          }
          break;
        }
        case 8:
        {
          for (y=0;y<(ssize_t) image->rows; y++)
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (Quantum *) NULL)
              break;
            count=ReadBlob(image,bytes_per_line,row_data);
            if (count != (ssize_t) bytes_per_line)
              {
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
                ThrowBinaryException(CorruptImageError,
                  "InsufficientImageDataInFile",image_info->filename);
              }
            p=row_data;
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              SetPixelIndex(image,*p,q);
              p++;
              q+=GetPixelChannels(image);
            }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            if (image->previous == (Image *) NULL)
              {
                status=SetImageProgress(image,LoadImageTag,
                  (MagickOffsetType) y,image->rows);
                if (status == MagickFalse)
                  break;
              }
          }
          break;
        }
        default:
        {
          row_data=(unsigned char *) RelinquishMagickMemory(row_data);
          ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
            image_info->filename);
        }
      }
    }
  else  /* has_clut==false */
    {
      switch (bits_per_pixel)
      {
        case 16:
        {
          for (y=0; y<(ssize_t) image->rows; y++)
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (Quantum *) NULL)
              break;
            count=ReadBlob(image,bytes_per_line,row_data);
            if (count != (ssize_t) bytes_per_line)
              {
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
                ThrowBinaryException(CorruptImageError,
                  "InsufficientImageDataInFile",image_info->filename);
              }
            p=row_data;
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              word = ((unsigned int)* p   )<<0*8 |
                      ((unsigned int)*(p+1))<<1*8;

              SetPixelRed(image,GetChannelValue(word,0,RGBA16),q);
              SetPixelGreen(image,GetChannelValue(word,1,RGBA16),q);
              SetPixelBlue(image,GetChannelValue(word,2,RGBA16),q);
              SetPixelAlpha(image,GetAlpha(word,RGBA16),q);
              q+=GetPixelChannels(image);
              p+=sizeof(unsigned short);
            }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            if (image->previous == (Image *) NULL)
              {
                status=SetImageProgress(image,LoadImageTag,
                  (MagickOffsetType) y,image->rows);
                if (status == MagickFalse)
                  break;
              }
          }
          break;
        }
        case 24:
        {
          for (y = 0; y<(ssize_t) image->rows; y++)
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (Quantum *) NULL)
              break;
            count=ReadBlob(image,bytes_per_line,row_data);
            if (count != (ssize_t) bytes_per_line)
              {
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
                ThrowBinaryException(CorruptImageError,
                  "InsufficientImageDataInFile",image_info->filename);
              }
            p=row_data;
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              word = (unsigned int)(* p   )<<0*8 |
                      (unsigned int)(*(p+1))<<1*8 |
                      (unsigned int)(*(p+2))<<2*8;

              SetPixelRed(image,GetChannelValue(word,0,RGB24),q);
              SetPixelGreen(image,GetChannelValue(word,1,RGB24),q);
              SetPixelBlue(image,GetChannelValue(word,2,RGB24),q);
              q+=GetPixelChannels(image);
              p+=3;
            }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            if (image->previous == (Image *) NULL)
              {
                status=SetImageProgress(image,LoadImageTag,
                  (MagickOffsetType) y,image->rows);
                if (status == MagickFalse)
                  break;
              }
          }
          break;
        }
        case 32:
        {
          for (y = 0; y<(ssize_t) image->rows; y++)
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (Quantum *) NULL)
              break;
            count=ReadBlob(image,bytes_per_line,row_data);
            if (count != (ssize_t) bytes_per_line)
              {
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
                ThrowBinaryException(CorruptImageError,
                  "InsufficientImageDataInFile",image_info->filename);
              }
            p=row_data;
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              word = ((unsigned int)* p   )<<0*8 |
                      ((unsigned int)*(p+1))<<1*8 |
                      ((unsigned int)*(p+2))<<2*8 |
                      ((unsigned int)*(p+3))<<3*8;

              SetPixelRed(image,GetChannelValue(word,0,RGBA32),q);
              SetPixelGreen(image,GetChannelValue(word,1,RGBA32),q);
              SetPixelBlue(image,GetChannelValue(word,2,RGBA32),q);
              SetPixelAlpha(image,GetAlpha(word,RGBA32),q);
              q+=GetPixelChannels(image);
              p+=4;
            }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            if (image->previous == (Image *) NULL)
              {
                status=SetImageProgress(image,LoadImageTag,
                  (MagickOffsetType) y,image->rows);
                if (status == MagickFalse)
                  break;
              }
          }
          break;
        }
        default:
        {
          row_data=(unsigned char *) RelinquishMagickMemory(row_data);
          ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
            image_info->filename);
        }
      }
    }
  row_data=(unsigned char *) RelinquishMagickMemory(row_data);
  if ((status != MagickFalse) && (clut_depth != 0))
  {
    CSM
      csm;

    ssize_t
      i;

    unsigned char
      *clut_data;

    /*
      * ### Read CLUT Data ###
      */
    clut_data=(unsigned char *) AcquireQuantumMemory(1,header->clut_size);
    if (clut_data == (unsigned char *) NULL)
      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
        image_info->filename);
    count=ReadBlob(image,header->clut_size,clut_data);
    if (count != (ssize_t) (header->clut_size))
      {
        clut_data=(unsigned char *) RelinquishMagickMemory(clut_data);
        ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile",
          image_info->filename);
      }
    /*
      * ### Process CLUT Data ###
      */
    p=clut_data;
    switch(clut_depth)
    {
      case 16:
      {
        for (i=0; i < (ssize_t) image->colors; i++)
        {
          word = ((unsigned short)* p   )<<0*8 |
                  ((unsigned short)*(p+1))<<1*8;

          image->colormap[i].red=GetChannelValue(word,0,RGBA16);
          image->colormap[i].green=GetChannelValue(word,1,RGBA16);
          image->colormap[i].blue=GetChannelValue(word,2,RGBA16);
          image->colormap[i].alpha=GetAlpha(word,RGBA16);
          p+=2;
        }
        break;
      }
      case 24:
      {
        for (i=0; i < (ssize_t) image->colors; i++)
        {
          word = ((unsigned int)* p   )<<0*8 |
                  ((unsigned int)*(p+1))<<1*8 |
                  ((unsigned int)*(p+2))<<2*8;

          image->colormap[i].red=GetChannelValue(word,0,RGB24);
          image->colormap[i].green=GetChannelValue(word,1,RGB24);
          image->colormap[i].blue=GetChannelValue(word,2,RGB24);
          p+=3;
        }
        break;
      }
      case 32:
      {
        for (i=0; i < (ssize_t) image->colors; i++)
        {
          word = ((unsigned int)* p   )<<0*8 |
                  ((unsigned int)*(p+1))<<1*8 |
                  ((unsigned int)*(p+2))<<2*8 |
                  ((unsigned int)*(p+3))<<3*8;

          image->colormap[i].red=GetChannelValue(word,0,RGBA32);
          image->colormap[i].green=GetChannelValue(word,1,RGBA32);
          image->colormap[i].blue=GetChannelValue(word,2,RGBA32);
          image->colormap[i].alpha=GetAlpha(word,RGBA32);
          p+=4;
        }
        break;
      }
    }
    clut_data=(unsigned char *) RelinquishMagickMemory(clut_data);
    /* CSM: CLUT Storage Mode */
    switch ((int) header->clut_type>>4)  /* High 4 bits */
    {
      case 0:
        csm=CSM1;
        break;
      case 1:
        csm=CSM2;
        break;
      default:
        ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
          image_info->filename);
        break;
    }
    if (csm == CSM1)
      {
        PixelInfo
          *oldColormap;

        oldColormap=(PixelInfo *) AcquireQuantumMemory((size_t)(image->colors)+
          1,sizeof(*image->colormap));
        if (oldColormap == (PixelInfo *) NULL)
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
            image_info->filename);
        deshufflePalette(image,oldColormap);
        RelinquishMagickMemory(oldColormap);
      }
  }
  return(status);
}


static int __io_async_cancel(struct io_cancel_data *cd,
			     struct io_uring_task *tctx,
			     unsigned int issue_flags)
{
	bool all = cd->flags & (IORING_ASYNC_CANCEL_ALL|IORING_ASYNC_CANCEL_ANY);
	struct io_ring_ctx *ctx = cd->ctx;
	struct io_tctx_node *node;
	int ret, nr = 0;

	do {
		ret = io_try_cancel(tctx, cd, issue_flags);
		if (ret == -ENOENT)
			break;
		if (!all)
			return ret;
		nr++;
	} while (1);

	/* slow path, try all io-wq's */
	io_ring_submit_lock(ctx, issue_flags);
	ret = -ENOENT;
	list_for_each_entry(node, &ctx->tctx_list, ctx_node) {
		struct io_uring_task *tctx = node->task->io_uring;

		ret = io_async_cancel_one(tctx, cd);
		if (ret != -ENOENT) {
			if (!all)
				break;
			nr++;
		}
	}
	io_ring_submit_unlock(ctx, issue_flags);
	return all ? nr : ret;
}

io_uring: fix off-by-one in sync cancelation file check

The passed in index should be validated against the number of registered
files we have, it needs to be smaller than the index value to avoid going
one beyond the end.

Fixes: 78a861b94959 ("io_uring: add sync cancelation API through io_uring_register()")
Reported-by: Luo Likang <luolikang@nsfocus.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>


int io_async_cancel_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
	struct io_cancel *cancel = io_kiocb_to_cmd(req, struct io_cancel);

	if (unlikely(req->flags & REQ_F_BUFFER_SELECT))
		return -EINVAL;
	if (sqe->off || sqe->len || sqe->splice_fd_in)
		return -EINVAL;

	cancel->addr = READ_ONCE(sqe->addr);
	cancel->flags = READ_ONCE(sqe->cancel_flags);
	if (cancel->flags & ~CANCEL_FLAGS)
		return -EINVAL;
	if (cancel->flags & IORING_ASYNC_CANCEL_FD) {
		if (cancel->flags & IORING_ASYNC_CANCEL_ANY)
			return -EINVAL;
		cancel->fd = READ_ONCE(sqe->fd);
	}

	return 0;
}


	__must_hold(&ctx->uring_lock)
{
	struct io_cancel_data cd = {
		.ctx	= ctx,
		.seq	= atomic_inc_return(&ctx->cancel_seq),
	};
	ktime_t timeout = KTIME_MAX;
	struct io_uring_sync_cancel_reg sc;
	struct fd f = { };
	DEFINE_WAIT(wait);
	int ret;

	if (copy_from_user(&sc, arg, sizeof(sc)))
		return -EFAULT;
	if (sc.flags & ~CANCEL_FLAGS)
		return -EINVAL;
	if (sc.pad[0] || sc.pad[1] || sc.pad[2] || sc.pad[3])
		return -EINVAL;

	cd.data = sc.addr;
	cd.flags = sc.flags;

	/* we can grab a normal file descriptor upfront */
	if ((cd.flags & IORING_ASYNC_CANCEL_FD) &&
	   !(cd.flags & IORING_ASYNC_CANCEL_FD_FIXED)) {
		f = fdget(sc.fd);
		if (!f.file)
			return -EBADF;
		cd.file = f.file;
	}

	ret = __io_sync_cancel(current->io_uring, &cd, sc.fd);

	/* found something, done! */
	if (ret != -EALREADY)
		goto out;

	if (sc.timeout.tv_sec != -1UL || sc.timeout.tv_nsec != -1UL) {
		struct timespec64 ts = {
			.tv_sec		= sc.timeout.tv_sec,
			.tv_nsec	= sc.timeout.tv_nsec
		};

		timeout = ktime_add_ns(timespec64_to_ktime(ts), ktime_get_ns());
	}

	/*
	 * Keep looking until we get -ENOENT. we'll get woken everytime
	 * every time a request completes and will retry the cancelation.
	 */
	do {
		cd.seq = atomic_inc_return(&ctx->cancel_seq);

		prepare_to_wait(&ctx->cq_wait, &wait, TASK_INTERRUPTIBLE);

		ret = __io_sync_cancel(current->io_uring, &cd, sc.fd);

		if (ret != -EALREADY)
			break;

		mutex_unlock(&ctx->uring_lock);
		ret = io_run_task_work_sig();
		if (ret < 0) {
			mutex_lock(&ctx->uring_lock);
			break;
		}
		ret = schedule_hrtimeout(&timeout, HRTIMER_MODE_ABS);
		mutex_lock(&ctx->uring_lock);
		if (!ret) {
			ret = -ETIME;
			break;
		}
	} while (1);

	finish_wait(&ctx->cq_wait, &wait);

	if (ret == -ENOENT || ret > 0)
		ret = 0;
out:
	fdput(f);
	return ret;
}


int io_try_cancel(struct io_uring_task *tctx, struct io_cancel_data *cd,
		  unsigned issue_flags)
{
	struct io_ring_ctx *ctx = cd->ctx;
	int ret;

	WARN_ON_ONCE(!io_wq_current_is_worker() && tctx != current->io_uring);

	ret = io_async_cancel_one(tctx, cd);
	/*
	 * Fall-through even for -EALREADY, as we may have poll armed
	 * that need unarming.
	 */
	if (!ret)
		return 0;

	ret = io_poll_cancel(ctx, cd, issue_flags);
	if (ret != -ENOENT)
		return ret;

	spin_lock(&ctx->completion_lock);
	if (!(cd->flags & IORING_ASYNC_CANCEL_FD))
		ret = io_timeout_cancel(ctx, cd);
	spin_unlock(&ctx->completion_lock);
	return ret;
}


void init_hash_table(struct io_hash_table *table, unsigned size)
{
	unsigned int i;

	for (i = 0; i < size; i++) {
		spin_lock_init(&table->hbs[i].lock);
		INIT_HLIST_HEAD(&table->hbs[i].list);
	}
}


int io_async_cancel(struct io_kiocb *req, unsigned int issue_flags)
{
	struct io_cancel *cancel = io_kiocb_to_cmd(req, struct io_cancel);
	struct io_cancel_data cd = {
		.ctx	= req->ctx,
		.data	= cancel->addr,
		.flags	= cancel->flags,
		.seq	= atomic_inc_return(&req->ctx->cancel_seq),
	};
	struct io_uring_task *tctx = req->task->io_uring;
	int ret;

	if (cd.flags & IORING_ASYNC_CANCEL_FD) {
		if (req->flags & REQ_F_FIXED_FILE ||
		    cd.flags & IORING_ASYNC_CANCEL_FD_FIXED) {
			req->flags |= REQ_F_FIXED_FILE;
			req->file = io_file_get_fixed(req, cancel->fd,
							issue_flags);
		} else {
			req->file = io_file_get_normal(req, cancel->fd);
		}
		if (!req->file) {
			ret = -EBADF;
			goto done;
		}
		cd.file = req->file;
	}

	ret = __io_async_cancel(&cd, tctx, issue_flags);
done:
	if (ret < 0)
		req_set_fail(req);
	io_req_set_res(req, ret, 0);
	return IOU_OK;
}


static bool io_cancel_cb(struct io_wq_work *work, void *data)
{
	struct io_kiocb *req = container_of(work, struct io_kiocb, work);
	struct io_cancel_data *cd = data;

	if (req->ctx != cd->ctx)
		return false;
	if (cd->flags & IORING_ASYNC_CANCEL_ANY) {
		;
	} else if (cd->flags & IORING_ASYNC_CANCEL_FD) {
		if (req->file != cd->file)
			return false;
	} else {
		if (req->cqe.user_data != cd->data)
			return false;
	}
	if (cd->flags & (IORING_ASYNC_CANCEL_ALL|IORING_ASYNC_CANCEL_ANY)) {
		if (cd->seq == req->work.cancel_seq)
			return false;
		req->work.cancel_seq = cd->seq;
	}
	return true;
}


static int __io_sync_cancel(struct io_uring_task *tctx,
			    struct io_cancel_data *cd, int fd)
{
	struct io_ring_ctx *ctx = cd->ctx;

	/* fixed must be grabbed every time since we drop the uring_lock */
	if ((cd->flags & IORING_ASYNC_CANCEL_FD) &&
	    (cd->flags & IORING_ASYNC_CANCEL_FD_FIXED)) {
		unsigned long file_ptr;

		if (unlikely(fd >= ctx->nr_user_files))
			return -EBADF;
		fd = array_index_nospec(fd, ctx->nr_user_files);
		file_ptr = io_fixed_file_slot(&ctx->file_table, fd)->file_ptr;
		cd->file = (struct file *) (file_ptr & FFS_MASK);
		if (!cd->file)
			return -EBADF;
	}

	return __io_async_cancel(cd, tctx, 0);
}


static int io_async_cancel_one(struct io_uring_task *tctx,
			       struct io_cancel_data *cd)
{
	enum io_wq_cancel cancel_ret;
	int ret = 0;
	bool all;

	if (!tctx || !tctx->io_wq)
		return -ENOENT;

	all = cd->flags & (IORING_ASYNC_CANCEL_ALL|IORING_ASYNC_CANCEL_ANY);
	cancel_ret = io_wq_cancel_cb(tctx->io_wq, io_cancel_cb, cd, all);
	switch (cancel_ret) {
	case IO_WQ_CANCEL_OK:
		ret = 0;
		break;
	case IO_WQ_CANCEL_RUNNING:
		ret = -EALREADY;
		break;
	case IO_WQ_CANCEL_NOTFOUND:
		ret = -ENOENT;
		break;
	}

	return ret;
}


stl_initialize_facet_check_nearby(stl_file *stl) {
  int i;

  if (stl->error) return;

  stl->stats.malloced = 0;
  stl->stats.freed = 0;
  stl->stats.collisions = 0;

  /*  tolerance = STL_MAX(stl->stats.shortest_edge, tolerance);*/
  /*  tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/
  /*  tolerance *= 0.5;*/

  stl->M = 81397;

  stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads));
  if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby");

  stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
  if(stl->tail == NULL) perror("stl_initialize_facet_check_nearby");

  stl->tail->next = stl->tail;

  for(i = 0; i < stl->M; i++) {
    stl->heads[i] = stl->tail;
  }
}

Fix heap buffer overflow in stl_update_connects_remove_1

- Add argument value check to the stl_update_connects_remove_1
- Add neighbor value check in stl_remove_degenerate

Fixes https://github.com/admesh/admesh/issues/28
Merges https://github.com/admesh/admesh/pull/55


stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
                    stl_vertex *a, stl_vertex *b) {

  float diff_x;
  float diff_y;
  float diff_z;
  float max_diff;

  if (stl->error) return;

  diff_x = ABS(a->x - b->x);
  diff_y = ABS(a->y - b->y);
  diff_z = ABS(a->z - b->z);
  max_diff = STL_MAX(diff_x, diff_y);
  max_diff = STL_MAX(diff_z, max_diff);
  stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge);

  if(diff_x == max_diff) {
    if(a->x > b->x) {
      memcpy(&edge->key[0], a, sizeof(stl_vertex));
      memcpy(&edge->key[3], b, sizeof(stl_vertex));
    } else {
      memcpy(&edge->key[0], b, sizeof(stl_vertex));
      memcpy(&edge->key[3], a, sizeof(stl_vertex));
      edge->which_edge += 3; /* this edge is loaded backwards */
    }
  } else if(diff_y == max_diff) {
    if(a->y > b->y) {
      memcpy(&edge->key[0], a, sizeof(stl_vertex));
      memcpy(&edge->key[3], b, sizeof(stl_vertex));
    } else {
      memcpy(&edge->key[0], b, sizeof(stl_vertex));
      memcpy(&edge->key[3], a, sizeof(stl_vertex));
      edge->which_edge += 3; /* this edge is loaded backwards */
    }
  } else {
    if(a->z > b->z) {
      memcpy(&edge->key[0], a, sizeof(stl_vertex));
      memcpy(&edge->key[3], b, sizeof(stl_vertex));
    } else {
      memcpy(&edge->key[0], b, sizeof(stl_vertex));
      memcpy(&edge->key[3], a, sizeof(stl_vertex));
      edge->which_edge += 3; /* this edge is loaded backwards */
    }
  }
}


stl_get_hash_for_edge(int M, stl_hash_edge *edge) {
  return ((edge->key[0] / 23 + edge->key[1] / 19 + edge->key[2] / 17
           + edge->key[3] /13  + edge->key[4] / 11 + edge->key[5] / 7 ) % M);
}


insert_hash_edge(stl_file *stl, stl_hash_edge edge,
                 void (*match_neighbors)(stl_file *stl,
                     stl_hash_edge *edge_a, stl_hash_edge *edge_b)) {
  stl_hash_edge *link;
  stl_hash_edge *new_edge;
  stl_hash_edge *temp;
  int            chain_number;

  if (stl->error) return;

  chain_number = stl_get_hash_for_edge(stl->M, &edge);

  link = stl->heads[chain_number];

  if(link == stl->tail) {
    /* This list doesn't have any edges currently in it.  Add this one. */
    new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
    if(new_edge == NULL) perror("insert_hash_edge");
    stl->stats.malloced++;
    *new_edge = edge;
    new_edge->next = stl->tail;
    stl->heads[chain_number] = new_edge;
    return;
  } else  if(!stl_compare_function(&edge, link)) {
    /* This is a match.  Record result in neighbors list. */
    match_neighbors(stl, &edge, link);
    /* Delete the matched edge from the list. */
    stl->heads[chain_number] = link->next;
    free(link);
    stl->stats.freed++;
    return;
  } else {
    /* Continue through the rest of the list */
    for(;;) {
      if(link->next == stl->tail) {
        /* This is the last item in the list. Insert a new edge. */
        new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
        if(new_edge == NULL) perror("insert_hash_edge");
        stl->stats.malloced++;
        *new_edge = edge;
        new_edge->next = stl->tail;
        link->next = new_edge;
        stl->stats.collisions++;
        return;
      } else  if(!stl_compare_function(&edge, link->next)) {
        /* This is a match.  Record result in neighbors list. */
        match_neighbors(stl, &edge, link->next);

        /* Delete the matched edge from the list. */
        temp = link->next;
        link->next = link->next->next;
        free(temp);
        stl->stats.freed++;
        return;
      } else {
        /* This is not a match.  Go to the next link */
        link = link->next;
        stl->stats.collisions++;
      }
    }
  }
}


stl_match_neighbors_nearby(stl_file *stl,
                           stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
  int facet1;
  int facet2;
  int vertex1;
  int vertex2;
  int vnot1;
  int vnot2;
  stl_vertex new_vertex1;
  stl_vertex new_vertex2;

  if (stl->error) return;

  stl_record_neighbors(stl, edge_a, edge_b);
  stl_which_vertices_to_change(stl, edge_a, edge_b, &facet1, &vertex1,
                               &facet2, &vertex2, &new_vertex1, &new_vertex2);
  if(facet1 != -1) {
    if(facet1 == edge_a->facet_number) {
      vnot1 = (edge_a->which_edge + 2) % 3;
    } else {
      vnot1 = (edge_b->which_edge + 2) % 3;
    }
    if(((vnot1 + 2) % 3) == vertex1) {
      vnot1 += 3;
    }
    stl_change_vertices(stl, facet1, vnot1, new_vertex1);
  }
  if(facet2 != -1) {
    if(facet2 == edge_a->facet_number) {
      vnot2 = (edge_a->which_edge + 2) % 3;
    } else {
      vnot2 = (edge_b->which_edge + 2) % 3;
    }
    if(((vnot2 + 2) % 3) == vertex2) {
      vnot2 += 3;
    }
    stl_change_vertices(stl, facet2, vnot2, new_vertex2);
  }
  stl->stats.edges_fixed += 2;
}


stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
  if(edge_a->facet_number == edge_b->facet_number) {
    return 1;			/* Don't match edges of the same facet */
  } else {
    return memcmp(edge_a, edge_b, SIZEOF_EDGE_SORT);
  }
}


stl_free_edges(stl_file *stl) {
  int i;
  stl_hash_edge *temp;

  if (stl->error) return;

  if(stl->stats.malloced != stl->stats.freed) {
    for(i = 0; i < stl->M; i++) {
      for(temp = stl->heads[i]; stl->heads[i] != stl->tail;
          temp = stl->heads[i]) {
        stl->heads[i] = stl->heads[i]->next;
        free(temp);
        stl->stats.freed++;
      }
    }
  }
  free(stl->heads);
  free(stl->tail);
}


stl_check_facets_exact(stl_file *stl) {
  /* This function builds the neighbors list.  No modifications are made
   *  to any of the facets.  The edges are said to match only if all six
   *  floats of the first edge matches all six floats of the second edge.
   */

  stl_hash_edge  edge;
  stl_facet      facet;
  int            i;
  int            j;

  if (stl->error) return;

  stl->stats.connected_edges = 0;
  stl->stats.connected_facets_1_edge = 0;
  stl->stats.connected_facets_2_edge = 0;
  stl->stats.connected_facets_3_edge = 0;

  stl_initialize_facet_check_exact(stl);

  for(i = 0; i < stl->stats.number_of_facets; i++) {
    facet = stl->facet_start[i];

    /* If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. */
    if(   !memcmp(&facet.vertex[0], &facet.vertex[1],
                  sizeof(stl_vertex))
          || !memcmp(&facet.vertex[1], &facet.vertex[2],
                     sizeof(stl_vertex))
          || !memcmp(&facet.vertex[0], &facet.vertex[2],
                     sizeof(stl_vertex))) {
      stl->stats.degenerate_facets += 1;
      stl_remove_facet(stl, i);
      i--;
      continue;

    }
    for(j = 0; j < 3; j++) {
      edge.facet_number = i;
      edge.which_edge = j;
      stl_load_edge_exact(stl, &edge, &facet.vertex[j],
                          &facet.vertex[(j + 1) % 3]);

      insert_hash_edge(stl, edge, stl_match_neighbors_exact);
    }
  }
  stl_free_edges(stl);
}


stl_change_vertices(stl_file *stl, int facet_num, int vnot,
                    stl_vertex new_vertex) {
  int first_facet;
  int direction;
  int next_edge;
  int pivot_vertex;

  if (stl->error) return;

  first_facet = facet_num;
  direction = 0;

  for(;;) {
    if(vnot > 2) {
      if(direction == 0) {
        pivot_vertex = (vnot + 2) % 3;
        next_edge = pivot_vertex;
        direction = 1;
      } else {
        pivot_vertex = (vnot + 1) % 3;
        next_edge = vnot % 3;
        direction = 0;
      }
    } else {
      if(direction == 0) {
        pivot_vertex = (vnot + 1) % 3;
        next_edge = vnot;
      } else {
        pivot_vertex = (vnot + 2) % 3;
        next_edge = pivot_vertex;
      }
    }
    stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex;
    vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge];
    facet_num = stl->neighbors_start[facet_num].neighbor[next_edge];

    if(facet_num == -1) {
      break;
    }

    if(facet_num == first_facet) {
      /* back to the beginning */
      printf("\
Back to the first facet changing vertices: probably a mobius part.\n\
Try using a smaller tolerance or don't do a nearby check\n");
      return;
    }
  }
}


stl_initialize_facet_check_exact(stl_file *stl) {
  int i;

  if (stl->error) return;

  stl->stats.malloced = 0;
  stl->stats.freed = 0;
  stl->stats.collisions = 0;

  stl->M = 81397;

  for(i = 0; i < stl->stats.number_of_facets ; i++) {
    /* initialize neighbors list to -1 to mark unconnected edges */
    stl->neighbors_start[i].neighbor[0] = -1;
    stl->neighbors_start[i].neighbor[1] = -1;
    stl->neighbors_start[i].neighbor[2] = -1;
  }

  stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads));
  if(stl->heads == NULL) perror("stl_initialize_facet_check_exact");

  stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
  if(stl->tail == NULL) perror("stl_initialize_facet_check_exact");

  stl->tail->next = stl->tail;

  for(i = 0; i < stl->M; i++) {
    stl->heads[i] = stl->tail;
  }
}


stl_update_connects_remove_1(stl_file *stl, int facet_num) {
  int j;

  if (
    stl->error ||
    facet_num < 0
  ) return;

  /* Update list of connected edges */
  j = ((stl->neighbors_start[facet_num].neighbor[0] == -1) +
       (stl->neighbors_start[facet_num].neighbor[1] == -1) +
       (stl->neighbors_start[facet_num].neighbor[2] == -1));
  if(j == 0) {		       /* Facet has 3 neighbors */
    stl->stats.connected_facets_3_edge -= 1;
  } else if(j == 1) {	     /* Facet has 2 neighbors */
    stl->stats.connected_facets_2_edge -= 1;
  } else if(j == 2) {	     /* Facet has 1 neighbor  */
    stl->stats.connected_facets_1_edge -= 1;
  }
}


stl_fill_holes(stl_file *stl) {
  stl_facet facet;
  stl_facet new_facet;
  int neighbors_initial[3];
  stl_hash_edge edge;
  int first_facet;
  int direction;
  int facet_num;
  int vnot;
  int next_edge;
  int pivot_vertex;
  int next_facet;
  int i;
  int j;
  int k;

  if (stl->error) return;

  /* Insert all unconnected edges into hash list */
  stl_initialize_facet_check_nearby(stl);
  for(i = 0; i < stl->stats.number_of_facets; i++) {
    facet = stl->facet_start[i];
    for(j = 0; j < 3; j++) {
      if(stl->neighbors_start[i].neighbor[j] != -1) continue;
      edge.facet_number = i;
      edge.which_edge = j;
      stl_load_edge_exact(stl, &edge, &facet.vertex[j],
                          &facet.vertex[(j + 1) % 3]);

      insert_hash_edge(stl, edge, stl_match_neighbors_exact);
    }
  }

  for(i = 0; i < stl->stats.number_of_facets; i++) {
    facet = stl->facet_start[i];
    neighbors_initial[0] = stl->neighbors_start[i].neighbor[0];
    neighbors_initial[1] = stl->neighbors_start[i].neighbor[1];
    neighbors_initial[2] = stl->neighbors_start[i].neighbor[2];
    first_facet = i;
    for(j = 0; j < 3; j++) {
      if(stl->neighbors_start[i].neighbor[j] != -1) continue;

      new_facet.vertex[0] = facet.vertex[j];
      new_facet.vertex[1] = facet.vertex[(j + 1) % 3];
      if(neighbors_initial[(j + 2) % 3] == -1) {
        direction = 1;
      } else {
        direction = 0;
      }

      facet_num = i;
      vnot = (j + 2) % 3;

      for(;;) {
        if(vnot > 2) {
          if(direction == 0) {
            pivot_vertex = (vnot + 2) % 3;
            next_edge = pivot_vertex;
            direction = 1;
          } else {
            pivot_vertex = (vnot + 1) % 3;
            next_edge = vnot % 3;
            direction = 0;
          }
        } else {
          if(direction == 0) {
            pivot_vertex = (vnot + 1) % 3;
            next_edge = vnot;
          } else {
            pivot_vertex = (vnot + 2) % 3;
            next_edge = pivot_vertex;
          }
        }
        next_facet = stl->neighbors_start[facet_num].neighbor[next_edge];

        if(next_facet == -1) {
          new_facet.vertex[2] = stl->facet_start[facet_num].
                                vertex[vnot % 3];
          stl_add_facet(stl, &new_facet);
          for(k = 0; k < 3; k++) {
            edge.facet_number = stl->stats.number_of_facets - 1;
            edge.which_edge = k;
            stl_load_edge_exact(stl, &edge, &new_facet.vertex[k],
                                &new_facet.vertex[(k + 1) % 3]);

            insert_hash_edge(stl, edge, stl_match_neighbors_exact);
          }
          break;
        } else {
          vnot = stl->neighbors_start[facet_num].
                 which_vertex_not[next_edge];
          facet_num = next_facet;
        }

        if(facet_num == first_facet) {
          /* back to the beginning */
          printf("\
Back to the first facet filling holes: probably a mobius part.\n\
Try using a smaller tolerance or don't do a nearby check\n");
          return;
        }
      }
    }
  }
  free(stl->heads);
  free(stl->tail);
}


stl_match_neighbors_exact(stl_file *stl,
                          stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
  if (stl->error) return;
  stl_record_neighbors(stl, edge_a, edge_b);
}


stl_check_facets_nearby(stl_file *stl, float tolerance) {
  stl_hash_edge  edge[3];
  stl_facet      facet;
  int            i;
  int            j;

  if (stl->error) return;

  if(   (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets)
        && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets)
        && (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) {
    /* No need to check any further.  All facets are connected */
    return;
  }

  stl_initialize_facet_check_nearby(stl);

  for(i = 0; i < stl->stats.number_of_facets; i++) {
    facet = stl->facet_start[i];
    for(j = 0; j < 3; j++) {
      if(stl->neighbors_start[i].neighbor[j] == -1) {
        edge[j].facet_number = i;
        edge[j].which_edge = j;
        if(stl_load_edge_nearby(stl, &edge[j], &facet.vertex[j],
                                &facet.vertex[(j + 1) % 3],
                                tolerance)) {
          /* only insert edges that have different keys */
          insert_hash_edge(stl, edge[j], stl_match_neighbors_nearby);
        }
      }
    }
  }

  stl_free_edges(stl);
}


stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
                     stl_vertex *a, stl_vertex *b, float tolerance) {
  float diff_x;
  float diff_y;
  float diff_z;
  float max_diff;
  unsigned vertex1[3];
  unsigned vertex2[3];

  diff_x = ABS(a->x - b->x);
  diff_y = ABS(a->y - b->y);
  diff_z = ABS(a->z - b->z);
  max_diff = STL_MAX(diff_x, diff_y);
  max_diff = STL_MAX(diff_z, max_diff);

  vertex1[0] = (unsigned)((a->x - stl->stats.min.x) / tolerance);
  vertex1[1] = (unsigned)((a->y - stl->stats.min.y) / tolerance);
  vertex1[2] = (unsigned)((a->z - stl->stats.min.z) / tolerance);
  vertex2[0] = (unsigned)((b->x - stl->stats.min.x) / tolerance);
  vertex2[1] = (unsigned)((b->y - stl->stats.min.y) / tolerance);
  vertex2[2] = (unsigned)((b->z - stl->stats.min.z) / tolerance);

  if(   (vertex1[0] == vertex2[0])
        && (vertex1[1] == vertex2[1])
        && (vertex1[2] == vertex2[2])) {
    /* Both vertices hash to the same value */
    return 0;
  }

  if(diff_x == max_diff) {
    if(a->x > b->x) {
      memcpy(&edge->key[0], vertex1, sizeof(stl_vertex));
      memcpy(&edge->key[3], vertex2, sizeof(stl_vertex));
    } else {
      memcpy(&edge->key[0], vertex2, sizeof(stl_vertex));
      memcpy(&edge->key[3], vertex1, sizeof(stl_vertex));
      edge->which_edge += 3; /* this edge is loaded backwards */
    }
  } else if(diff_y == max_diff) {
    if(a->y > b->y) {
      memcpy(&edge->key[0], vertex1, sizeof(stl_vertex));
      memcpy(&edge->key[3], vertex2, sizeof(stl_vertex));
    } else {
      memcpy(&edge->key[0], vertex2, sizeof(stl_vertex));
      memcpy(&edge->key[3], vertex1, sizeof(stl_vertex));
      edge->which_edge += 3; /* this edge is loaded backwards */
    }
  } else {
    if(a->z > b->z) {
      memcpy(&edge->key[0], vertex1, sizeof(stl_vertex));
      memcpy(&edge->key[3], vertex2, sizeof(stl_vertex));
    } else {
      memcpy(&edge->key[0], vertex2, sizeof(stl_vertex));
      memcpy(&edge->key[3], vertex1, sizeof(stl_vertex));
      edge->which_edge += 3; /* this edge is loaded backwards */
    }
  }
  return 1;
}


stl_remove_degenerate(stl_file *stl, int facet) {
  int edge1;
  int edge2;
  int edge3;
  int neighbor1;
  int neighbor2;
  int neighbor3;
  int vnot1;
  int vnot2;
  int vnot3;

  if (stl->error) return;

  if(   !memcmp(&stl->facet_start[facet].vertex[0],
                &stl->facet_start[facet].vertex[1], sizeof(stl_vertex))
        && !memcmp(&stl->facet_start[facet].vertex[1],
                   &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
    /* all 3 vertices are equal.  Just remove the facet.  I don't think*/
    /* this is really possible, but just in case... */
    printf("removing a facet in stl_remove_degenerate\n");

    stl_remove_facet(stl, facet);
    return;
  }

  if(!memcmp(&stl->facet_start[facet].vertex[0],
             &stl->facet_start[facet].vertex[1], sizeof(stl_vertex))) {
    edge1 = 1;
    edge2 = 2;
    edge3 = 0;
  } else if(!memcmp(&stl->facet_start[facet].vertex[1],
                    &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
    edge1 = 0;
    edge2 = 2;
    edge3 = 1;
  } else if(!memcmp(&stl->facet_start[facet].vertex[2],
                    &stl->facet_start[facet].vertex[0], sizeof(stl_vertex))) {
    edge1 = 0;
    edge2 = 1;
    edge3 = 2;
  } else {
    /* No degenerate. Function shouldn't have been called. */
    return;
  }
  neighbor1 = stl->neighbors_start[facet].neighbor[edge1];
  neighbor2 = stl->neighbors_start[facet].neighbor[edge2];

  if(neighbor1 == -1 && neighbor2 != -1) {
    stl_update_connects_remove_1(stl, neighbor2);
  }
  else if (neighbor2 == -1 && neighbor1 != -1) {
    stl_update_connects_remove_1(stl, neighbor1);
  }

  neighbor3 = stl->neighbors_start[facet].neighbor[edge3];
  vnot1 = stl->neighbors_start[facet].which_vertex_not[edge1];
  vnot2 = stl->neighbors_start[facet].which_vertex_not[edge2];
  vnot3 = stl->neighbors_start[facet].which_vertex_not[edge3];

  if(neighbor1 != -1){
    stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2;
    stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2;
  }
  if(neighbor2 != -1){
    stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1;
    stl->neighbors_start[neighbor2].which_vertex_not[(vnot2 + 1) % 3] = vnot1;
  }

  stl_remove_facet(stl, facet);

  if(neighbor3 != -1) {
    stl_update_connects_remove_1(stl, neighbor3);
    stl->neighbors_start[neighbor3].neighbor[(vnot3 + 1) % 3] = -1;
  }
}


stl_record_neighbors(stl_file *stl,
                     stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
  int i;
  int j;

  if (stl->error) return;

  /* Facet a's neighbor is facet b */
  stl->neighbors_start[edge_a->facet_number].neighbor[edge_a->which_edge % 3] =
    edge_b->facet_number;	/* sets the .neighbor part */

  stl->neighbors_start[edge_a->facet_number].
  which_vertex_not[edge_a->which_edge % 3] =
    (edge_b->which_edge + 2) % 3; /* sets the .which_vertex_not part */

  /* Facet b's neighbor is facet a */
  stl->neighbors_start[edge_b->facet_number].neighbor[edge_b->which_edge % 3] =
    edge_a->facet_number;	/* sets the .neighbor part */

  stl->neighbors_start[edge_b->facet_number].
  which_vertex_not[edge_b->which_edge % 3] =
    (edge_a->which_edge + 2) % 3; /* sets the .which_vertex_not part */

  if(   ((edge_a->which_edge < 3) && (edge_b->which_edge < 3))
        || ((edge_a->which_edge > 2) && (edge_b->which_edge > 2))) {
    /* these facets are oriented in opposite directions.  */
    /*  their normals are probably messed up. */
    stl->neighbors_start[edge_a->facet_number].
    which_vertex_not[edge_a->which_edge % 3] += 3;
    stl->neighbors_start[edge_b->facet_number].
    which_vertex_not[edge_b->which_edge % 3] += 3;
  }

  /* Count successful connects */
  /* Total connects */
  stl->stats.connected_edges += 2;
  /* Count individual connects */
  i = ((stl->neighbors_start[edge_a->facet_number].neighbor[0] == -1) +
       (stl->neighbors_start[edge_a->facet_number].neighbor[1] == -1) +
       (stl->neighbors_start[edge_a->facet_number].neighbor[2] == -1));
  j = ((stl->neighbors_start[edge_b->facet_number].neighbor[0] == -1) +
       (stl->neighbors_start[edge_b->facet_number].neighbor[1] == -1) +
       (stl->neighbors_start[edge_b->facet_number].neighbor[2] == -1));
  if(i == 2) {
    stl->stats.connected_facets_1_edge +=1;
  } else if(i == 1) {
    stl->stats.connected_facets_2_edge +=1;
  } else {
    stl->stats.connected_facets_3_edge +=1;
  }
  if(j == 2) {
    stl->stats.connected_facets_1_edge +=1;
  } else if(j == 1) {
    stl->stats.connected_facets_2_edge +=1;
  } else {
    stl->stats.connected_facets_3_edge +=1;
  }
}


stl_remove_unconnected_facets(stl_file *stl) {
  /* A couple of things need to be done here.  One is to remove any */
  /* completely unconnected facets (0 edges connected) since these are */
  /* useless and could be completely wrong.   The second thing that needs to */
  /* be done is to remove any degenerate facets that were created during */
  /* stl_check_facets_nearby(). */

  int i;

  if (stl->error) return;

  /* remove degenerate facets */
  for(i = 0; i < stl->stats.number_of_facets; i++) {
    if(   !memcmp(&stl->facet_start[i].vertex[0],
                  &stl->facet_start[i].vertex[1], sizeof(stl_vertex))
          || !memcmp(&stl->facet_start[i].vertex[1],
                     &stl->facet_start[i].vertex[2], sizeof(stl_vertex))
          || !memcmp(&stl->facet_start[i].vertex[0],
                     &stl->facet_start[i].vertex[2], sizeof(stl_vertex))) {
      stl_remove_degenerate(stl, i);
      i--;
    }
  }

  if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) {
    /* remove completely unconnected facets */
    for(i = 0; i < stl->stats.number_of_facets; i++) {
      if(   (stl->neighbors_start[i].neighbor[0] == -1)
            && (stl->neighbors_start[i].neighbor[1] == -1)
            && (stl->neighbors_start[i].neighbor[2] == -1)) {
        /* This facet is completely unconnected.  Remove it. */
        stl_remove_facet(stl, i);
        i--;
      }
    }
  }
}


stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
                             stl_hash_edge *edge_b, int *facet1, int *vertex1,
                             int *facet2, int *vertex2,
                             stl_vertex *new_vertex1, stl_vertex *new_vertex2) {
  int v1a;			/* pair 1, facet a */
  int v1b;			/* pair 1, facet b */
  int v2a;			/* pair 2, facet a */
  int v2b;			/* pair 2, facet b */

  /* Find first pair */
  if(edge_a->which_edge < 3) {
    v1a = edge_a->which_edge;
    v2a = (edge_a->which_edge + 1) % 3;
  } else {
    v2a = edge_a->which_edge % 3;
    v1a = (edge_a->which_edge + 1) % 3;
  }
  if(edge_b->which_edge < 3) {
    v1b = edge_b->which_edge;
    v2b = (edge_b->which_edge + 1) % 3;
  } else {
    v2b = edge_b->which_edge % 3;
    v1b = (edge_b->which_edge + 1) % 3;
  }

  /* Of the first pair, which vertex, if any, should be changed */
  if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v1a],
             &stl->facet_start[edge_b->facet_number].vertex[v1b],
             sizeof(stl_vertex))) {
    /* These facets are already equal.  No need to change. */
    *facet1 = -1;
  } else {
    if(   (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1)
          && (stl->neighbors_start[edge_a->facet_number].
              neighbor[(v1a + 2) % 3] == -1)) {
      /* This vertex has no neighbors.  This is a good one to change */
      *facet1 = edge_a->facet_number;
      *vertex1 = v1a;
      *new_vertex1 = stl->facet_start[edge_b->facet_number].vertex[v1b];
    } else {
      *facet1 = edge_b->facet_number;
      *vertex1 = v1b;
      *new_vertex1 = stl->facet_start[edge_a->facet_number].vertex[v1a];
    }
  }

  /* Of the second pair, which vertex, if any, should be changed */
  if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v2a],
             &stl->facet_start[edge_b->facet_number].vertex[v2b],
             sizeof(stl_vertex))) {
    /* These facets are already equal.  No need to change. */
    *facet2 = -1;
  } else {
    if(   (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1)
          && (stl->neighbors_start[edge_a->facet_number].
              neighbor[(v2a + 2) % 3] == -1)) {
      /* This vertex has no neighbors.  This is a good one to change */
      *facet2 = edge_a->facet_number;
      *vertex2 = v2a;
      *new_vertex2 = stl->facet_start[edge_b->facet_number].vertex[v2b];
    } else {
      *facet2 = edge_b->facet_number;
      *vertex2 = v2b;
      *new_vertex2 = stl->facet_start[edge_a->facet_number].vertex[v2a];
    }
  }
}


stl_add_facet(stl_file *stl, stl_facet *new_facet) {
  if (stl->error) return;

  stl->stats.facets_added += 1;
  if(stl->stats.facets_malloced < stl->stats.number_of_facets + 1) {
    stl->facet_start = (stl_facet*)realloc(stl->facet_start,
                                           (sizeof(stl_facet) * (stl->stats.facets_malloced + 256)));
    if(stl->facet_start == NULL) perror("stl_add_facet");
    stl->neighbors_start = (stl_neighbors*)realloc(stl->neighbors_start,
                           (sizeof(stl_neighbors) * (stl->stats.facets_malloced + 256)));
    if(stl->neighbors_start == NULL) perror("stl_add_facet");
    stl->stats.facets_malloced += 256;
  }
  stl->facet_start[stl->stats.number_of_facets] = *new_facet;

  /* note that the normal vector is not set here, just initialized to 0 */
  stl->facet_start[stl->stats.number_of_facets].normal.x = 0.0;
  stl->facet_start[stl->stats.number_of_facets].normal.y = 0.0;
  stl->facet_start[stl->stats.number_of_facets].normal.z = 0.0;

  stl->neighbors_start[stl->stats.number_of_facets].neighbor[0] = -1;
  stl->neighbors_start[stl->stats.number_of_facets].neighbor[1] = -1;
  stl->neighbors_start[stl->stats.number_of_facets].neighbor[2] = -1;
  stl->stats.number_of_facets += 1;
}


stl_remove_facet(stl_file *stl, int facet_number) {
  int neighbor[3];
  int vnot[3];
  int i;
  int j;

  if (stl->error) return;

  stl->stats.facets_removed += 1;
  /* Update list of connected edges */
  j = ((stl->neighbors_start[facet_number].neighbor[0] == -1) +
       (stl->neighbors_start[facet_number].neighbor[1] == -1) +
       (stl->neighbors_start[facet_number].neighbor[2] == -1));
  if(j == 2) {
    stl->stats.connected_facets_1_edge -= 1;
  } else if(j == 1) {
    stl->stats.connected_facets_2_edge -= 1;
    stl->stats.connected_facets_1_edge -= 1;
  } else if(j == 0) {
    stl->stats.connected_facets_3_edge -= 1;
    stl->stats.connected_facets_2_edge -= 1;
    stl->stats.connected_facets_1_edge -= 1;
  }

  stl->facet_start[facet_number] =
    stl->facet_start[stl->stats.number_of_facets - 1];
  /* I could reallocate at this point, but it is not really necessary. */
  stl->neighbors_start[facet_number] =
    stl->neighbors_start[stl->stats.number_of_facets - 1];
  stl->stats.number_of_facets -= 1;

  for(i = 0; i < 3; i++) {
    neighbor[i] = stl->neighbors_start[facet_number].neighbor[i];
    vnot[i] = stl->neighbors_start[facet_number].which_vertex_not[i];
  }

  for(i = 0; i < 3; i++) {
    if(neighbor[i] != -1) {
      if(stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] !=
          stl->stats.number_of_facets) {
        printf("\
in stl_remove_facet: neighbor = %d numfacets = %d this is wrong\n",
               stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3],
               stl->stats.number_of_facets);
        return;
      }
      stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3]
        = facet_number;
    }
  }
}


static void request_module_async(struct work_struct *work)
{
	struct em28xx *dev = container_of(work,
			     struct em28xx, request_module_wk);

	/*
	 * The em28xx extensions can be modules or builtin. If the
	 * modules are already loaded or are built in, those extensions
	 * can be initialised right now. Otherwise, the module init
	 * code will do it.
	 */

	/*
	 * Devices with an audio-only intf also have a V4L/DVB/RC
	 * intf. Don't register extensions twice on those devices.
	 */
	if (dev->is_audio_only) {
#if defined(CONFIG_MODULES) && defined(MODULE)
		request_module("em28xx-alsa");
#endif
		return;
	}

	em28xx_init_extension(dev);

#if defined(CONFIG_MODULES) && defined(MODULE)
	if (dev->has_video)
		request_module("em28xx-v4l");
	if (dev->usb_audio_type == EM28XX_USB_AUDIO_CLASS)
		request_module("snd-usb-audio");
	else if (dev->usb_audio_type == EM28XX_USB_AUDIO_VENDOR)
		request_module("em28xx-alsa");
	if (dev->board.has_dvb)
		request_module("em28xx-dvb");
	if (dev->board.buttons ||
	    ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir))
		request_module("em28xx-rc");
#endif /* CONFIG_MODULES */
}

media: em28xx: initialize refcount before kref_get

The commit 47677e51e2a4("[media] em28xx: Only deallocate struct
em28xx after finishing all extensions") adds kref_get to many init
functions (e.g., em28xx_audio_init). However, kref_init is called too
late in em28xx_usb_probe, since em28xx_init_dev before will invoke
those init functions and call kref_get function. Then refcount bug
occurs in my local syzkaller instance.

Fix it by moving kref_init before em28xx_init_dev. This issue occurs
not only in dev but also dev->dev_next.

Fixes: 47677e51e2a4 ("[media] em28xx: Only deallocate struct em28xx after finishing all extensions")
Reported-by: syzkaller <syzkaller@googlegroups.com>
Signed-off-by: Dongliang Mu <mudongliangabcd@gmail.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>


static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
			   struct usb_interface *intf,
			   int minor)
{
	int retval;
	const char *chip_name = NULL;

	dev->intf = intf;
	mutex_init(&dev->ctrl_urb_lock);
	spin_lock_init(&dev->slock);

	dev->em28xx_write_regs = em28xx_write_regs;
	dev->em28xx_read_reg = em28xx_read_reg;
	dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
	dev->em28xx_write_regs_req = em28xx_write_regs_req;
	dev->em28xx_read_reg_req = em28xx_read_reg_req;
	dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;

	em28xx_set_model(dev);

	dev->wait_after_write = 5;

	/* Based on the Chip ID, set the device configuration */
	retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
	if (retval > 0) {
		dev->chip_id = retval;

		switch (dev->chip_id) {
		case CHIP_ID_EM2800:
			chip_name = "em2800";
			break;
		case CHIP_ID_EM2710:
			chip_name = "em2710";
			break;
		case CHIP_ID_EM2750:
			chip_name = "em2750";
			break;
		case CHIP_ID_EM2765:
			chip_name = "em2765";
			dev->wait_after_write = 0;
			dev->is_em25xx = 1;
			dev->eeprom_addrwidth_16bit = 1;
			break;
		case CHIP_ID_EM2820:
			chip_name = "em2710/2820";
			if (le16_to_cpu(udev->descriptor.idVendor) == 0xeb1a) {
				__le16 idProd = udev->descriptor.idProduct;

				if (le16_to_cpu(idProd) == 0x2710)
					chip_name = "em2710";
				else if (le16_to_cpu(idProd) == 0x2820)
					chip_name = "em2820";
			}
			/* NOTE: the em2820 is used in webcams, too ! */
			break;
		case CHIP_ID_EM2840:
			chip_name = "em2840";
			break;
		case CHIP_ID_EM2860:
			chip_name = "em2860";
			break;
		case CHIP_ID_EM2870:
			chip_name = "em2870";
			dev->wait_after_write = 0;
			break;
		case CHIP_ID_EM2874:
			chip_name = "em2874";
			dev->wait_after_write = 0;
			dev->eeprom_addrwidth_16bit = 1;
			break;
		case CHIP_ID_EM28174:
			chip_name = "em28174";
			dev->wait_after_write = 0;
			dev->eeprom_addrwidth_16bit = 1;
			break;
		case CHIP_ID_EM28178:
			chip_name = "em28178";
			dev->wait_after_write = 0;
			dev->eeprom_addrwidth_16bit = 1;
			break;
		case CHIP_ID_EM2883:
			chip_name = "em2882/3";
			dev->wait_after_write = 0;
			break;
		case CHIP_ID_EM2884:
			chip_name = "em2884";
			dev->wait_after_write = 0;
			dev->eeprom_addrwidth_16bit = 1;
			break;
		}
	}
	if (!chip_name)
		dev_info(&dev->intf->dev,
			 "unknown em28xx chip ID (%d)\n", dev->chip_id);
	else
		dev_info(&dev->intf->dev, "chip ID is %s\n", chip_name);

	em28xx_media_device_init(dev, udev);

	if (dev->is_audio_only) {
		retval = em28xx_audio_setup(dev);
		if (retval) {
			retval = -ENODEV;
			goto err_deinit_media;
		}
		em28xx_init_extension(dev);

		return 0;
	}

	em28xx_pre_card_setup(dev);

	rt_mutex_init(&dev->i2c_bus_lock);

	/* register i2c bus 0 */
	if (dev->board.is_em2800)
		retval = em28xx_i2c_register(dev, 0, EM28XX_I2C_ALGO_EM2800);
	else
		retval = em28xx_i2c_register(dev, 0, EM28XX_I2C_ALGO_EM28XX);
	if (retval < 0) {
		dev_err(&dev->intf->dev,
			"%s: em28xx_i2c_register bus 0 - error [%d]!\n",
		       __func__, retval);
		goto err_deinit_media;
	}

	/* register i2c bus 1 */
	if (dev->def_i2c_bus) {
		if (dev->is_em25xx)
			retval = em28xx_i2c_register(dev, 1,
						     EM28XX_I2C_ALGO_EM25XX_BUS_B);
		else
			retval = em28xx_i2c_register(dev, 1,
						     EM28XX_I2C_ALGO_EM28XX);
		if (retval < 0) {
			dev_err(&dev->intf->dev,
				"%s: em28xx_i2c_register bus 1 - error [%d]!\n",
				__func__, retval);

			goto err_unreg_i2c;
		}
	}

	/* Do board specific init and eeprom reading */
	em28xx_card_setup(dev);

	return 0;

err_unreg_i2c:
	em28xx_i2c_unregister(dev, 0);
err_deinit_media:
	em28xx_unregister_media_device(dev);
	return retval;
}


static void request_modules(struct em28xx *dev)
{
	INIT_WORK(&dev->request_module_wk, request_module_async);
	schedule_work(&dev->request_module_wk);
}


static int em28xx_usb_resume(struct usb_interface *intf)
{
	struct em28xx *dev;

	dev = usb_get_intfdata(intf);
	if (!dev)
		return 0;
	em28xx_resume_extension(dev);
	return 0;
}


void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
{
	memset(ctl, 0, sizeof(*ctl));

	ctl->fname   = XC2028_DEFAULT_FIRMWARE;
	ctl->max_len = 64;
	ctl->mts = em28xx_boards[dev->model].mts_firmware;

	switch (dev->model) {
	case EM2880_BOARD_EMPIRE_DUAL_TV:
	case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
	case EM2882_BOARD_TERRATEC_HYBRID_XS:
	case EM2880_BOARD_TERRATEC_HYBRID_XS:
	case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
	case EM2881_BOARD_PINNACLE_HYBRID_PRO:
	case EM2882_BOARD_ZOLID_HYBRID_TV_STICK:
		ctl->demod = XC3028_FE_ZARLINK456;
		break;
	case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
	case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
		ctl->demod = XC3028_FE_DEFAULT;
		break;
	case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
		ctl->demod = XC3028_FE_DEFAULT;
		ctl->fname = XC3028L_DEFAULT_FIRMWARE;
		break;
	case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
	case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
	case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
		/* FIXME: Better to specify the needed IF */
		ctl->demod = XC3028_FE_DEFAULT;
		break;
	case EM2883_BOARD_KWORLD_HYBRID_330U:
	case EM2882_BOARD_DIKOM_DK300:
	case EM2882_BOARD_KWORLD_VS_DVBT:
		ctl->demod = XC3028_FE_CHINA;
		ctl->fname = XC2028_DEFAULT_FIRMWARE;
		break;
	case EM2882_BOARD_EVGA_INDTUBE:
		ctl->demod = XC3028_FE_CHINA;
		ctl->fname = XC3028L_DEFAULT_FIRMWARE;
		break;
	default:
		ctl->demod = XC3028_FE_OREN538;
	}
}


static void em28xx_release_resources(struct em28xx *dev)
{
	struct usb_device *udev = interface_to_usbdev(dev->intf);

	/*FIXME: I2C IR should be disconnected */

	mutex_lock(&dev->lock);

	em28xx_unregister_media_device(dev);

	if (dev->def_i2c_bus)
		em28xx_i2c_unregister(dev, 1);
	em28xx_i2c_unregister(dev, 0);

	if (dev->ts == PRIMARY_TS)
		usb_put_dev(udev);

	/* Mark device as unused */
	clear_bit(dev->devno, em28xx_devused);

	mutex_unlock(&dev->lock);
};


static int em28xx_duplicate_dev(struct em28xx *dev)
{
	int nr;
	struct em28xx *sec_dev = kmemdup(dev, sizeof(*sec_dev), GFP_KERNEL);

	if (!sec_dev) {
		dev->dev_next = NULL;
		return -ENOMEM;
	}
	/* Check to see next free device and mark as used */
	do {
		nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS);
		if (nr >= EM28XX_MAXBOARDS) {
			/* No free device slots */
			dev_warn(&dev->intf->dev, ": Supports only %i em28xx boards.\n",
				 EM28XX_MAXBOARDS);
			kfree(sec_dev);
			dev->dev_next = NULL;
			return -ENOMEM;
		}
	} while (test_and_set_bit(nr, em28xx_devused));
	sec_dev->devno = nr;
	snprintf(sec_dev->name, 28, "em28xx #%d", nr);
	sec_dev->dev_next = NULL;
	dev->dev_next = sec_dev;
	return 0;
}


static int em28xx_media_device_init(struct em28xx *dev,
				    struct usb_device *udev)
{
#ifdef CONFIG_MEDIA_CONTROLLER
	struct media_device *mdev;

	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
	if (!mdev)
		return -ENOMEM;

	if (udev->product)
		media_device_usb_init(mdev, udev, udev->product);
	else if (udev->manufacturer)
		media_device_usb_init(mdev, udev, udev->manufacturer);
	else
		media_device_usb_init(mdev, udev, dev_name(&dev->intf->dev));

	dev->media_dev = mdev;
#endif
	return 0;
}


int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
{
	struct em28xx_i2c_bus *i2c_bus = ptr;
	struct em28xx *dev = i2c_bus->dev;
	int rc = 0;

	if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000)
		return 0;

	if (command != XC2028_TUNER_RESET && command != XC5000_TUNER_RESET)
		return 0;

	rc = em28xx_gpio_set(dev, dev->board.tuner_gpio);

	return rc;
}


static int em28xx_hint_board(struct em28xx *dev)
{
	int i;

	if (dev->is_webcam) {
		if (dev->em28xx_sensor == EM28XX_MT9V011) {
			dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
		} else if (dev->em28xx_sensor == EM28XX_MT9M001 ||
			   dev->em28xx_sensor == EM28XX_MT9M111) {
			dev->model = EM2750_BOARD_UNKNOWN;
		}
		/* FIXME: IMPROVE ! */

		return 0;
	}

	/*
	 * HINT method: EEPROM
	 *
	 * This method works only for boards with eeprom.
	 * Uses a hash of all eeprom bytes. The hash should be
	 * unique for a vendor/tuner pair.
	 * There are a high chance that tuners for different
	 * video standards produce different hashes.
	 */
	for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) {
		if (dev->hash == em28xx_eeprom_hash[i].hash) {
			dev->model = em28xx_eeprom_hash[i].model;
			dev->tuner_type = em28xx_eeprom_hash[i].tuner;

			dev_err(&dev->intf->dev,
				"Your board has no unique USB ID.\n"
				"A hint were successfully done, based on eeprom hash.\n"
				"This method is not 100%% failproof.\n"
				"If the board were misdetected, please email this log to:\n"
				"\tV4L Mailing List  <linux-media@vger.kernel.org>\n"
				"Board detected as %s\n",
			       em28xx_boards[dev->model].name);

			return 0;
		}
	}

	/*
	 * HINT method: I2C attached devices
	 *
	 * This method works for all boards.
	 * Uses a hash of i2c scanned devices.
	 * Devices with the same i2c attached chips will
	 * be considered equal.
	 * This method is less precise than the eeprom one.
	 */

	/* user did not request i2c scanning => do it now */
	if (!dev->i2c_hash)
		em28xx_do_i2c_scan(dev, dev->def_i2c_bus);

	for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) {
		if (dev->i2c_hash == em28xx_i2c_hash[i].hash) {
			dev->model = em28xx_i2c_hash[i].model;
			dev->tuner_type = em28xx_i2c_hash[i].tuner;
			dev_err(&dev->intf->dev,
				"Your board has no unique USB ID.\n"
				"A hint were successfully done, based on i2c devicelist hash.\n"
				"This method is not 100%% failproof.\n"
				"If the board were misdetected, please email this log to:\n"
				"\tV4L Mailing List  <linux-media@vger.kernel.org>\n"
				"Board detected as %s\n",
				em28xx_boards[dev->model].name);

			return 0;
		}
	}

	dev_err(&dev->intf->dev,
		"Your board has no unique USB ID and thus need a hint to be detected.\n"
		"You may try to use card=<n> insmod option to workaround that.\n"
		"Please send an email with this log to:\n"
		"\tV4L Mailing List <linux-media@vger.kernel.org>\n"
		"Board eeprom hash is 0x%08lx\n"
		"Board i2c devicelist hash is 0x%08lx\n",
		dev->hash, dev->i2c_hash);

	dev_err(&dev->intf->dev,
		"Here is a list of valid choices for the card=<n> insmod option:\n");
	for (i = 0; i < em28xx_bcount; i++) {
		dev_err(&dev->intf->dev,
			"    card=%d -> %s\n", i, em28xx_boards[i].name);
	}
	return -1;
}


static inline void em28xx_set_model(struct em28xx *dev)
{
	dev->board = em28xx_boards[dev->model];
	dev->has_msp34xx = dev->board.has_msp34xx;
	dev->is_webcam = dev->board.is_webcam;

	em28xx_set_xclk_i2c_speed(dev);

	/* Should be initialized early, for I2C to work */
	dev->def_i2c_bus = dev->board.def_i2c_bus;
}


void em28xx_free_device(struct kref *ref)
{
	struct em28xx *dev = kref_to_dev(ref);

	dev_info(&dev->intf->dev, "Freeing device\n");

	if (!dev->disconnected)
		em28xx_release_resources(dev);

	if (dev->ts == PRIMARY_TS)
		kfree(dev->alt_max_pkt_size_isoc);

	kfree(dev);
}


static inline void em28xx_set_xclk_i2c_speed(struct em28xx *dev)
{
	const struct em28xx_board *board = &em28xx_boards[dev->model];
	u8 xclk = board->xclk, i2c_speed = board->i2c_speed;

	/*
	 * Those are the default values for the majority of boards
	 * Use those values if not specified otherwise at boards entry
	 */
	if (!xclk)
		xclk = EM28XX_XCLK_IR_RC5_MODE |
		       EM28XX_XCLK_FREQUENCY_12MHZ;

	em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);

	if (!i2c_speed)
		i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
			    EM28XX_I2C_FREQ_100_KHZ;

	dev->i2c_speed = i2c_speed & 0x03;

	if (!dev->board.is_em2800)
		em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, i2c_speed);
	msleep(50);
}