DL_Dxf::~DL_Dxf() {
    if (vertices!=NULL) {
        delete[] vertices;
    }
    if (knots!=NULL) {
        delete[] knots;
    }
    if (controlPoints!=NULL) {
        delete[] controlPoints;
    }
    if (fitPoints!=NULL) {
        delete[] fitPoints;
    }
    if (weights!=NULL) {
        delete[] weights;
    }
    if (leaderVertices!=NULL) {
        delete[] leaderVertices;
    }
}


void DL_Dxf::writeDimOrdinate(DL_WriterA& dw,
                             const DL_DimensionData& data,
                             const DL_DimOrdinateData& edata,
                             const DL_Attributes& attrib) {

    dw.entity("DIMENSION");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbDimension");
    }

    dw.dxfReal(10, data.dpx);
    dw.dxfReal(20, data.dpy);
    dw.dxfReal(30, data.dpz);

    dw.dxfReal(11, data.mpx);
    dw.dxfReal(21, data.mpy);
    dw.dxfReal(31, 0.0);

    int type = data.type;
    if (edata.xtype) {
        type|=0x40;
    }

    dw.dxfInt(70, type);
    if (version>DL_VERSION_R12) {
        dw.dxfInt(71, data.attachmentPoint);
        dw.dxfInt(72, data.lineSpacingStyle); // opt
        //dw.dxfInt(74, data.arrow1Flipped);
        //dw.dxfInt(75, data.arrow2Flipped);
        dw.dxfReal(41, data.lineSpacingFactor); // opt
    }

    dw.dxfString(1, data.text);   // opt
    //dw.dxfString(3, data.style);
    dw.dxfString(3, "Standard");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbOrdinateDimension");
    }

    dw.dxfReal(13, edata.dpx1);
    dw.dxfReal(23, edata.dpy1);
    dw.dxfReal(33, 0.0);

    dw.dxfReal(14, edata.dpx2);
    dw.dxfReal(24, edata.dpy2);
    dw.dxfReal(34, 0.0);
}

check vertexIndex which might be -1 for broken DXF


unsigned long DL_Dxf::writeImage(DL_WriterA& dw,
                       const DL_ImageData& data,
                       const DL_Attributes& attrib) {

    /*if (data.file.empty()) {
        std::cerr << "DL_Dxf::writeImage: "
        << "Image file must not be empty\n";
        return;
}*/

    dw.entity("IMAGE");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbRasterImage");
        dw.dxfInt(90, 0);
    }
    // insertion point
    dw.dxfReal(10, data.ipx);
    dw.dxfReal(20, data.ipy);
    dw.dxfReal(30, data.ipz);

    // vector along bottom side (1 pixel long)
    dw.dxfReal(11, data.ux);
    dw.dxfReal(21, data.uy);
    dw.dxfReal(31, data.uz);

    // vector along left side (1 pixel long)
    dw.dxfReal(12, data.vx);
    dw.dxfReal(22, data.vy);
    dw.dxfReal(32, data.vz);

    // image size in pixel
    dw.dxfReal(13, data.width);
    dw.dxfReal(23, data.height);

    // handle of IMAGEDEF object
    unsigned long handle = dw.handle(340);

    // flags
    dw.dxfInt(70, 15);

    // clipping:
    dw.dxfInt(280, 0);

    // brightness, contrast, fade
    dw.dxfInt(281, data.brightness);
    dw.dxfInt(282, data.contrast);
    dw.dxfInt(283, data.fade);

    return handle;
}


void DL_Dxf::writeDimAligned(DL_WriterA& dw,
                             const DL_DimensionData& data,
                             const DL_DimAlignedData& edata,
                             const DL_Attributes& attrib) {

    dw.entity("DIMENSION");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbDimension");
    }

    dw.dxfReal(10, data.dpx);
    dw.dxfReal(20, data.dpy);
    dw.dxfReal(30, data.dpz);

    dw.dxfReal(11, data.mpx);
    dw.dxfReal(21, data.mpy);
    dw.dxfReal(31, 0.0);

    dw.dxfInt(70, data.type);
    if (version>DL_VERSION_R12) {
        dw.dxfInt(71, data.attachmentPoint);
        dw.dxfInt(72, data.lineSpacingStyle); // opt
        dw.dxfInt(74, data.arrow1Flipped);
        dw.dxfInt(75, data.arrow2Flipped);
        dw.dxfReal(41, data.lineSpacingFactor); // opt
    }

    dw.dxfReal(42, data.angle);

    dw.dxfString(1, data.text);   // opt
    //dw.dxfString(3, data.style);
    dw.dxfString(3, "Standard");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbAlignedDimension");
    }

    dw.dxfReal(13, edata.epx1);
    dw.dxfReal(23, edata.epy1);
    dw.dxfReal(33, 0.0);

    dw.dxfReal(14, edata.epx2);
    dw.dxfReal(24, edata.epy2);
    dw.dxfReal(34, 0.0);

    writeDimStyleOverrides(dw, data);
}


void DL_Dxf::writeLine(DL_WriterA& dw,
                       const DL_LineData& data,
                       const DL_Attributes& attrib) {
    dw.entity("LINE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbLine");
    }
    dw.coord(DL_LINE_START_CODE, data.x1, data.y1, data.z1);
    dw.coord(DL_LINE_END_CODE, data.x2, data.y2, data.z2);
}


void DL_Dxf::test() {
    char* buf1;
    char* buf2;
    char* buf3;
    char* buf4;
    char* buf5;
    char* buf6;

    buf1 = new char[10];
    buf2 = new char[10];
    buf3 = new char[10];
    buf4 = new char[10];
    buf5 = new char[10];
    buf6 = new char[10];

    strcpy(buf1, "  10\n");
    strcpy(buf2, "10");
    strcpy(buf3, "10\n");
    strcpy(buf4, "  10 \n");
    strcpy(buf5, "  10 \r");
    strcpy(buf6, "\t10 \n");

    std::cout << "1 buf1: '" << buf1 << "'\n";
    stripWhiteSpace(&buf1);
    std::cout << "2 buf1: '" << buf1 << "'\n";
    //assert(!strcmp(buf1, "10"));

    std::cout << "1 buf2: '" << buf2 << "'\n";
    stripWhiteSpace(&buf2);
    std::cout << "2 buf2: '" << buf2 << "'\n";

    std::cout << "1 buf3: '" << buf3 << "'\n";
    stripWhiteSpace(&buf3);
    std::cout << "2 buf3: '" << buf3 << "'\n";

    std::cout << "1 buf4: '" << buf4 << "'\n";
    stripWhiteSpace(&buf4);
    std::cout << "2 buf4: '" << buf4 << "'\n";

    std::cout << "1 buf5: '" << buf5 << "'\n";
    stripWhiteSpace(&buf5);
    std::cout << "2 buf5: '" << buf5 << "'\n";

    std::cout << "1 buf6: '" << buf6 << "'\n";
    stripWhiteSpace(&buf6);
    std::cout << "2 buf6: '" << buf6 << "'\n";

}


void DL_Dxf::addTrace(DL_CreationInterface* creationInterface) {
    DL_TraceData td;

    for (int k = 0; k < 4; k++) {
       td.x[k] = getRealValue(10 + k, 0.0);
       td.y[k] = getRealValue(20 + k, 0.0);
       td.z[k] = getRealValue(30 + k, 0.0);
    }
    creationInterface->addTrace(td);
}


void DL_Dxf::writeHatchLoop1(DL_WriterA& dw,
                             const DL_HatchLoopData& data) {

    dw.dxfInt(92, 1);
    dw.dxfInt(93, data.numEdges);
    //dw.dxfInt(97, 0);
}


void DL_Dxf::writeObjects(DL_WriterA& dw, const std::string& appDictionaryName) {
    dw.dxfString(  0, "SECTION");
    dw.dxfString(  2, "OBJECTS");

    dw.dxfString(  0, "DICTIONARY");
    dw.dxfHex(5, 0xC);
    dw.dxfString(100, "AcDbDictionary");
    dw.dxfInt(280, 0);
    dw.dxfInt(281, 1);
    dw.dxfString(  3, "ACAD_GROUP");
    dw.dxfHex(350, 0xD);
    dw.dxfString(  3, "ACAD_LAYOUT");
    dw.dxfHex(350, 0x1A);
    dw.dxfString(  3, "ACAD_MLINESTYLE");
    dw.dxfHex(350, 0x17);
    dw.dxfString(  3, "ACAD_PLOTSETTINGS");
    dw.dxfHex(350, 0x19);
    dw.dxfString(  3, "ACAD_PLOTSTYLENAME");
    dw.dxfHex(350, 0xE);
    dw.dxfString(  3, "AcDbVariableDictionary");
    int acDbVariableDictionaryHandle = dw.handle(350);
    //int acDbVariableDictionaryHandle = dw.getNextHandle();
    //dw.dxfHex(350, acDbVariableDictionaryHandle);
    //dw.incHandle();

    if (appDictionaryName.length()!=0) {
        dw.dxfString(  3, appDictionaryName);
        appDictionaryHandle = dw.handle(350);
        //appDictionaryHandle = dw.getNextHandle();
        //dw.dxfHex(350, appDictionaryHandle);
        //dw.incHandle();
    }

    dw.dxfString(  0, "DICTIONARY");
    dw.dxfHex(5, 0xD);
    //dw.handle();                                    // D
    //dw.dxfHex(330, 0xC);
    dw.dxfString(100, "AcDbDictionary");
    dw.dxfInt(280, 0);
    dw.dxfInt(281, 1);

    dw.dxfString(  0, "ACDBDICTIONARYWDFLT");
    dw.dxfHex(5, 0xE);
    //dicId4 = dw.handle();                           // E
    //dw.dxfHex(330, 0xC);                       // C
    dw.dxfString(100, "AcDbDictionary");
    dw.dxfInt(281, 1);
    dw.dxfString(  3, "Normal");
    dw.dxfHex(350, 0xF);
    //dw.dxfHex(350, dw.getNextHandle()+5);        // F
    dw.dxfString(100, "AcDbDictionaryWithDefault");
    dw.dxfHex(340, 0xF);
    //dw.dxfHex(340, dw.getNextHandle()+5);        // F

    dw.dxfString(  0, "ACDBPLACEHOLDER");
    dw.dxfHex(5, 0xF);
    //dw.handle();                                    // F
    //dw.dxfHex(330, dicId4);                      // E

    dw.dxfString(  0, "DICTIONARY");
    //dicId3 = dw.handle();                           // 17
    dw.dxfHex(5, 0x17);
    //dw.dxfHex(330, 0xC);                       // C
    dw.dxfString(100, "AcDbDictionary");
    dw.dxfInt(280, 0);
    dw.dxfInt(281, 1);
    dw.dxfString(  3, "Standard");
    dw.dxfHex(350, 0x18);
    //dw.dxfHex(350, dw.getNextHandle()+5);        // 18

    dw.dxfString(  0, "MLINESTYLE");
    dw.dxfHex(5, 0x18);
    //dw.handle();                                    // 18
    //dw.dxfHex(330, dicId3);                      // 17
    dw.dxfString(100, "AcDbMlineStyle");
    dw.dxfString(  2, "STANDARD");
    dw.dxfInt( 70, 0);
    dw.dxfString(  3, "");
    dw.dxfInt( 62, 256);
    dw.dxfReal( 51, 90.0);
    dw.dxfReal( 52, 90.0);
    dw.dxfInt( 71, 2);
    dw.dxfReal( 49, 0.5);
    dw.dxfInt( 62, 256);
    dw.dxfString(  6, "BYLAYER");
    dw.dxfReal( 49, -0.5);
    dw.dxfInt( 62, 256);
    dw.dxfString(  6, "BYLAYER");

    dw.dxfString(  0, "DICTIONARY");
    dw.dxfHex(5, 0x19);
    //dw.handle();                           // 17
    //dw.dxfHex(330, 0xC);                       // C
    dw.dxfString(100, "AcDbDictionary");
    dw.dxfInt(280, 0);
    dw.dxfInt(281, 1);

    dw.dxfString(  0, "DICTIONARY");
    //dicId2 = dw.handle();                           // 1A
    dw.dxfHex(5, 0x1A);
    //dw.dxfHex(330, 0xC);
    dw.dxfString(100, "AcDbDictionary");
    dw.dxfInt(281, 1);
    dw.dxfString(  3, "Layout1");
    dw.dxfHex(350, 0x1E);
    //dw.dxfHex(350, dw.getNextHandle()+2);        // 1E
    dw.dxfString(  3, "Layout2");
    dw.dxfHex(350, 0x26);
    //dw.dxfHex(350, dw.getNextHandle()+4);        // 26
    dw.dxfString(  3, "Model");
    dw.dxfHex(350, 0x22);
    //dw.dxfHex(350, dw.getNextHandle()+5);        // 22

    dw.dxfString(  0, "LAYOUT");
    dw.dxfHex(5, 0x1E);
    //dw.handle();                                    // 1E
    //dw.dxfHex(330, dicId2);                      // 1A
    dw.dxfString(100, "AcDbPlotSettings");
    dw.dxfString(  1, "");
    dw.dxfString(  2, "none_device");
    dw.dxfString(  4, "");
    dw.dxfString(  6, "");
    dw.dxfReal( 40, 0.0);
    dw.dxfReal( 41, 0.0);
    dw.dxfReal( 42, 0.0);
    dw.dxfReal( 43, 0.0);
    dw.dxfReal( 44, 0.0);
    dw.dxfReal( 45, 0.0);
    dw.dxfReal( 46, 0.0);
    dw.dxfReal( 47, 0.0);
    dw.dxfReal( 48, 0.0);
    dw.dxfReal( 49, 0.0);
    dw.dxfReal(140, 0.0);
    dw.dxfReal(141, 0.0);
    dw.dxfReal(142, 1.0);
    dw.dxfReal(143, 1.0);
    dw.dxfInt( 70, 688);
    dw.dxfInt( 72, 0);
    dw.dxfInt( 73, 0);
    dw.dxfInt( 74, 5);
    dw.dxfString(  7, "");
    dw.dxfInt( 75, 16);
    dw.dxfReal(147, 1.0);
    dw.dxfReal(148, 0.0);
    dw.dxfReal(149, 0.0);
    dw.dxfString(100, "AcDbLayout");
    dw.dxfString(  1, "Layout1");
    dw.dxfInt( 70, 1);
    dw.dxfInt( 71, 1);
    dw.dxfReal( 10, 0.0);
    dw.dxfReal( 20, 0.0);
    dw.dxfReal( 11, 420.0);
    dw.dxfReal( 21, 297.0);
    dw.dxfReal( 12, 0.0);
    dw.dxfReal( 22, 0.0);
    dw.dxfReal( 32, 0.0);
    dw.dxfReal( 14, 1.000000000000000E+20);
    dw.dxfReal( 24, 1.000000000000000E+20);
    dw.dxfReal( 34, 1.000000000000000E+20);
    dw.dxfReal( 15, -1.000000000000000E+20);
    dw.dxfReal( 25, -1.000000000000000E+20);
    dw.dxfReal( 35, -1.000000000000000E+20);
    dw.dxfReal(146, 0.0);
    dw.dxfReal( 13, 0.0);
    dw.dxfReal( 23, 0.0);
    dw.dxfReal( 33, 0.0);
    dw.dxfReal( 16, 1.0);
    dw.dxfReal( 26, 0.0);
    dw.dxfReal( 36, 0.0);
    dw.dxfReal( 17, 0.0);
    dw.dxfReal( 27, 1.0);
    dw.dxfReal( 37, 0.0);
    dw.dxfInt( 76, 0);
    //dw.dxfHex(330, dw.getPaperSpaceHandle());    // 1B
    dw.dxfHex(330, 0x1B);

    dw.dxfString(  0, "LAYOUT");
    dw.dxfHex(5, 0x22);
    //dw.handle();                                    // 22
    //dw.dxfHex(330, dicId2);                      // 1A
    dw.dxfString(100, "AcDbPlotSettings");
    dw.dxfString(  1, "");
    dw.dxfString(  2, "none_device");
    dw.dxfString(  4, "");
    dw.dxfString(  6, "");
    dw.dxfReal( 40, 0.0);
    dw.dxfReal( 41, 0.0);
    dw.dxfReal( 42, 0.0);
    dw.dxfReal( 43, 0.0);
    dw.dxfReal( 44, 0.0);
    dw.dxfReal( 45, 0.0);
    dw.dxfReal( 46, 0.0);
    dw.dxfReal( 47, 0.0);
    dw.dxfReal( 48, 0.0);
    dw.dxfReal( 49, 0.0);
    dw.dxfReal(140, 0.0);
    dw.dxfReal(141, 0.0);
    dw.dxfReal(142, 1.0);
    dw.dxfReal(143, 1.0);
    dw.dxfInt( 70, 1712);
    dw.dxfInt( 72, 0);
    dw.dxfInt( 73, 0);
    dw.dxfInt( 74, 0);
    dw.dxfString(  7, "");
    dw.dxfInt( 75, 0);
    dw.dxfReal(147, 1.0);
    dw.dxfReal(148, 0.0);
    dw.dxfReal(149, 0.0);
    dw.dxfString(100, "AcDbLayout");
    dw.dxfString(  1, "Model");
    dw.dxfInt( 70, 1);
    dw.dxfInt( 71, 0);
    dw.dxfReal( 10, 0.0);
    dw.dxfReal( 20, 0.0);
    dw.dxfReal( 11, 12.0);
    dw.dxfReal( 21, 9.0);
    dw.dxfReal( 12, 0.0);
    dw.dxfReal( 22, 0.0);
    dw.dxfReal( 32, 0.0);
    dw.dxfReal( 14, 0.0);
    dw.dxfReal( 24, 0.0);
    dw.dxfReal( 34, 0.0);
    dw.dxfReal( 15, 0.0);
    dw.dxfReal( 25, 0.0);
    dw.dxfReal( 35, 0.0);
    dw.dxfReal(146, 0.0);
    dw.dxfReal( 13, 0.0);
    dw.dxfReal( 23, 0.0);
    dw.dxfReal( 33, 0.0);
    dw.dxfReal( 16, 1.0);
    dw.dxfReal( 26, 0.0);
    dw.dxfReal( 36, 0.0);
    dw.dxfReal( 17, 0.0);
    dw.dxfReal( 27, 1.0);
    dw.dxfReal( 37, 0.0);
    dw.dxfInt( 76, 0);
    //dw.dxfHex(330, dw.getModelSpaceHandle());    // 1F
    dw.dxfHex(330, 0x1F);

    dw.dxfString(  0, "LAYOUT");
    //dw.handle();                                    // 26
    dw.dxfHex(5, 0x26);
    //dw.dxfHex(330, dicId2);                      // 1A
    dw.dxfString(100, "AcDbPlotSettings");
    dw.dxfString(  1, "");
    dw.dxfString(  2, "none_device");
    dw.dxfString(  4, "");
    dw.dxfString(  6, "");
    dw.dxfReal( 40, 0.0);
    dw.dxfReal( 41, 0.0);
    dw.dxfReal( 42, 0.0);
    dw.dxfReal( 43, 0.0);
    dw.dxfReal( 44, 0.0);
    dw.dxfReal( 45, 0.0);
    dw.dxfReal( 46, 0.0);
    dw.dxfReal( 47, 0.0);
    dw.dxfReal( 48, 0.0);
    dw.dxfReal( 49, 0.0);
    dw.dxfReal(140, 0.0);
    dw.dxfReal(141, 0.0);
    dw.dxfReal(142, 1.0);
    dw.dxfReal(143, 1.0);
    dw.dxfInt( 70, 688);
    dw.dxfInt( 72, 0);
    dw.dxfInt( 73, 0);
    dw.dxfInt( 74, 5);
    dw.dxfString(  7, "");
    dw.dxfInt( 75, 16);
    dw.dxfReal(147, 1.0);
    dw.dxfReal(148, 0.0);
    dw.dxfReal(149, 0.0);
    dw.dxfString(100, "AcDbLayout");
    dw.dxfString(  1, "Layout2");
    dw.dxfInt( 70, 1);
    dw.dxfInt( 71, 2);
    dw.dxfReal( 10, 0.0);
    dw.dxfReal( 20, 0.0);
    dw.dxfReal( 11, 12.0);
    dw.dxfReal( 21, 9.0);
    dw.dxfReal( 12, 0.0);
    dw.dxfReal( 22, 0.0);
    dw.dxfReal( 32, 0.0);
    dw.dxfReal( 14, 0.0);
    dw.dxfReal( 24, 0.0);
    dw.dxfReal( 34, 0.0);
    dw.dxfReal( 15, 0.0);
    dw.dxfReal( 25, 0.0);
    dw.dxfReal( 35, 0.0);
    dw.dxfReal(146, 0.0);
    dw.dxfReal( 13, 0.0);
    dw.dxfReal( 23, 0.0);
    dw.dxfReal( 33, 0.0);
    dw.dxfReal( 16, 1.0);
    dw.dxfReal( 26, 0.0);
    dw.dxfReal( 36, 0.0);
    dw.dxfReal( 17, 0.0);
    dw.dxfReal( 27, 1.0);
    dw.dxfReal( 37, 0.0);
    dw.dxfInt( 76, 0);
    //dw.dxfHex(330, dw.getPaperSpace0Handle());   // 23
    dw.dxfHex(330, 0x23);

    dw.dxfString(  0, "DICTIONARY");
    //dw.dxfHex(5, 0x2C);
    //dicId5 =
    dw.dxfHex(5, acDbVariableDictionaryHandle);
    //dw.handle();                           // 2C
    //dw.dxfHex(330, 0xC);                       // C
    dw.dxfString(100, "AcDbDictionary");
    dw.dxfInt(281, 1);
    dw.dxfString(  3, "DIMASSOC");
    //dw.dxfHex(350, 0x2F);
    dw.dxfHex(350, dw.getNextHandle()+1);        // 2E
    dw.dxfString(  3, "HIDETEXT");
    //dw.dxfHex(350, 0x2E);
    dw.dxfHex(350, dw.getNextHandle());        // 2D

    dw.dxfString(  0, "DICTIONARYVAR");
    //dw.dxfHex(5, 0x2E);
    dw.handle();                                    // 2E
    //dw.dxfHex(330, dicId5);                      // 2C
    dw.dxfString(100, "DictionaryVariables");
    dw.dxfInt(280, 0);
    dw.dxfInt(  1, 2);

    dw.dxfString(  0, "DICTIONARYVAR");
    //dw.dxfHex(5, 0x2D);
    dw.handle();                                    // 2D
    //dw.dxfHex(330, dicId5);                      // 2C
    dw.dxfString(100, "DictionaryVariables");
    dw.dxfInt(280, 0);
    dw.dxfInt(  1, 1);
}


void DL_Dxf::addLinetype(DL_CreationInterface* creationInterface) {
    std::string name = getStringValue(2, "");
    if (name.length()==0) {
        return;
    }
    int numDashes = getIntValue(73, 0);
    //double dashes[numDashes];

    DL_LinetypeData d(
        // name:
        name,
        // description:
        getStringValue(3, ""),
        // flags
        getIntValue(70, 0),
        // number of dashes:
        numDashes,
        // pattern length:
        getRealValue(40, 0.0)
        // pattern:
        //dashes
    );

    if (name != "By Layer" && name != "By Block" && name != "BYLAYER" && name != "BYBLOCK") {
        creationInterface->addLinetype(d);
    }
}


void DL_Dxf::writeControlPoint(DL_WriterA& dw,
                               const DL_ControlPointData& data) {

    dw.dxfReal(10, data.x);
    dw.dxfReal(20, data.y);
    dw.dxfReal(30, data.z);
}


void DL_Dxf::addText(DL_CreationInterface* creationInterface) {
    DL_TextData d(
        // insertion point
        getRealValue(10, 0.0),
        getRealValue(20, 0.0),
        getRealValue(30, 0.0),
        // alignment point
        getRealValue(11, DL_NANDOUBLE),
        getRealValue(21, DL_NANDOUBLE),
        getRealValue(31, DL_NANDOUBLE),
        // height
        getRealValue(40, 2.5),
        // x scale
        getRealValue(41, 1.0),
        // generation flags
        getIntValue(71, 0),
        // h just
        getIntValue(72, 0),
        // v just
        getIntValue(73, 0),
        // text
        getStringValue(1, ""),
        // style
        getStringValue(7, ""),
        // angle
        (getRealValue(50, 0.0)*2*M_PI)/360.0);

    creationInterface->addText(d);
}


void DL_Dxf::writeHatch2(DL_WriterA& dw,
                         const DL_HatchData& data,
                         const DL_Attributes& /*attrib*/) {

    dw.dxfInt(75, 0);                // odd parity
    dw.dxfInt(76, 1);                // pattern type
    if (data.solid==false) {
        dw.dxfReal(52, data.angle);
        dw.dxfReal(41, data.scale);
        dw.dxfInt(77, 0);            // not double
        //dw.dxfInt(78, 0);
        dw.dxfInt(78, 1);
        dw.dxfReal(53, 45.0);
        dw.dxfReal(43, 0.0);
        dw.dxfReal(44, 0.0);
        dw.dxfReal(45, -0.0883883476483184);
        dw.dxfReal(46, 0.0883883476483185);
        dw.dxfInt(79, 0);
    }
    dw.dxfInt(98, 0);

    if (version==DL_VERSION_2000) {
        dw.dxfString(1001, "ACAD");
        dw.dxfReal(1010, data.originX);
        dw.dxfReal(1020, data.originY);
        dw.dxfInt(1030, 0.0);
    }
}


void DL_Dxf::addEllipse(DL_CreationInterface* creationInterface) {
    DL_EllipseData d(getRealValue(10, 0.0),
                     getRealValue(20, 0.0),
                     getRealValue(30, 0.0),
                     getRealValue(11, 0.0),
                     getRealValue(21, 0.0),
                     getRealValue(31, 0.0),
                     getRealValue(40, 1.0),
                     getRealValue(41, 0.0),
                     getRealValue(42, 2*M_PI));

    creationInterface->addEllipse(d);
}


bool DL_Dxf::handleHatchData(DL_CreationInterface* creationInterface) {
    // New polyline loop, group code 92
    // or new loop with individual edges, group code 93
    if (groupCode==92 || groupCode==93) {
        if (firstHatchLoop) {
            hatchEdges.clear();
            firstHatchLoop = false;
        }
        if (groupCode==92 && (toInt(groupValue)&2)==2) {
            addHatchLoop();
        }
        if (groupCode==93) {
            addHatchLoop();
        }
        return true;
    }

    // New hatch edge or new section / entity: add last hatch edge:
    if (groupCode==72 || groupCode==0 || groupCode==78 || groupCode==98) {
        // polyline boundaries use code 72 for bulge flag:
        if (groupCode!=72 || (getIntValue(92, 0)&2)==0) {
            addHatchEdge();
        }

        if (groupCode==0 /*|| groupCode==78*/) {
            addHatch(creationInterface);
        }
        else {
            hatchEdge.type = toInt(groupValue);
        }
        return true;
    }

    // polyline boundary:
    if ((getIntValue(92, 0)&2)==2) {
        switch (groupCode) {
        case 10:
            hatchEdge.type = 0;
            hatchEdge.vertices.push_back(std::vector<double>());
            hatchEdge.vertices.back().push_back(toReal(groupValue));
            return true;
        case 20:
            if (!hatchEdge.vertices.empty()) {
                hatchEdge.vertices.back().push_back(toReal(groupValue));
                hatchEdge.defined = true;
            }
            return true;
        case 42:
            if (!hatchEdge.vertices.empty()) {
                hatchEdge.vertices.back().push_back(toReal(groupValue));
                hatchEdge.defined = true;
            }
            return true;
        }
    }
    else {
        // Line edge:
        if (hatchEdge.type==1) {
            switch (groupCode) {
            case 10:
                hatchEdge.x1 = toReal(groupValue);
                return true;
            case 20:
                hatchEdge.y1 = toReal(groupValue);
                return true;
            case 11:
                hatchEdge.x2 = toReal(groupValue);
                return true;
            case 21:
                hatchEdge.y2 = toReal(groupValue);
                hatchEdge.defined = true;
                return true;
            }
        }

        // Arc edge:
        if (hatchEdge.type==2) {
            switch(groupCode) {
            case 10:
                hatchEdge.cx = toReal(groupValue);
                return true;
            case 20:
                hatchEdge.cy = toReal(groupValue);
                return true;
            case 40:
                hatchEdge.radius = toReal(groupValue);
                return true;
            case 50:
                hatchEdge.angle1 = toReal(groupValue)/360.0*2*M_PI;
                return true;
            case 51:
                hatchEdge.angle2 = toReal(groupValue)/360.0*2*M_PI;
                return true;
            case 73:
                hatchEdge.ccw = (bool)toInt(groupValue);
                hatchEdge.defined = true;
                return true;
            }
        }

        // Ellipse arc edge:
        if (hatchEdge.type==3) {
            switch (groupCode) {
            case 10:
                hatchEdge.cx = toReal(groupValue);
                return true;
            case 20:
                hatchEdge.cy = toReal(groupValue);
                return true;
            case 11:
                hatchEdge.mx = toReal(groupValue);
                return true;
            case 21:
                hatchEdge.my = toReal(groupValue);
                return true;
            case 40:
                hatchEdge.ratio = toReal(groupValue);
                return true;
            case 50:
                hatchEdge.angle1 = toReal(groupValue)/360.0*2*M_PI;
                return true;
            case 51:
                hatchEdge.angle2 = toReal(groupValue)/360.0*2*M_PI;
                return true;
            case 73:
                hatchEdge.ccw = (bool)toInt(groupValue);
                hatchEdge.defined = true;
                return true;
            }
        }

        // Spline edge:
        if (hatchEdge.type==4) {
            switch (groupCode) {
            case 94:
                hatchEdge.degree = toInt(groupValue);
                return true;
            case 73:
                hatchEdge.rational = toBool(groupValue);
                return true;
            case 74:
                hatchEdge.periodic = toBool(groupValue);
                return true;
            case 95:
                hatchEdge.nKnots = toInt(groupValue);
                return true;
            case 96:
                hatchEdge.nControl = toInt(groupValue);
                return true;
            case 97:
                hatchEdge.nFit = toInt(groupValue);
                return true;
            case 40:
                if (hatchEdge.knots.size() < hatchEdge.nKnots) {
                    hatchEdge.knots.push_back(toReal(groupValue));
                }
                return true;
            case 10:
                if (hatchEdge.controlPoints.size() < hatchEdge.nControl) {
                    std::vector<double> v;
                    v.push_back(toReal(groupValue));
                    hatchEdge.controlPoints.push_back(v);
                }
                return true;
            case 20:
                if (!hatchEdge.controlPoints.empty() && hatchEdge.controlPoints.back().size()==1) {
                    hatchEdge.controlPoints.back().push_back(toReal(groupValue));
                }
                hatchEdge.defined = true;
                return true;
            case 42:
                if (hatchEdge.weights.size() < hatchEdge.nControl) {
                    hatchEdge.weights.push_back(toReal(groupValue));
                }
                return true;
            case 11:
                if (hatchEdge.fitPoints.size() < hatchEdge.nFit) {
                    std::vector<double> v;
                    v.push_back(toReal(groupValue));
                    hatchEdge.fitPoints.push_back(v);
                }
                return true;
            case 21:
                if (!hatchEdge.fitPoints.empty() && hatchEdge.fitPoints.back().size()==1) {
                    hatchEdge.fitPoints.back().push_back(toReal(groupValue));
                }
                hatchEdge.defined = true;
                return true;
            case 12:
                hatchEdge.startTangentX = toReal(groupValue);
                return true;
            case 22:
                hatchEdge.startTangentY = toReal(groupValue);
                return true;
            case 13:
                hatchEdge.endTangentX = toReal(groupValue);
                return true;
            case 23:
                hatchEdge.endTangentY = toReal(groupValue);
                return true;
            }
        }
    }

    return false;
}


void DL_Dxf::addDimAligned(DL_CreationInterface* creationInterface) {
    DL_DimensionData d = getDimData();

    // aligned dimension:
    DL_DimAlignedData da(
        // extension point 1
        getRealValue(13, 0.0),
        getRealValue(23, 0.0),
        getRealValue(33, 0.0),
        // extension point 2
        getRealValue(14, 0.0),
        getRealValue(24, 0.0),
        getRealValue(34, 0.0));
    creationInterface->addDimAlign(d, da);
}


void DL_Dxf::writeMText(DL_WriterA& dw,
                        const DL_MTextData& data,
                        const DL_Attributes& attrib) {

    dw.entity("MTEXT");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbMText");
    }
    dw.dxfReal(10, data.ipx);
    dw.dxfReal(20, data.ipy);
    dw.dxfReal(30, data.ipz);
    dw.dxfReal(40, data.height);
    dw.dxfReal(41, data.width);

    dw.dxfInt(71, data.attachmentPoint);
    dw.dxfInt(72, data.drawingDirection);

    // Creare text chunks of 250 characters each:
    int length = data.text.length();
    char chunk[251];
    int i;
    for (i=250; i<length; i+=250) {
        strncpy(chunk, &data.text.c_str()[i-250], 250);
        chunk[250]='\0';
        dw.dxfString(3, chunk);
    }
    strncpy(chunk, &data.text.c_str()[i-250], 250);
    chunk[250]='\0';
    dw.dxfString(1, chunk);

    dw.dxfString(7, data.style);

    // since dxflib 2.0.2.1: degrees not rad (error in autodesk dxf doc)
    dw.dxfReal(50, data.angle/(2.0*M_PI)*360.0);

    dw.dxfInt(73, data.lineSpacingStyle);
    dw.dxfReal(44, data.lineSpacingFactor);
}


void DL_Dxf::addBlock(DL_CreationInterface* creationInterface) {
    std::string name = getStringValue(2, "");
    if (name.length()==0) {
        return;
    }

    DL_BlockData d(
        // Name:
        name,
        // flags:
        getIntValue(70, 0),
        // base point:
        getRealValue(10, 0.0),
        getRealValue(20, 0.0),
        getRealValue(30, 0.0));

    creationInterface->addBlock(d);
}


bool DL_Dxf::handleXRecordData(DL_CreationInterface* creationInterface) {
    if (groupCode==105) {
        return false;
    }

    if (groupCode==5) {
        creationInterface->addXRecord(groupValue);
        return true;
    }

    if (groupCode==280) {
        xRecordValues = true;
        return true;
    }

    if (!xRecordValues) {
        return false;
    }

    // string:
    if (groupCode<=9 ||
        groupCode==100 || groupCode==102 || groupCode==105 ||
        (groupCode>=300 && groupCode<=369) ||
        (groupCode>=1000 && groupCode<=1009)) {

        creationInterface->addXRecordString(groupCode, groupValue);
        return true;
    }

    // int:
    else if ((groupCode>=60 && groupCode<=99) || (groupCode>=160 && groupCode<=179) || (groupCode>=270 && groupCode<=289)) {
        creationInterface->addXRecordInt(groupCode, toInt(groupValue));
        return true;
    }

    // bool:
    else if (groupCode>=290 && groupCode<=299) {
        creationInterface->addXRecordBool(groupCode, toBool(groupValue));
        return true;
    }

    // double:
    else if ((groupCode>=10 && groupCode<=59) || (groupCode>=110 && groupCode<=149) || (groupCode>=210 && groupCode<=239)) {
        creationInterface->addXRecordReal(groupCode, toReal(groupValue));
        return true;
    }

    return false;
}


void DL_Dxf::addComment(DL_CreationInterface* creationInterface, const std::string& comment) {
    creationInterface->addComment(comment);
}


int udf_expand_file_adinicb(struct inode *inode)
{
	struct page *page;
	char *kaddr;
	struct udf_inode_info *iinfo = UDF_I(inode);
	int err;

	WARN_ON_ONCE(!inode_is_locked(inode));
	if (!iinfo->i_lenAlloc) {
		if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
			iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
		else
			iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
		/* from now on we have normal address_space methods */
		inode->i_data.a_ops = &udf_aops;
		up_write(&iinfo->i_data_sem);
		mark_inode_dirty(inode);
		return 0;
	}
	/*
	 * Release i_data_sem so that we can lock a page - page lock ranks
	 * above i_data_sem. i_mutex still protects us against file changes.
	 */
	up_write(&iinfo->i_data_sem);

	page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);
	if (!page)
		return -ENOMEM;

	if (!PageUptodate(page)) {
		kaddr = kmap_atomic(page);
		memset(kaddr + iinfo->i_lenAlloc, 0x00,
		       PAGE_SIZE - iinfo->i_lenAlloc);
		memcpy(kaddr, iinfo->i_data + iinfo->i_lenEAttr,
			iinfo->i_lenAlloc);
		flush_dcache_page(page);
		SetPageUptodate(page);
		kunmap_atomic(kaddr);
	}
	down_write(&iinfo->i_data_sem);
	memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00,
	       iinfo->i_lenAlloc);
	iinfo->i_lenAlloc = 0;
	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
	else
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
	/* from now on we have normal address_space methods */
	inode->i_data.a_ops = &udf_aops;
	set_page_dirty(page);
	unlock_page(page);
	up_write(&iinfo->i_data_sem);
	err = filemap_fdatawrite(inode->i_mapping);
	if (err) {
		/* Restore everything back so that we don't lose data... */
		lock_page(page);
		down_write(&iinfo->i_data_sem);
		kaddr = kmap_atomic(page);
		memcpy(iinfo->i_data + iinfo->i_lenEAttr, kaddr, inode->i_size);
		kunmap_atomic(kaddr);
		unlock_page(page);
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
		inode->i_data.a_ops = &udf_adinicb_aops;
		iinfo->i_lenAlloc = inode->i_size;
		up_write(&iinfo->i_data_sem);
	}
	put_page(page);
	mark_inode_dirty(inode);

	return err;
}

udf: Restore i_lenAlloc when inode expansion fails

When we fail to expand inode from inline format to a normal format, we
restore inode to contain the original inline formatting but we forgot to
set i_lenAlloc back. The mismatch between i_lenAlloc and i_size was then
causing further problems such as warnings and lost data down the line.

Reported-by: butt3rflyh4ck <butterflyhuangxx@gmail.com>
CC: stable@vger.kernel.org
Fixes: 7e49b6f2480c ("udf: Convert UDF to new truncate calling sequence")
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jan Kara <jack@suse.cz>


static MagickBooleanType SetGrayscaleImage(Image *image,
  ExceptionInfo *exception)
{
  CacheView
    *image_view;

  MagickBooleanType
    status;

  PixelInfo
    *colormap;

  register ssize_t
    i;

  ssize_t
    *colormap_index,
    j,
    y;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->type != GrayscaleType)
    (void) TransformImageColorspace(image,GRAYColorspace,exception);
  if (image->storage_class == PseudoClass)
    colormap_index=(ssize_t *) AcquireQuantumMemory(MagickMax(image->colors+1,
      MaxMap),sizeof(*colormap_index));
  else
    colormap_index=(ssize_t *) AcquireQuantumMemory(MagickMax(MaxColormapSize+1,
      MaxMap),sizeof(*colormap_index));
  if (colormap_index == (ssize_t *) NULL)
    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
      image->filename);
  if (image->storage_class != PseudoClass)
    {
      (void) memset(colormap_index,(-1),MaxColormapSize*
        sizeof(*colormap_index));
      if (AcquireImageColormap(image,MaxColormapSize,exception) == MagickFalse)
        {
          colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
            image->filename);
        }
      image->colors=0;
      status=MagickTrue;
      image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
      #pragma omp parallel for schedule(static) shared(status) \
        magick_number_threads(image,image,image->rows,1)
#endif
      for (y=0; y < (ssize_t) image->rows; y++)
      {
        register Quantum
          *magick_restrict q;

        register ssize_t
          x;

        if (status == MagickFalse)
          continue;
        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
          exception);
        if (q == (Quantum *) NULL)
          {
            status=MagickFalse;
            continue;
          }
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          register size_t
            intensity;

          intensity=ScaleQuantumToMap(GetPixelRed(image,q));
          if (colormap_index[intensity] < 0)
            {
#if defined(MAGICKCORE_OPENMP_SUPPORT)
              #pragma omp critical (MagickCore_SetGrayscaleImage)
#endif
              if (colormap_index[intensity] < 0)
                {
                  colormap_index[intensity]=(ssize_t) image->colors;
                  image->colormap[image->colors].red=(double)
                    GetPixelRed(image,q);
                  image->colormap[image->colors].green=(double)
                    GetPixelGreen(image,q);
                  image->colormap[image->colors].blue=(double)
                    GetPixelBlue(image,q);
                  image->colors++;
               }
            }
          SetPixelIndex(image,(Quantum) colormap_index[intensity],q);
          q+=GetPixelChannels(image);
        }
        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
          status=MagickFalse;
      }
      image_view=DestroyCacheView(image_view);
    }
  for (i=0; i < (ssize_t) image->colors; i++)
    image->colormap[i].alpha=(double) i;
  qsort((void *) image->colormap,image->colors,sizeof(PixelInfo),
    IntensityCompare);
  colormap=(PixelInfo *) AcquireQuantumMemory(image->colors,sizeof(*colormap));
  if (colormap == (PixelInfo *) NULL)
    {
      colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
        image->filename);
    }
  j=0;
  colormap[j]=image->colormap[0];
  for (i=0; i < (ssize_t) image->colors; i++)
  {
    if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse)
      {
        j++;
        colormap[j]=image->colormap[i];
      }
    colormap_index[(ssize_t) image->colormap[i].alpha]=j;
  }
  image->colors=(size_t) (j+1);
  image->colormap=(PixelInfo *) RelinquishMagickMemory(image->colormap);
  image->colormap=colormap;
  status=MagickTrue;
  image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
  #pragma omp parallel for schedule(static) shared(status) \
    magick_number_threads(image,image,image->rows,1)
#endif
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    register Quantum
      *magick_restrict q;

    register ssize_t
      x;

    if (status == MagickFalse)
      continue;
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      {
        status=MagickFalse;
        continue;
      }
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap(
        GetPixelIndex(image,q))],q);
      q+=GetPixelChannels(image);
    }
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
      status=MagickFalse;
  }
  image_view=DestroyCacheView(image_view);
  colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
  image->type=GrayscaleType;
  if (SetImageMonochrome(image,exception) != MagickFalse)
    image->type=BilevelType;
  return(status);
}


static MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info,
  ExceptionInfo *exception)
{
#define AssignImageTag  "Assign/Image"

  ColorspaceType
    colorspace;

  ssize_t
    y;

  /*
    Allocate image colormap.
  */
  colorspace=image->colorspace;
  if (cube_info->quantize_info->colorspace != UndefinedColorspace)
    (void) TransformImageColorspace(image,cube_info->quantize_info->colorspace,
      exception);
  if (AcquireImageColormap(image,cube_info->colors,exception) == MagickFalse)
    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
      image->filename);
  image->colors=0;
  cube_info->transparent_pixels=0;
  cube_info->transparent_index=(-1);
  (void) DefineImageColormap(image,cube_info,cube_info->root);
  /*
    Create a reduced color image.
  */
  if (cube_info->quantize_info->dither_method != NoDitherMethod)
    (void) DitherImage(image,cube_info,exception);
  else
    {
      CacheView
        *image_view;

      MagickBooleanType
        status;

      status=MagickTrue;
      image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
      #pragma omp parallel for schedule(static) shared(status) \
        magick_number_threads(image,image,image->rows,1)
#endif
      for (y=0; y < (ssize_t) image->rows; y++)
      {
        CubeInfo
          cube;

        register Quantum
          *magick_restrict q;

        register ssize_t
          x;

        ssize_t
          count;

        if (status == MagickFalse)
          continue;
        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
          exception);
        if (q == (Quantum *) NULL)
          {
            status=MagickFalse;
            continue;
          }
        cube=(*cube_info);
        for (x=0; x < (ssize_t) image->columns; x+=count)
        {
          DoublePixelPacket
            pixel;

          register const NodeInfo
            *node_info;

          register ssize_t
            i;

          size_t
            id,
            index;

          /*
            Identify the deepest node containing the pixel's color.
          */
          for (count=1; (x+count) < (ssize_t) image->columns; count++)
          {
            PixelInfo
              packet;

            GetPixelInfoPixel(image,q+count*GetPixelChannels(image),&packet);
            if (IsPixelEquivalent(image,q,&packet) == MagickFalse)
              break;
          }
          AssociateAlphaPixel(image,&cube,q,&pixel);
          node_info=cube.root;
          for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
          {
            id=ColorToNodeId(&cube,&pixel,index);
            if (node_info->child[id] == (NodeInfo *) NULL)
              break;
            node_info=node_info->child[id];
          }
          /*
            Find closest color among siblings and their children.
          */
          cube.target=pixel;
          cube.distance=(double) (4.0*(QuantumRange+1.0)*(QuantumRange+1.0)+
            1.0);
          ClosestColor(image,&cube,node_info->parent);
          index=cube.color_number;
          for (i=0; i < (ssize_t) count; i++)
          {
            if (image->storage_class == PseudoClass)
              SetPixelIndex(image,(Quantum) index,q);
            if (cube.quantize_info->measure_error == MagickFalse)
              {
                SetPixelRed(image,ClampToQuantum(
                  image->colormap[index].red),q);
                SetPixelGreen(image,ClampToQuantum(
                  image->colormap[index].green),q);
                SetPixelBlue(image,ClampToQuantum(
                  image->colormap[index].blue),q);
                if (cube.associate_alpha != MagickFalse)
                  SetPixelAlpha(image,ClampToQuantum(
                    image->colormap[index].alpha),q);
              }
            q+=GetPixelChannels(image);
          }
        }
        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
          status=MagickFalse;
        if (image->progress_monitor != (MagickProgressMonitor) NULL)
          {
            MagickBooleanType
              proceed;

            proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y,
              image->rows);
            if (proceed == MagickFalse)
              status=MagickFalse;
          }
      }
      image_view=DestroyCacheView(image_view);
    }
  if (cube_info->quantize_info->measure_error != MagickFalse)
    (void) GetImageQuantizeError(image,exception);
  if ((cube_info->quantize_info->number_colors == 2) &&
      ((cube_info->quantize_info->colorspace == LinearGRAYColorspace) ||
       (cube_info->quantize_info->colorspace == GRAYColorspace)))
    {
      double
        intensity;

      /*
        Monochrome image.
      */
      intensity=0.0;
      if ((image->colors > 1) &&
          (GetPixelInfoLuma(image->colormap+0) >
           GetPixelInfoLuma(image->colormap+1)))
        intensity=(double) QuantumRange;
      image->colormap[0].red=intensity;
      image->colormap[0].green=intensity;
      image->colormap[0].blue=intensity;
      if (image->colors > 1)
        {
          image->colormap[1].red=(double) QuantumRange-intensity;
          image->colormap[1].green=(double) QuantumRange-intensity;
          image->colormap[1].blue=(double) QuantumRange-intensity;
        }
    }
  (void) SyncImage(image,exception);
  if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
      (IssRGBCompatibleColorspace(colorspace) == MagickFalse))
    (void) TransformImageColorspace(image,colorspace,exception);
  return(MagickTrue);
}


static MagickBooleanType FloydSteinbergDither(Image *image,CubeInfo *cube_info,
  ExceptionInfo *exception)
{
#define DitherImageTag  "Dither/Image"

  CacheView
    *image_view;

  const char
    *artifact;

  double
    amount;

  DoublePixelPacket
    **pixels;

  MagickBooleanType
    status;

  ssize_t
    y;

  /*
    Distribute quantization error using Floyd-Steinberg.
  */
  pixels=AcquirePixelThreadSet(image->columns);
  if (pixels == (DoublePixelPacket **) NULL)
    return(MagickFalse);
  status=MagickTrue;
  amount=1.0;
  artifact=GetImageArtifact(image,"dither:diffusion-amount");
  if (artifact != (const char *) NULL)
    amount=StringToDoubleInterval(artifact,1.0);
  image_view=AcquireAuthenticCacheView(image,exception);
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    const int
      id = GetOpenMPThreadId();

    CubeInfo
      cube;

    DoublePixelPacket
      *current,
      *previous;

    register Quantum
      *magick_restrict q;

    register ssize_t
      x;

    size_t
      index;

    ssize_t
      v;

    if (status == MagickFalse)
      continue;
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      {
        status=MagickFalse;
        continue;
      }
    cube=(*cube_info);
    current=pixels[id]+(y & 0x01)*image->columns;
    previous=pixels[id]+((y+1) & 0x01)*image->columns;
    v=(ssize_t) ((y & 0x01) != 0 ? -1 : 1);
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      DoublePixelPacket
        color,
        pixel;

      register ssize_t
        i;

      ssize_t
        u;

      u=(y & 0x01) != 0 ? (ssize_t) image->columns-1-x : x;
      AssociateAlphaPixel(image,&cube,q+u*GetPixelChannels(image),&pixel);
      if (x > 0)
        {
          pixel.red+=7.0*amount*current[u-v].red/16;
          pixel.green+=7.0*amount*current[u-v].green/16;
          pixel.blue+=7.0*amount*current[u-v].blue/16;
          if (cube.associate_alpha != MagickFalse)
            pixel.alpha+=7.0*amount*current[u-v].alpha/16;
        }
      if (y > 0)
        {
          if (x < (ssize_t) (image->columns-1))
            {
              pixel.red+=previous[u+v].red/16;
              pixel.green+=previous[u+v].green/16;
              pixel.blue+=previous[u+v].blue/16;
              if (cube.associate_alpha != MagickFalse)
                pixel.alpha+=previous[u+v].alpha/16;
            }
          pixel.red+=5.0*amount*previous[u].red/16;
          pixel.green+=5.0*amount*previous[u].green/16;
          pixel.blue+=5.0*amount*previous[u].blue/16;
          if (cube.associate_alpha != MagickFalse)
            pixel.alpha+=5.0*amount*previous[u].alpha/16;
          if (x > 0)
            {
              pixel.red+=3.0*amount*previous[u-v].red/16;
              pixel.green+=3.0*amount*previous[u-v].green/16;
              pixel.blue+=3.0*amount*previous[u-v].blue/16;
              if (cube.associate_alpha != MagickFalse)
                pixel.alpha+=3.0*amount*previous[u-v].alpha/16;
            }
        }
      pixel.red=(double) ClampPixel(pixel.red);
      pixel.green=(double) ClampPixel(pixel.green);
      pixel.blue=(double) ClampPixel(pixel.blue);
      if (cube.associate_alpha != MagickFalse)
        pixel.alpha=(double) ClampPixel(pixel.alpha);
      i=CacheOffset(&cube,&pixel);
      if (cube.cache[i] < 0)
        {
          register NodeInfo
            *node_info;

          register size_t
            node_id;

          /*
            Identify the deepest node containing the pixel's color.
          */
          node_info=cube.root;
          for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
          {
            node_id=ColorToNodeId(&cube,&pixel,index);
            if (node_info->child[node_id] == (NodeInfo *) NULL)
              break;
            node_info=node_info->child[node_id];
          }
          /*
            Find closest color among siblings and their children.
          */
          cube.target=pixel;
          cube.distance=(double) (4.0*(QuantumRange+1.0)*(QuantumRange+1.0)+
            1.0);
          ClosestColor(image,&cube,node_info->parent);
          cube.cache[i]=(ssize_t) cube.color_number;
        }
      /*
        Assign pixel to closest colormap entry.
      */
      index=(size_t) cube.cache[i];
      if (image->storage_class == PseudoClass)
        SetPixelIndex(image,(Quantum) index,q+u*GetPixelChannels(image));
      if (cube.quantize_info->measure_error == MagickFalse)
        {
          SetPixelRed(image,ClampToQuantum(image->colormap[index].red),
            q+u*GetPixelChannels(image));
          SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),
            q+u*GetPixelChannels(image));
          SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),
            q+u*GetPixelChannels(image));
          if (cube.associate_alpha != MagickFalse)
            SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),
              q+u*GetPixelChannels(image));
        }
      if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
        status=MagickFalse;
      /*
        Store the error.
      */
      AssociateAlphaPixelInfo(&cube,image->colormap+index,&color);
      current[u].red=pixel.red-color.red;
      current[u].green=pixel.green-color.green;
      current[u].blue=pixel.blue-color.blue;
      if (cube.associate_alpha != MagickFalse)
        current[u].alpha=pixel.alpha-color.alpha;
      if (image->progress_monitor != (MagickProgressMonitor) NULL)
        {
          MagickBooleanType
            proceed;

          proceed=SetImageProgress(image,DitherImageTag,(MagickOffsetType) y,
            image->rows);
          if (proceed == MagickFalse)
            status=MagickFalse;
        }
    }
  }
  image_view=DestroyCacheView(image_view);
  pixels=DestroyPixelThreadSet(pixels);
  return(MagickTrue);
}


static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
{
	unsigned int iter = 100000;

	while (snd_ctl_remove_numid_conflict(card, count)) {
		if (--iter == 0) {
			/* this situation is very unlikely */
			dev_err(card->dev, "unable to allocate new control numid\n");
			return -ENOMEM;
		}
	}
	return 0;
}

ALSA: control: Re-order bounds checking in get_ctl_id_hash()

These two checks are in the reverse order so it might read one element
beyond the end of the array.  First check if the "i" is within bounds
before using it.

Fixes: 6ab55ec0a938 ("ALSA: control: Fix an out-of-bounds bug in get_ctl_id_hash()")
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Link: https://lore.kernel.org/r/YwjgNh/gkG1hH7po@kili
Signed-off-by: Takashi Iwai <tiwai@suse.de>


static int snd_ctl_elem_read(struct snd_card *card,
			     struct snd_ctl_elem_value *control)
{
	struct snd_kcontrol *kctl;
	struct snd_kcontrol_volatile *vd;
	unsigned int index_offset;
	struct snd_ctl_elem_info info;
	const u32 pattern = 0xdeadbeef;
	int ret;

	kctl = snd_ctl_find_id(card, &control->id);
	if (kctl == NULL)
		return -ENOENT;

	index_offset = snd_ctl_get_ioff(kctl, &control->id);
	vd = &kctl->vd[index_offset];
	if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL)
		return -EPERM;

	snd_ctl_build_ioff(&control->id, kctl, index_offset);

#ifdef CONFIG_SND_CTL_DEBUG
	/* info is needed only for validation */
	memset(&info, 0, sizeof(info));
	info.id = control->id;
	ret = __snd_ctl_elem_info(card, kctl, &info, NULL);
	if (ret < 0)
		return ret;
#endif

	if (!snd_ctl_skip_validation(&info))
		fill_remaining_elem_value(control, &info, pattern);
	ret = snd_power_ref_and_wait(card);
	if (!ret)
		ret = kctl->get(kctl, control);
	snd_power_unref(card);
	if (ret < 0)
		return ret;
	if (!snd_ctl_skip_validation(&info) &&
	    sanity_check_elem_value(card, control, &info, pattern) < 0) {
		dev_err(card->dev,
			"control %i:%i:%i:%s:%i: access overflow\n",
			control->id.iface, control->id.device,
			control->id.subdevice, control->id.name,
			control->id.index);
		return -EINVAL;
	}
	return ret;
}


int snd_ctl_create(struct snd_card *card)
{
	static const struct snd_device_ops ops = {
		.dev_free = snd_ctl_dev_free,
		.dev_register =	snd_ctl_dev_register,
		.dev_disconnect = snd_ctl_dev_disconnect,
	};
	int err;

	if (snd_BUG_ON(!card))
		return -ENXIO;
	if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
		return -ENXIO;

	snd_device_initialize(&card->ctl_dev, card);
	dev_set_name(&card->ctl_dev, "controlC%d", card->number);

	err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
	if (err < 0)
		put_device(&card->ctl_dev);
	return err;
}


static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
{
	int subscribe;
	if (get_user(subscribe, ptr))
		return -EFAULT;
	if (subscribe < 0) {
		subscribe = file->subscribed;
		if (put_user(subscribe, ptr))
			return -EFAULT;
		return 0;
	}
	if (subscribe) {
		file->subscribed = 1;
		return 0;
	} else if (file->subscribed) {
		snd_ctl_empty_read_queue(file);
		file->subscribed = 0;
	}
	return 0;
}


static int sanity_check_int_value(struct snd_card *card,
				  const struct snd_ctl_elem_value *control,
				  const struct snd_ctl_elem_info *info,
				  int i, bool print_error)
{
	long long lval, lmin, lmax, lstep;
	u64 rem;

	switch (info->type) {
	default:
	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
		lval = control->value.integer.value[i];
		lmin = 0;
		lmax = 1;
		lstep = 0;
		break;
	case SNDRV_CTL_ELEM_TYPE_INTEGER:
		lval = control->value.integer.value[i];
		lmin = info->value.integer.min;
		lmax = info->value.integer.max;
		lstep = info->value.integer.step;
		break;
	case SNDRV_CTL_ELEM_TYPE_INTEGER64:
		lval = control->value.integer64.value[i];
		lmin = info->value.integer64.min;
		lmax = info->value.integer64.max;
		lstep = info->value.integer64.step;
		break;
	case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
		lval = control->value.enumerated.item[i];
		lmin = 0;
		lmax = info->value.enumerated.items - 1;
		lstep = 0;
		break;
	}

	if (lval < lmin || lval > lmax) {
		if (print_error)
			dev_err(card->dev,
				"control %i:%i:%i:%s:%i: value out of range %lld (%lld/%lld) at count %i\n",
				control->id.iface, control->id.device,
				control->id.subdevice, control->id.name,
				control->id.index, lval, lmin, lmax, i);
		return -EINVAL;
	}
	if (lstep) {
		div64_u64_rem(lval, lstep, &rem);
		if (rem) {
			if (print_error)
				dev_err(card->dev,
					"control %i:%i:%i:%s:%i: unaligned value %lld (step %lld) at count %i\n",
					control->id.iface, control->id.device,
					control->id.subdevice, control->id.name,
					control->id.index, lval, lstep, i);
			return -EINVAL;
		}
	}

	return 0;
}


static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
			    struct snd_kcontrol *kctl,
			    struct snd_ctl_elem_id *id,
			    unsigned int __user *buf, unsigned int size)
{
	static const struct {
		int op;
		int perm;
	} pairs[] = {
		{SNDRV_CTL_TLV_OP_READ,  SNDRV_CTL_ELEM_ACCESS_TLV_READ},
		{SNDRV_CTL_TLV_OP_WRITE, SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
		{SNDRV_CTL_TLV_OP_CMD,   SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
	};
	struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
	int i, ret;

	/* Check support of the request for this element. */
	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
		if (op_flag == pairs[i].op && (vd->access & pairs[i].perm))
			break;
	}
	if (i == ARRAY_SIZE(pairs))
		return -ENXIO;

	if (kctl->tlv.c == NULL)
		return -ENXIO;

	/* Write and command operations are not allowed for locked element. */
	if (op_flag != SNDRV_CTL_TLV_OP_READ &&
	    vd->owner != NULL && vd->owner != file)
		return -EPERM;

	ret = snd_power_ref_and_wait(file->card);
	if (!ret)
		ret = kctl->tlv.c(kctl, op_flag, size, buf);
	snd_power_unref(file->card);
	return ret;
}


static int snd_ctl_open(struct inode *inode, struct file *file)
{
	unsigned long flags;
	struct snd_card *card;
	struct snd_ctl_file *ctl;
	int i, err;

	err = stream_open(inode, file);
	if (err < 0)
		return err;

	card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);
	if (!card) {
		err = -ENODEV;
		goto __error1;
	}
	err = snd_card_file_add(card, file);
	if (err < 0) {
		err = -ENODEV;
		goto __error1;
	}
	if (!try_module_get(card->module)) {
		err = -EFAULT;
		goto __error2;
	}
	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
	if (ctl == NULL) {
		err = -ENOMEM;
		goto __error;
	}
	INIT_LIST_HEAD(&ctl->events);
	init_waitqueue_head(&ctl->change_sleep);
	spin_lock_init(&ctl->read_lock);
	ctl->card = card;
	for (i = 0; i < SND_CTL_SUBDEV_ITEMS; i++)
		ctl->preferred_subdevice[i] = -1;
	ctl->pid = get_pid(task_pid(current));
	file->private_data = ctl;
	write_lock_irqsave(&card->ctl_files_rwlock, flags);
	list_add_tail(&ctl->list, &card->ctl_files);
	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
	snd_card_unref(card);
	return 0;

      __error:
	module_put(card->module);
      __error2:
	snd_card_file_remove(card, file);
      __error1:
	if (card)
		snd_card_unref(card);
      	return err;
}


void snd_ctl_notify_one(struct snd_card *card, unsigned int mask,
			struct snd_kcontrol *kctl, unsigned int ioff)
{
	struct snd_ctl_elem_id id = kctl->id;
	struct snd_ctl_layer_ops *lops;

	id.index += ioff;
	id.numid += ioff;
	snd_ctl_notify(card, mask, &id);
	down_read(&snd_ctl_layer_rwsem);
	for (lops = snd_ctl_layer; lops; lops = lops->next)
		lops->lnotify(card, mask, kctl, ioff);
	up_read(&snd_ctl_layer_rwsem);
}


static int sanity_check_elem_value(struct snd_card *card,
				   const struct snd_ctl_elem_value *control,
				   const struct snd_ctl_elem_info *info,
				   u32 pattern)
{
	size_t offset;
	int ret;
	u32 *p;

	ret = sanity_check_input_values(card, control, info, true);
	if (ret < 0)
		return ret;

	/* check whether the remaining area kept untouched */
	offset = value_sizes[info->type] * info->count;
	offset = DIV_ROUND_UP(offset, sizeof(u32));
	p = (u32 *)control->value.bytes.data + offset;
	for (; offset < sizeof(control->value) / sizeof(u32); offset++, p++) {
		if (*p != pattern) {
			ret = -EINVAL;
			break;
		}
		*p = 0; /* clear the checked area */
	}

	return ret;
}


static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
			    size_t count, loff_t * offset)
{
	struct snd_ctl_file *ctl;
	int err = 0;
	ssize_t result = 0;

	ctl = file->private_data;
	if (snd_BUG_ON(!ctl || !ctl->card))
		return -ENXIO;
	if (!ctl->subscribed)
		return -EBADFD;
	if (count < sizeof(struct snd_ctl_event))
		return -EINVAL;
	spin_lock_irq(&ctl->read_lock);
	while (count >= sizeof(struct snd_ctl_event)) {
		struct snd_ctl_event ev;
		struct snd_kctl_event *kev;
		while (list_empty(&ctl->events)) {
			wait_queue_entry_t wait;
			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
				err = -EAGAIN;
				goto __end_lock;
			}
			init_waitqueue_entry(&wait, current);
			add_wait_queue(&ctl->change_sleep, &wait);
			set_current_state(TASK_INTERRUPTIBLE);
			spin_unlock_irq(&ctl->read_lock);
			schedule();
			remove_wait_queue(&ctl->change_sleep, &wait);
			if (ctl->card->shutdown)
				return -ENODEV;
			if (signal_pending(current))
				return -ERESTARTSYS;
			spin_lock_irq(&ctl->read_lock);
		}
		kev = snd_kctl_event(ctl->events.next);
		ev.type = SNDRV_CTL_EVENT_ELEM;
		ev.data.elem.mask = kev->mask;
		ev.data.elem.id = kev->id;
		list_del(&kev->list);
		spin_unlock_irq(&ctl->read_lock);
		kfree(kev);
		if (copy_to_user(buffer, &ev, sizeof(struct snd_ctl_event))) {
			err = -EFAULT;
			goto __end;
		}
		spin_lock_irq(&ctl->read_lock);
		buffer += sizeof(struct snd_ctl_event);
		count -= sizeof(struct snd_ctl_event);
		result += sizeof(struct snd_ctl_event);
	}
      __end_lock:
	spin_unlock_irq(&ctl->read_lock);
      __end:
      	return result > 0 ? result : err;
}


static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
				   struct snd_ctl_elem_id *id)
{
	struct snd_card *card = file->card;
	struct snd_kcontrol *kctl;
	int idx, ret;

	down_write(&card->controls_rwsem);
	kctl = snd_ctl_find_id(card, id);
	if (kctl == NULL) {
		ret = -ENOENT;
		goto error;
	}
	if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
		ret = -EINVAL;
		goto error;
	}
	for (idx = 0; idx < kctl->count; idx++)
		if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
			ret = -EBUSY;
			goto error;
		}
	ret = snd_ctl_remove(card, kctl);
error:
	up_write(&card->controls_rwsem);
	return ret;
}


static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
				     struct list_head *lists)
{
	struct snd_kctl_ioctl *p;

	if (snd_BUG_ON(!fcn))
		return -EINVAL;
	down_write(&snd_ioctl_rwsem);
	list_for_each_entry(p, lists, list) {
		if (p->fioctl == fcn) {
			list_del(&p->list);
			up_write(&snd_ioctl_rwsem);
			kfree(p);
			return 0;
		}
	}
	up_write(&snd_ioctl_rwsem);
	snd_BUG();
	return -EINVAL;
}


int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
	return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
}


int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
			      struct snd_ctl_elem_info *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
	uinfo->count = 1;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = 1;
	return 0;
}


static int __snd_ctl_remove(struct snd_card *card,
			    struct snd_kcontrol *kcontrol,
			    bool remove_hash)
{
	unsigned int idx;

	if (snd_BUG_ON(!card || !kcontrol))
		return -EINVAL;
	list_del(&kcontrol->list);

	if (remove_hash)
		remove_hash_entries(card, kcontrol);

	card->controls_count -= kcontrol->count;
	for (idx = 0; idx < kcontrol->count; idx++)
		snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx);
	snd_ctl_free_one(kcontrol);
	return 0;
}


static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
			     struct snd_ctl_tlv __user *buf,
                             int op_flag)
{
	struct snd_ctl_tlv header;
	unsigned int __user *container;
	unsigned int container_size;
	struct snd_kcontrol *kctl;
	struct snd_ctl_elem_id id;
	struct snd_kcontrol_volatile *vd;

	if (copy_from_user(&header, buf, sizeof(header)))
		return -EFAULT;

	/* In design of control core, numerical ID starts at 1. */
	if (header.numid == 0)
		return -EINVAL;

	/* At least, container should include type and length fields.  */
	if (header.length < sizeof(unsigned int) * 2)
		return -EINVAL;
	container_size = header.length;
	container = buf->tlv;

	kctl = snd_ctl_find_numid(file->card, header.numid);
	if (kctl == NULL)
		return -ENOENT;

	/* Calculate index of the element in this set. */
	id = kctl->id;
	snd_ctl_build_ioff(&id, kctl, header.numid - id.numid);
	vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];

	if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
		return call_tlv_handler(file, op_flag, kctl, &id, container,
					container_size);
	} else {
		if (op_flag == SNDRV_CTL_TLV_OP_READ) {
			return read_tlv_buf(kctl, &id, container,
					    container_size);
		}
	}

	/* Not supported. */
	return -ENXIO;
}


static int snd_ctl_release(struct inode *inode, struct file *file)
{
	unsigned long flags;
	struct snd_card *card;
	struct snd_ctl_file *ctl;
	struct snd_kcontrol *control;
	unsigned int idx;

	ctl = file->private_data;
	file->private_data = NULL;
	card = ctl->card;
	write_lock_irqsave(&card->ctl_files_rwlock, flags);
	list_del(&ctl->list);
	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
	down_write(&card->controls_rwsem);
	list_for_each_entry(control, &card->controls, list)
		for (idx = 0; idx < control->count; idx++)
			if (control->vd[idx].owner == ctl)
				control->vd[idx].owner = NULL;
	up_write(&card->controls_rwsem);
	snd_fasync_free(ctl->fasync);
	snd_ctl_empty_read_queue(ctl);
	put_pid(ctl->pid);
	kfree(ctl);
	module_put(card->module);
	snd_card_file_remove(card, file);
	return 0;
}


struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
				  void *private_data)
{
	struct snd_kcontrol *kctl;
	unsigned int count;
	unsigned int access;
	int err;

	if (snd_BUG_ON(!ncontrol || !ncontrol->info))
		return NULL;

	count = ncontrol->count;
	if (count == 0)
		count = 1;

	access = ncontrol->access;
	if (access == 0)
		access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
	access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
		   SNDRV_CTL_ELEM_ACCESS_VOLATILE |
		   SNDRV_CTL_ELEM_ACCESS_INACTIVE |
		   SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
		   SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
		   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK |
		   SNDRV_CTL_ELEM_ACCESS_LED_MASK |
		   SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK);

	err = snd_ctl_new(&kctl, count, access, NULL);
	if (err < 0)
		return NULL;

	/* The 'numid' member is decided when calling snd_ctl_add(). */
	kctl->id.iface = ncontrol->iface;
	kctl->id.device = ncontrol->device;
	kctl->id.subdevice = ncontrol->subdevice;
	if (ncontrol->name) {
		strscpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name));
		if (strcmp(ncontrol->name, kctl->id.name) != 0)
			pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
				ncontrol->name, kctl->id.name);
	}
	kctl->id.index = ncontrol->index;

	kctl->info = ncontrol->info;
	kctl->get = ncontrol->get;
	kctl->put = ncontrol->put;
	kctl->tlv.p = ncontrol->tlv.p;

	kctl->private_value = ncontrol->private_value;
	kctl->private_data = private_data;

	return kctl;
}


static int snd_ctl_elem_list_user(struct snd_card *card,
				  struct snd_ctl_elem_list __user *_list)
{
	struct snd_ctl_elem_list list;
	int err;

	if (copy_from_user(&list, _list, sizeof(list)))
		return -EFAULT;
	err = snd_ctl_elem_list(card, &list);
	if (err)
		return err;
	if (copy_to_user(_list, &list, sizeof(list)))
		return -EFAULT;

	return 0;
}


int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
		      unsigned int items, const char *const names[])
{
	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	info->count = channels;
	info->value.enumerated.items = items;
	if (!items)
		return 0;
	if (info->value.enumerated.item >= items)
		info->value.enumerated.item = items - 1;
	WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name),
	     "ALSA: too long item name '%s'\n",
	     names[info->value.enumerated.item]);
	strscpy(info->value.enumerated.name,
		names[info->value.enumerated.item],
		sizeof(info->value.enumerated.name));
	return 0;
}


static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
{
	unsigned long flags;
	struct snd_kctl_event *cread;

	spin_lock_irqsave(&ctl->read_lock, flags);
	while (!list_empty(&ctl->events)) {
		cread = snd_kctl_event(ctl->events.next);
		list_del(&cread->list);
		kfree(cread);
	}
	spin_unlock_irqrestore(&ctl->read_lock, flags);
}


int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
	return snd_ctl_add_replace(card, kcontrol, CTL_ADD_EXCLUSIVE);
}


static int snd_ctl_elem_list(struct snd_card *card,
			     struct snd_ctl_elem_list *list)
{
	struct snd_kcontrol *kctl;
	struct snd_ctl_elem_id id;
	unsigned int offset, space, jidx;
	int err = 0;

	offset = list->offset;
	space = list->space;

	down_read(&card->controls_rwsem);
	list->count = card->controls_count;
	list->used = 0;
	if (space > 0) {
		list_for_each_entry(kctl, &card->controls, list) {
			if (offset >= kctl->count) {
				offset -= kctl->count;
				continue;
			}
			for (jidx = offset; jidx < kctl->count; jidx++) {
				snd_ctl_build_ioff(&id, kctl, jidx);
				if (copy_to_user(list->pids + list->used, &id,
						 sizeof(id))) {
					err = -EFAULT;
					goto out;
				}
				list->used++;
				if (!--space)
					goto out;
			}
			offset = 0;
		}
	}
 out:
	up_read(&card->controls_rwsem);
	return err;
}


static int snd_ctl_elem_lock(struct snd_ctl_file *file,
			     struct snd_ctl_elem_id __user *_id)
{
	struct snd_card *card = file->card;
	struct snd_ctl_elem_id id;
	struct snd_kcontrol *kctl;
	struct snd_kcontrol_volatile *vd;
	int result;

	if (copy_from_user(&id, _id, sizeof(id)))
		return -EFAULT;
	down_write(&card->controls_rwsem);
	kctl = snd_ctl_find_id(card, &id);
	if (kctl == NULL) {
		result = -ENOENT;
	} else {
		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
		if (vd->owner != NULL)
			result = -EBUSY;
		else {
			vd->owner = file;
			result = 0;
		}
	}
	up_write(&card->controls_rwsem);
	return result;
}


static size_t compute_user_elem_size(size_t size, unsigned int count)
{
	return sizeof(struct user_element) + size * count;
}


static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_info *uinfo)
{
	struct user_element *ue = kcontrol->private_data;
	unsigned int offset;

	offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
	*uinfo = ue->info;
	snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);

	return 0;
}


static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kctl, int op_flag,
				 unsigned int size, unsigned int __user *buf)
{
	if (op_flag == SNDRV_CTL_TLV_OP_WRITE)
		return replace_user_tlv(kctl, buf, size);
	else
		return read_user_tlv(kctl, buf, size);
}


int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
{
	struct snd_ctl_file *kctl;
	int subdevice = -1;
	unsigned long flags;

	read_lock_irqsave(&card->ctl_files_rwlock, flags);
	list_for_each_entry(kctl, &card->ctl_files, list) {
		if (kctl->pid == task_pid(current)) {
			subdevice = kctl->preferred_subdevice[type];
			if (subdevice != -1)
				break;
		}
	}
	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
	return subdevice;
}


static void remove_hash_entries(struct snd_card *card,
				struct snd_kcontrol *kcontrol)
{
	struct snd_ctl_elem_id id = kcontrol->id;
	struct snd_kcontrol *matched;
	unsigned long h;
	int i;

	for (i = 0; i < kcontrol->count; i++) {
		xa_erase(&card->ctl_numids, id.numid);
		h = get_ctl_id_hash(&id);
		matched = xa_load(&card->ctl_hash, h);
		if (matched && (matched == kcontrol ||
				elem_id_matches(matched, &id)))
			xa_erase(&card->ctl_hash, h);
		id.index++;
		id.numid++;
	}
}


static int snd_ctl_dev_disconnect(struct snd_device *device)
{
	struct snd_card *card = device->device_data;
	struct snd_ctl_file *ctl;
	struct snd_ctl_layer_ops *lops;
	unsigned long flags;

	read_lock_irqsave(&card->ctl_files_rwlock, flags);
	list_for_each_entry(ctl, &card->ctl_files, list) {
		wake_up(&ctl->change_sleep);
		snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
	}
	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);

	down_read(&card->controls_rwsem);
	down_read(&snd_ctl_layer_rwsem);
	for (lops = snd_ctl_layer; lops; lops = lops->next)
		lops->ldisconnect(card);
	up_read(&snd_ctl_layer_rwsem);
	up_read(&card->controls_rwsem);

	return snd_unregister_device(&card->ctl_dev);
}


static unsigned long get_ctl_id_hash(const struct snd_ctl_elem_id *id)
{
	int i;
	unsigned long h;

	h = id->iface;
	h = MULTIPLIER * h + id->device;
	h = MULTIPLIER * h + id->subdevice;
	for (i = 0; i < SNDRV_CTL_ELEM_ID_NAME_MAXLEN && id->name[i]; i++)
		h = MULTIPLIER * h + id->name[i];
	h = MULTIPLIER * h + id->index;
	h &= LONG_MAX;
	return h;
}


static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
			     unsigned int cmd, void __user *arg)
{
	struct snd_ctl_card_info *info;

	info = kzalloc(sizeof(*info), GFP_KERNEL);
	if (! info)
		return -ENOMEM;
	down_read(&snd_ioctl_rwsem);
	info->card = card->number;
	strscpy(info->id, card->id, sizeof(info->id));
	strscpy(info->driver, card->driver, sizeof(info->driver));
	strscpy(info->name, card->shortname, sizeof(info->name));
	strscpy(info->longname, card->longname, sizeof(info->longname));
	strscpy(info->mixername, card->mixername, sizeof(info->mixername));
	strscpy(info->components, card->components, sizeof(info->components));
	up_read(&snd_ioctl_rwsem);
	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
		kfree(info);
		return -EFAULT;
	}
	kfree(info);
	return 0;
}


static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
			    unsigned int size)
{
	struct user_element *ue = kctl->private_data;
	unsigned int *container;
	unsigned int mask = 0;
	int i;
	int change;

	if (size > 1024 * 128)	/* sane value */
		return -EINVAL;

	// does the TLV size change cause overflow?
	if (check_user_elem_overflow(ue->card, (ssize_t)(size - ue->tlv_data_size)))
		return -ENOMEM;

	container = vmemdup_user(buf, size);
	if (IS_ERR(container))
		return PTR_ERR(container);

	change = ue->tlv_data_size != size;
	if (!change)
		change = memcmp(ue->tlv_data, container, size) != 0;
	if (!change) {
		kvfree(container);
		return 0;
	}

	if (ue->tlv_data == NULL) {
		/* Now TLV data is available. */
		for (i = 0; i < kctl->count; ++i)
			kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
		mask = SNDRV_CTL_EVENT_MASK_INFO;
	} else {
		ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
		ue->tlv_data_size = 0;
		kvfree(ue->tlv_data);
	}

	ue->tlv_data = container;
	ue->tlv_data_size = size;
	// decremented at private_free.
	ue->card->user_ctl_alloc_size += size;

	mask |= SNDRV_CTL_EVENT_MASK_TLV;
	for (i = 0; i < kctl->count; ++i)
		snd_ctl_notify_one(ue->card, mask, kctl, i);

	return change;
}


static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
				  struct snd_ctl_elem_info __user *_info)
{
	struct snd_ctl_elem_info info;
	int result;

	if (copy_from_user(&info, _info, sizeof(info)))
		return -EFAULT;
	result = snd_ctl_elem_info(ctl, &info);
	if (result < 0)
		return result;
	/* drop internal access flags */
	info.access &= ~(SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK|
			 SNDRV_CTL_ELEM_ACCESS_LED_MASK);
	if (copy_to_user(_info, &info, sizeof(info)))
		return -EFAULT;
	return result;
}


static __poll_t snd_ctl_poll(struct file *file, poll_table * wait)
{
	__poll_t mask;
	struct snd_ctl_file *ctl;

	ctl = file->private_data;
	if (!ctl->subscribed)
		return 0;
	poll_wait(file, &ctl->change_sleep, wait);

	mask = 0;
	if (!list_empty(&ctl->events))
		mask |= EPOLLIN | EPOLLRDNORM;

	return mask;
}


static int snd_ctl_check_elem_info(struct snd_card *card,
				   const struct snd_ctl_elem_info *info)
{
	static const unsigned int max_value_counts[] = {
		[SNDRV_CTL_ELEM_TYPE_BOOLEAN]	= 128,
		[SNDRV_CTL_ELEM_TYPE_INTEGER]	= 128,
		[SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128,
		[SNDRV_CTL_ELEM_TYPE_BYTES]	= 512,
		[SNDRV_CTL_ELEM_TYPE_IEC958]	= 1,
		[SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64,
	};

	if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
	    info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) {
		if (card)
			dev_err(card->dev,
				"control %i:%i:%i:%s:%i: invalid type %d\n",
				info->id.iface, info->id.device,
				info->id.subdevice, info->id.name,
				info->id.index, info->type);
		return -EINVAL;
	}
	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
	    info->value.enumerated.items == 0) {
		if (card)
			dev_err(card->dev,
				"control %i:%i:%i:%s:%i: zero enum items\n",
				info->id.iface, info->id.device,
				info->id.subdevice, info->id.name,
				info->id.index);
		return -EINVAL;
	}
	if (info->count > max_value_counts[info->type]) {
		if (card)
			dev_err(card->dev,
				"control %i:%i:%i:%s:%i: invalid count %d\n",
				info->id.iface, info->id.device,
				info->id.subdevice, info->id.name,
				info->id.index, info->count);
		return -EINVAL;
	}

	return 0;
}


int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
{
	struct snd_kcontrol *kctl;
	int ret;

	down_write(&card->controls_rwsem);
	kctl = snd_ctl_find_id(card, id);
	if (kctl == NULL) {
		up_write(&card->controls_rwsem);
		return -ENOENT;
	}
	ret = snd_ctl_remove(card, kctl);
	up_write(&card->controls_rwsem);
	return ret;
}


struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
				     struct snd_ctl_elem_id *id)
{
	struct snd_kcontrol *kctl;

	if (snd_BUG_ON(!card || !id))
		return NULL;
	if (id->numid != 0)
		return snd_ctl_find_numid(card, id->numid);
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
	kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id));
	if (kctl && elem_id_matches(kctl, id))
		return kctl;
	if (!card->ctl_hash_collision)
		return NULL; /* we can rely on only hash table */
#endif
	/* no matching in hash table - try all as the last resort */
	list_for_each_entry(kctl, &card->controls, list)
		if (elem_id_matches(kctl, id))
			return kctl;

	return NULL;
}


struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
{
	if (snd_BUG_ON(!card || !numid))
		return NULL;
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
	return xa_load(&card->ctl_numids, numid);
#else
	return snd_ctl_find_numid_slow(card, numid);
#endif
}


void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
{
	struct snd_card *card;
	int card_number;

	down_write(&snd_ctl_layer_rwsem);
	lops->next = snd_ctl_layer;
	snd_ctl_layer = lops;
	up_write(&snd_ctl_layer_rwsem);
	for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
		card = snd_card_ref(card_number);
		if (card) {
			down_read(&card->controls_rwsem);
			lops->lregister(card);
			up_read(&card->controls_rwsem);
			snd_card_unref(card);
		}
	}
}


static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_value *ucontrol)
{
	struct user_element *ue = kcontrol->private_data;
	unsigned int size = ue->elem_data_size;
	char *src = ue->elem_data +
			snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;

	memcpy(&ucontrol->value, src, size);
	return 0;
}


static int snd_ctl_dev_free(struct snd_device *device)
{
	struct snd_card *card = device->device_data;
	struct snd_kcontrol *control;

	down_write(&card->controls_rwsem);
	while (!list_empty(&card->controls)) {
		control = snd_kcontrol(card->controls.next);
		__snd_ctl_remove(card, control, false);
	}

#ifdef CONFIG_SND_CTL_FAST_LOOKUP
	xa_destroy(&card->ctl_numids);
	xa_destroy(&card->ctl_hash);
#endif
	up_write(&card->controls_rwsem);
	put_device(&card->ctl_dev);
	return 0;
}


static void add_hash_entries(struct snd_card *card,
			     struct snd_kcontrol *kcontrol)
{
	struct snd_ctl_elem_id id = kcontrol->id;
	int i;

	xa_store_range(&card->ctl_numids, kcontrol->id.numid,
		       kcontrol->id.numid + kcontrol->count - 1,
		       kcontrol, GFP_KERNEL);

	for (i = 0; i < kcontrol->count; i++) {
		id.index = kcontrol->id.index + i;
		if (xa_insert(&card->ctl_hash, get_ctl_id_hash(&id),
			      kcontrol, GFP_KERNEL)) {
			/* skip hash for this entry, noting we had collision */
			card->ctl_hash_collision = true;
			dev_dbg(card->dev, "ctl_hash collision %d:%s:%d\n",
				id.iface, id.name, id.index);
		}
	}
}


int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
			int active)
{
	struct snd_kcontrol *kctl;
	struct snd_kcontrol_volatile *vd;
	unsigned int index_offset;
	int ret;

	down_write(&card->controls_rwsem);
	kctl = snd_ctl_find_id(card, id);
	if (kctl == NULL) {
		ret = -ENOENT;
		goto unlock;
	}
	index_offset = snd_ctl_get_ioff(kctl, id);
	vd = &kctl->vd[index_offset];
	ret = 0;
	if (active) {
		if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
			goto unlock;
		vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
	} else {
		if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
			goto unlock;
		vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
	}
	snd_ctl_build_ioff(id, kctl, index_offset);
	downgrade_write(&card->controls_rwsem);
	snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, index_offset);
	up_read(&card->controls_rwsem);
	return 1;

 unlock:
	up_write(&card->controls_rwsem);
	return ret;
}


int snd_ctl_request_layer(const char *module_name)
{
	struct snd_ctl_layer_ops *lops;

	if (module_name == NULL)
		return 0;
	down_read(&snd_ctl_layer_rwsem);
	for (lops = snd_ctl_layer; lops; lops = lops->next)
		if (strcmp(lops->module_name, module_name) == 0)
			break;
	up_read(&snd_ctl_layer_rwsem);
	if (lops)
		return 0;
	return request_module(module_name);
}


static void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
				      struct snd_ctl_elem_info *info,
				      u32 pattern)
{
	size_t offset = value_sizes[info->type] * info->count;

	offset = DIV_ROUND_UP(offset, sizeof(u32));
	memset32((u32 *)control->value.bytes.data + offset, pattern,
		 sizeof(control->value) / sizeof(u32) - offset);
}


static inline void remove_hash_entries(struct snd_card *card,
				       struct snd_kcontrol *kcontrol)
{
}


static int sanity_check_input_values(struct snd_card *card,
				     const struct snd_ctl_elem_value *control,
				     const struct snd_ctl_elem_info *info,
				     bool print_error)
{
	int i, ret;

	switch (info->type) {
	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
	case SNDRV_CTL_ELEM_TYPE_INTEGER:
	case SNDRV_CTL_ELEM_TYPE_INTEGER64:
	case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
		for (i = 0; i < info->count; i++) {
			ret = sanity_check_int_value(card, control, info, i,
						     print_error);
			if (ret < 0)
				return ret;
		}
		break;
	default:
		break;
	}

	return 0;
}


static int snd_ctl_elem_remove(struct snd_ctl_file *file,
			       struct snd_ctl_elem_id __user *_id)
{
	struct snd_ctl_elem_id id;

	if (copy_from_user(&id, _id, sizeof(id)))
		return -EFAULT;
	return snd_ctl_remove_user_ctl(file, &id);
}


static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
				       struct snd_ctl_elem_info *uinfo)
{
	struct user_element *ue = kcontrol->private_data;
	const char *names;
	unsigned int item;
	unsigned int offset;

	item = uinfo->value.enumerated.item;

	offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
	*uinfo = ue->info;
	snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);

	item = min(item, uinfo->value.enumerated.items - 1);
	uinfo->value.enumerated.item = item;

	names = ue->priv_data;
	for (; item > 0; --item)
		names += strlen(names) + 1;
	strcpy(uinfo->value.enumerated.name, names);

	return 0;
}


static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
				   struct snd_ctl_elem_value __user *_control)
{
	struct snd_ctl_elem_value *control;
	struct snd_card *card;
	int result;

	control = memdup_user(_control, sizeof(*control));
	if (IS_ERR(control))
		return PTR_ERR(control);

	card = file->card;
	result = snd_ctl_elem_write(card, file, control);
	if (result < 0)
		goto error;

	if (copy_to_user(_control, control, sizeof(*control)))
		result = -EFAULT;
 error:
	kfree(control);
	return result;
}


snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
{
	struct snd_kcontrol *kctl;

	list_for_each_entry(kctl, &card->controls, list) {
		if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
			return kctl;
	}
	return NULL;
}


static int __snd_ctl_elem_info(struct snd_card *card,
			       struct snd_kcontrol *kctl,
			       struct snd_ctl_elem_info *info,
			       struct snd_ctl_file *ctl)
{
	struct snd_kcontrol_volatile *vd;
	unsigned int index_offset;
	int result;

#ifdef CONFIG_SND_DEBUG
	info->access = 0;
#endif
	result = snd_power_ref_and_wait(card);
	if (!result)
		result = kctl->info(kctl, info);
	snd_power_unref(card);
	if (result >= 0) {
		snd_BUG_ON(info->access);
		index_offset = snd_ctl_get_ioff(kctl, &info->id);
		vd = &kctl->vd[index_offset];
		snd_ctl_build_ioff(&info->id, kctl, index_offset);
		info->access = vd->access;
		if (vd->owner) {
			info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
			if (vd->owner == ctl)
				info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
			info->owner = pid_vnr(vd->owner->pid);
		} else {
			info->owner = -1;
		}
		if (!snd_ctl_skip_validation(info) &&
		    snd_ctl_check_elem_info(card, info) < 0)
			result = -EINVAL;
	}
	return result;
}


int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
		    bool add_on_replace)
{
	return snd_ctl_add_replace(card, kcontrol,
				   add_on_replace ? CTL_ADD_ON_REPLACE : CTL_REPLACE);
}


static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
		       unsigned int access, struct snd_ctl_file *file)
{
	unsigned int idx;

	if (count == 0 || count > MAX_CONTROL_COUNT)
		return -EINVAL;

	*kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL);
	if (!*kctl)
		return -ENOMEM;

	for (idx = 0; idx < count; idx++) {
		(*kctl)->vd[idx].access = access;
		(*kctl)->vd[idx].owner = file;
	}
	(*kctl)->count = count;

	return 0;
}


static int snd_ctl_elem_add(struct snd_ctl_file *file,
			    struct snd_ctl_elem_info *info, int replace)
{
	struct snd_card *card = file->card;
	struct snd_kcontrol *kctl;
	unsigned int count;
	unsigned int access;
	long private_size;
	size_t alloc_size;
	struct user_element *ue;
	unsigned int offset;
	int err;

	if (!*info->id.name)
		return -EINVAL;
	if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name))
		return -EINVAL;

	/* Delete a control to replace them if needed. */
	if (replace) {
		info->id.numid = 0;
		err = snd_ctl_remove_user_ctl(file, &info->id);
		if (err)
			return err;
	}

	/* Check the number of elements for this userspace control. */
	count = info->owner;
	if (count == 0)
		count = 1;

	/* Arrange access permissions if needed. */
	access = info->access;
	if (access == 0)
		access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
	access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
		   SNDRV_CTL_ELEM_ACCESS_INACTIVE |
		   SNDRV_CTL_ELEM_ACCESS_TLV_WRITE);

	/* In initial state, nothing is available as TLV container. */
	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
		access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
	access |= SNDRV_CTL_ELEM_ACCESS_USER;

	/*
	 * Check information and calculate the size of data specific to
	 * this userspace control.
	 */
	/* pass NULL to card for suppressing error messages */
	err = snd_ctl_check_elem_info(NULL, info);
	if (err < 0)
		return err;
	/* user-space control doesn't allow zero-size data */
	if (info->count < 1)
		return -EINVAL;
	private_size = value_sizes[info->type] * info->count;
	alloc_size = compute_user_elem_size(private_size, count);

	down_write(&card->controls_rwsem);
	if (check_user_elem_overflow(card, alloc_size)) {
		err = -ENOMEM;
		goto unlock;
	}

	/*
	 * Keep memory object for this userspace control. After passing this
	 * code block, the instance should be freed by snd_ctl_free_one().
	 *
	 * Note that these elements in this control are locked.
	 */
	err = snd_ctl_new(&kctl, count, access, file);
	if (err < 0)
		goto unlock;
	memcpy(&kctl->id, &info->id, sizeof(kctl->id));
	ue = kzalloc(alloc_size, GFP_KERNEL);
	if (!ue) {
		kfree(kctl);
		err = -ENOMEM;
		goto unlock;
	}
	kctl->private_data = ue;
	kctl->private_free = snd_ctl_elem_user_free;

	// increment the allocated size; decremented again at private_free.
	card->user_ctl_alloc_size += alloc_size;

	/* Set private data for this userspace control. */
	ue->card = card;
	ue->info = *info;
	ue->info.access = 0;
	ue->elem_data = (char *)ue + sizeof(*ue);
	ue->elem_data_size = private_size;
	if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
		err = snd_ctl_elem_init_enum_names(ue);
		if (err < 0) {
			snd_ctl_free_one(kctl);
			goto unlock;
		}
	}

	/* Set callback functions. */
	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
		kctl->info = snd_ctl_elem_user_enum_info;
	else
		kctl->info = snd_ctl_elem_user_info;
	if (access & SNDRV_CTL_ELEM_ACCESS_READ)
		kctl->get = snd_ctl_elem_user_get;
	if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
		kctl->put = snd_ctl_elem_user_put;
	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
		kctl->tlv.c = snd_ctl_elem_user_tlv;

	/* This function manage to free the instance on failure. */
	err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE);
	if (err < 0) {
		snd_ctl_free_one(kctl);
		goto unlock;
	}
	offset = snd_ctl_get_ioff(kctl, &info->id);
	snd_ctl_build_ioff(&info->id, kctl, offset);
	/*
	 * Here we cannot fill any field for the number of elements added by
	 * this operation because there're no specific fields. The usage of
	 * 'owner' field for this purpose may cause any bugs to userspace
	 * applications because the field originally means PID of a process
	 * which locks the element.
	 */
 unlock:
	up_write(&card->controls_rwsem);
	return err;
}


static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
			       struct snd_ctl_elem_id __user *_id)
{
	struct snd_card *card = file->card;
	struct snd_ctl_elem_id id;
	struct snd_kcontrol *kctl;
	struct snd_kcontrol_volatile *vd;
	int result;

	if (copy_from_user(&id, _id, sizeof(id)))
		return -EFAULT;
	down_write(&card->controls_rwsem);
	kctl = snd_ctl_find_id(card, &id);
	if (kctl == NULL) {
		result = -ENOENT;
	} else {
		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
		if (vd->owner == NULL)
			result = -EINVAL;
		else if (vd->owner != file)
			result = -EPERM;
		else {
			vd->owner = NULL;
			result = 0;
		}
	}
	up_write(&card->controls_rwsem);
	return result;
}


static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_value *ucontrol)
{
	int change;
	struct user_element *ue = kcontrol->private_data;
	unsigned int size = ue->elem_data_size;
	char *dst = ue->elem_data +
			snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;

	change = memcmp(&ucontrol->value, dst, size) != 0;
	if (change)
		memcpy(dst, &ucontrol->value, size);
	return change;
}


static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
			     struct snd_ctl_elem_info *info)
{
	struct snd_card *card = ctl->card;
	struct snd_kcontrol *kctl;
	int result;

	down_read(&card->controls_rwsem);
	kctl = snd_ctl_find_id(card, &info->id);
	if (kctl == NULL)
		result = -ENOENT;
	else
		result = __snd_ctl_elem_info(card, kctl, info, ctl);
	up_read(&card->controls_rwsem);
	return result;
}


static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
{
	struct user_element *ue = kcontrol->private_data;

	// decrement the allocation size.
	ue->card->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count);
	ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
	if (ue->priv_data)
		ue->card->user_ctl_alloc_size -= ue->info.value.enumerated.names_length;

	kvfree(ue->tlv_data);
	kvfree(ue->priv_data);
	kfree(ue);
}


static int snd_ctl_elem_init_enum_names(struct user_element *ue)
{
	char *names, *p;
	size_t buf_len, name_len;
	unsigned int i;
	const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;

	buf_len = ue->info.value.enumerated.names_length;
	if (buf_len > 64 * 1024)
		return -EINVAL;

	if (check_user_elem_overflow(ue->card, buf_len))
		return -ENOMEM;
	names = vmemdup_user((const void __user *)user_ptrval, buf_len);
	if (IS_ERR(names))
		return PTR_ERR(names);

	/* check that there are enough valid names */
	p = names;
	for (i = 0; i < ue->info.value.enumerated.items; ++i) {
		name_len = strnlen(p, buf_len);
		if (name_len == 0 || name_len >= 64 || name_len == buf_len) {
			kvfree(names);
			return -EINVAL;
		}
		p += name_len + 1;
		buf_len -= name_len + 1;
	}

	ue->priv_data = names;
	ue->info.value.enumerated.names_ptr = 0;
	// increment the allocation size; decremented again at private_free.
	ue->card->user_ctl_alloc_size += ue->info.value.enumerated.names_length;

	return 0;
}


int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
	return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
}


int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
{
	return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
}


static int snd_ctl_elem_read_user(struct snd_card *card,
				  struct snd_ctl_elem_value __user *_control)
{
	struct snd_ctl_elem_value *control;
	int result;

	control = memdup_user(_control, sizeof(*control));
	if (IS_ERR(control))
		return PTR_ERR(control);

	down_read(&card->controls_rwsem);
	result = snd_ctl_elem_read(card, control);
	up_read(&card->controls_rwsem);
	if (result < 0)
		goto error;

	if (copy_to_user(_control, control, sizeof(*control)))
		result = -EFAULT;
 error:
	kfree(control);
	return result;
}


int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
		      struct snd_ctl_elem_id *dst_id)
{
	struct snd_kcontrol *kctl;

	down_write(&card->controls_rwsem);
	kctl = snd_ctl_find_id(card, src_id);
	if (kctl == NULL) {
		up_write(&card->controls_rwsem);
		return -ENOENT;
	}
	remove_hash_entries(card, kctl);
	kctl->id = *dst_id;
	kctl->id.numid = card->last_numid + 1;
	card->last_numid += kctl->count;
	add_hash_entries(card, kctl);
	up_write(&card->controls_rwsem);
	return 0;
}


void snd_ctl_notify(struct snd_card *card, unsigned int mask,
		    struct snd_ctl_elem_id *id)
{
	unsigned long flags;
	struct snd_ctl_file *ctl;
	struct snd_kctl_event *ev;

	if (snd_BUG_ON(!card || !id))
		return;
	if (card->shutdown)
		return;
	read_lock_irqsave(&card->ctl_files_rwlock, flags);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
	card->mixer_oss_change_count++;
#endif
	list_for_each_entry(ctl, &card->ctl_files, list) {
		if (!ctl->subscribed)
			continue;
		spin_lock(&ctl->read_lock);
		list_for_each_entry(ev, &ctl->events, list) {
			if (ev->id.numid == id->numid) {
				ev->mask |= mask;
				goto _found;
			}
		}
		ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
		if (ev) {
			ev->id = *id;
			ev->mask = mask;
			list_add_tail(&ev->list, &ctl->events);
		} else {
			dev_err(card->dev, "No memory available to allocate event\n");
		}
	_found:
		wake_up(&ctl->change_sleep);
		spin_unlock(&ctl->read_lock);
		snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
	}
	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
}


void snd_ctl_free_one(struct snd_kcontrol *kcontrol)
{
	if (kcontrol) {
		if (kcontrol->private_free)
			kcontrol->private_free(kcontrol);
		kfree(kcontrol);
	}
}


static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists)
{
	struct snd_kctl_ioctl *pn;

	pn = kzalloc(sizeof(struct snd_kctl_ioctl), GFP_KERNEL);
	if (pn == NULL)
		return -ENOMEM;
	pn->fioctl = fcn;
	down_write(&snd_ioctl_rwsem);
	list_add_tail(&pn->list, lists);
	up_write(&snd_ioctl_rwsem);
	return 0;
}


static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
			      struct snd_ctl_elem_value *control)
{
	struct snd_kcontrol *kctl;
	struct snd_kcontrol_volatile *vd;
	unsigned int index_offset;
	int result;

	down_write(&card->controls_rwsem);
	kctl = snd_ctl_find_id(card, &control->id);
	if (kctl == NULL) {
		up_write(&card->controls_rwsem);
		return -ENOENT;
	}

	index_offset = snd_ctl_get_ioff(kctl, &control->id);
	vd = &kctl->vd[index_offset];
	if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL ||
	    (file && vd->owner && vd->owner != file)) {
		up_write(&card->controls_rwsem);
		return -EPERM;
	}

	snd_ctl_build_ioff(&control->id, kctl, index_offset);
	result = snd_power_ref_and_wait(card);
	/* validate input values */
	if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION) && !result) {
		struct snd_ctl_elem_info info;

		memset(&info, 0, sizeof(info));
		info.id = control->id;
		result = __snd_ctl_elem_info(card, kctl, &info, NULL);
		if (!result)
			result = sanity_check_input_values(card, control, &info,
							   false);
	}
	if (!result)
		result = kctl->put(kctl, control);
	snd_power_unref(card);
	if (result < 0) {
		up_write(&card->controls_rwsem);
		return result;
	}

	if (result > 0) {
		downgrade_write(&card->controls_rwsem);
		snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, index_offset);
		up_read(&card->controls_rwsem);
	} else {
		up_write(&card->controls_rwsem);
	}

	return 0;
}


static bool check_user_elem_overflow(struct snd_card *card, ssize_t add)
{
	return (ssize_t)card->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
}


static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct snd_ctl_file *ctl;
	struct snd_card *card;
	struct snd_kctl_ioctl *p;
	void __user *argp = (void __user *)arg;
	int __user *ip = argp;
	int err;

	ctl = file->private_data;
	card = ctl->card;
	if (snd_BUG_ON(!card))
		return -ENXIO;
	switch (cmd) {
	case SNDRV_CTL_IOCTL_PVERSION:
		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
	case SNDRV_CTL_IOCTL_CARD_INFO:
		return snd_ctl_card_info(card, ctl, cmd, argp);
	case SNDRV_CTL_IOCTL_ELEM_LIST:
		return snd_ctl_elem_list_user(card, argp);
	case SNDRV_CTL_IOCTL_ELEM_INFO:
		return snd_ctl_elem_info_user(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_READ:
		return snd_ctl_elem_read_user(card, argp);
	case SNDRV_CTL_IOCTL_ELEM_WRITE:
		return snd_ctl_elem_write_user(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_LOCK:
		return snd_ctl_elem_lock(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
		return snd_ctl_elem_unlock(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_ADD:
		return snd_ctl_elem_add_user(ctl, argp, 0);
	case SNDRV_CTL_IOCTL_ELEM_REPLACE:
		return snd_ctl_elem_add_user(ctl, argp, 1);
	case SNDRV_CTL_IOCTL_ELEM_REMOVE:
		return snd_ctl_elem_remove(ctl, argp);
	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
		return snd_ctl_subscribe_events(ctl, ip);
	case SNDRV_CTL_IOCTL_TLV_READ:
		down_read(&ctl->card->controls_rwsem);
		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
		up_read(&ctl->card->controls_rwsem);
		return err;
	case SNDRV_CTL_IOCTL_TLV_WRITE:
		down_write(&ctl->card->controls_rwsem);
		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
		up_write(&ctl->card->controls_rwsem);
		return err;
	case SNDRV_CTL_IOCTL_TLV_COMMAND:
		down_write(&ctl->card->controls_rwsem);
		err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
		up_write(&ctl->card->controls_rwsem);
		return err;
	case SNDRV_CTL_IOCTL_POWER:
		return -ENOPROTOOPT;
	case SNDRV_CTL_IOCTL_POWER_STATE:
		return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
	}
	down_read(&snd_ioctl_rwsem);
	list_for_each_entry(p, &snd_control_ioctls, list) {
		err = p->fioctl(card, ctl, cmd, arg);
		if (err != -ENOIOCTLCMD) {
			up_read(&snd_ioctl_rwsem);
			return err;
		}
	}
	up_read(&snd_ioctl_rwsem);
	dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);
	return -ENOTTY;
}


static bool elem_id_matches(const struct snd_kcontrol *kctl,
			    const struct snd_ctl_elem_id *id)
{
	return kctl->id.iface == id->iface &&
		kctl->id.device == id->device &&
		kctl->id.subdevice == id->subdevice &&
		!strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)) &&
		kctl->id.index <= id->index &&
		kctl->id.index + kctl->count > id->index;
}


int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_info *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
	uinfo->count = 2;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = 1;
	return 0;
}


static int snd_ctl_elem_add_user(struct snd_ctl_file *file,
				 struct snd_ctl_elem_info __user *_info, int replace)
{
	struct snd_ctl_elem_info info;
	int err;

	if (copy_from_user(&info, _info, sizeof(info)))
		return -EFAULT;
	err = snd_ctl_elem_add(file, &info, replace);
	if (err < 0)
		return err;
	if (copy_to_user(_info, &info, sizeof(info))) {
		snd_ctl_remove_user_ctl(file, &info.id);
		return -EFAULT;
	}

	return 0;
}


static int snd_ctl_dev_register(struct snd_device *device)
{
	struct snd_card *card = device->device_data;
	struct snd_ctl_layer_ops *lops;
	int err;

	err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
				  &snd_ctl_f_ops, card, &card->ctl_dev);
	if (err < 0)
		return err;
	down_read(&card->controls_rwsem);
	down_read(&snd_ctl_layer_rwsem);
	for (lops = snd_ctl_layer; lops; lops = lops->next)
		lops->lregister(card);
	up_read(&snd_ctl_layer_rwsem);
	up_read(&card->controls_rwsem);
	return 0;
}


static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
					  unsigned int count)
{
	struct snd_kcontrol *kctl;

	/* Make sure that the ids assigned to the control do not wrap around */
	if (card->last_numid >= UINT_MAX - count)
		card->last_numid = 0;

	list_for_each_entry(kctl, &card->controls, list) {
		if (kctl->id.numid < card->last_numid + 1 + count &&
		    kctl->id.numid + kctl->count > card->last_numid + 1) {
		    	card->last_numid = kctl->id.numid + kctl->count - 1;
			return true;
		}
	}
	return false;
}