[{"data":1,"prerenderedAt":1569},["ShallowReactive",2],{"cfn":3,"$f3emoxk5ko6wb3":961,"$f1wjxujew1lplb":1196},[4,7,10,13,16,19,22,25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,73,76,79,82,85,88,91,94,97,100,103,106,109,112,115,118,121,124,127,130,133,136,139,142,145,148,151,154,157,160,163,166,169,172,175,178,181,184,187,190,193,196,199,202,205,208,211,214,217,220,223,226,229,232,235,238,241,244,247,250,253,256,259,262,265,268,271,274,277,280,283,286,289,292,295,298,301,304,307,310,313,316,319,322,325,328,331,334,337,340,343,346,349,352,355,358,361,364,367,370,373,376,379,382,385,388,391,394,397,400,403,406,409,412,415,418,421,424,427,430,433,436,439,442,445,448,451,454,457,460,463,466,469,472,475,478,481,484,487,490,493,496,499,502,505,508,511,514,517,520,523,526,529,532,535,538,541,544,547,550,553,556,559,562,565,568,571,574,577,580,583,586,589,592,595,598,601,604,607,610,613,616,619,622,625,628,631,634,637,640,643,646,649,652,655,658,661,664,667,670,673,676,679,682,685,688,691,694,697,700,703,706,709,712,715,718,721,724,727,730,733,736,739,742,745,748,751,754,757,760,763,766,769,772,775,778,781,784,787,790,793,796,799,802,805,808,811,814,817,820,823,826,829,832,835,838,841,844,847,850,853,856,859,862,865,868,871,874,877,880,883,886,889,892,895,898,901,904,907,910,913,916,919,922,925,928,931,934,937,940,943,946,949,952,955,958],{"name":5,"code":6},"Amsterdam, Netherlands - (AMS)","AMS",{"name":8,"code":9},"Amman, Jordan - (AMM)","AMM",{"name":11,"code":12},"Adelaide, SA, Australia - (ADL)","ADL",{"name":14,"code":15},"Americana, Brazil - (QWJ)","QWJ",{"name":17,"code":18},"Accra, Ghana - (ACC)","ACC",{"name":20,"code":21},"Ahmedabad, India - (AMD)","AMD",{"name":23,"code":24},"Albuquerque, NM, United States - (ABQ)","ABQ",{"name":26,"code":27},"Auckland, New Zealand - (AKL)","AKL",{"name":29,"code":30},"Athens, Greece - (ATH)","ATH",{"name":32,"code":33},"Arica, Chile - (ARI)","ARI",{"name":35,"code":36},"Algiers, Algeria - (ALG)","ALG",{"name":38,"code":39},"Almaty, Kazakhstan - (ALA)","ALA",{"name":41,"code":42},"Astara, Azerbaijan - (LLK)","LLK",{"name":44,"code":45},"Anchorage, AK, United States - (ANC)","ANC",{"name":47,"code":48},"Ashburn, VA, United States - (IAD)","IAD",{"name":50,"code":51},"Brisbane, QLD, Australia - (BNE)","BNE",{"name":53,"code":54},"Barcelona, Spain - (BCN)","BCN",{"name":56,"code":57},"Baghdad, Iraq - (BGW)","BGW",{"name":59,"code":60},"Annaba, Algeria - (AAE)","AAE",{"name":62,"code":63},"Atlanta, GA, United States - (ATL)","ATL",{"name":65,"code":66},"Belgrade, Serbia - (BEG)","BEG",{"name":68,"code":69},"Baku, Azerbaijan - (GYD)","GYD",{"name":71,"code":72},"Bangalore, India - (BLR)","BLR",{"name":74,"code":75},"Antananarivo, Madagascar - (TNR)","TNR",{"name":77,"code":78},"Canberra, ACT, Australia - (CBR)","CBR",{"name":80,"code":81},"Berlin, Germany - (TXL)","TXL",{"name":83,"code":84},"Bangkok, Thailand - (BKK)","BKK",{"name":86,"code":87},"Cape Town, South Africa - (CPT)","CPT",{"name":89,"code":90},"Belo Horizonte, Brazil - (CNF)","CNF",{"name":92,"code":93},"Basra, Iraq - (BSR)","BSR",{"name":95,"code":96},"Christchurch, New Zealand - (CHC)","CHC",{"name":98,"code":99},"Austin, TX, United States - (AUS)","AUS",{"name":101,"code":102},"Beirut, Lebanon - (BEY)","BEY",{"name":104,"code":105},"Bandar Seri Begawan, Brunei - (BWN)","BWN",{"name":107,"code":108},"Hagatna, Guam - (GUM)","GUM",{"name":110,"code":111},"Bangor, ME, United States - (BGR)","BGR",{"name":113,"code":114},"Bordeaux, France - (BOD)","BOD",{"name":116,"code":117},"Boston, MA, United States - (BOS)","BOS",{"name":119,"code":120},"Dakar, Senegal - (DKR)","DKR",{"name":122,"code":123},"Dammam, Saudi Arabia - (DMM)","DMM",{"name":125,"code":126},"Bratislava, Slovakia   - (BTS)","BTS",{"name":128,"code":129},"Melbourne, VIC, Australia - (MEL)","MEL",{"name":131,"code":132},"Doha, Qatar - (DOH)","DOH",{"name":134,"code":135},"Brussels, Belgium - (BRU)","BRU",{"name":137,"code":138},"Dar Es Salaam, Tanzania - (DAR)","DAR",{"name":140,"code":141},"Brasilia, Brazil - (BSB)","BSB",{"name":143,"code":144},"Buenos Aires, Argentina - (EZE)","EZE",{"name":146,"code":147},"Bucharest, Romania - (OTP)","OTP",{"name":149,"code":150},"Dubai, United Arab Emirates - (DXB)","DXB",{"name":152,"code":153},"Djibouti City, Djibouti - (JIB)","JIB",{"name":155,"code":156},"Calgary, AB, Canada - (YYC)","YYC",{"name":158,"code":159},"Cebu, Philippines - (CEB)","CEB",{"name":161,"code":162},"Noumea, New Caledonia - (NOU)","NOU",{"name":164,"code":165},"Perth, WA, Australia - (PER)","PER",{"name":167,"code":168},"Budapest, Hungary - (BUD)","BUD",{"name":170,"code":171},"Durban, South Africa - (DUR)","DUR",{"name":173,"code":174},"Charlotte, NC, United States - (CLT)","CLT",{"name":176,"code":177},"Erbil, Iraq - (EBL)","EBL",{"name":179,"code":180},"Chandigarh, India - (IXC)","IXC",{"name":182,"code":183},"Chicago, IL, United States - (ORD)","ORD",{"name":185,"code":186},"Sydney, NSW, Australia - (SYD)","SYD",{"name":188,"code":189},"Campinas, Brazil - (VCP)","VCP",{"name":191,"code":192},"Changde, China - (CGD)","CGD",{"name":194,"code":195},"Haifa, Israel - (HFA)","HFA",{"name":197,"code":198},"Gaborone, Botswana - (GBE)","GBE",{"name":200,"code":201},"Copenhagen, Denmark - (CPH)","CPH",{"name":203,"code":204},"Chennai, India - (MAA)","MAA",{"name":206,"code":207},"Harare, Zimbabwe - (HRE)","HRE",{"name":209,"code":210},"Jeddah, Saudi Arabia - (JED)","JED",{"name":212,"code":213},"Tahiti, French Polynesia - (PPT)","PPT",{"name":215,"code":216},"Cleveland, OH, United States - (CLE)","CLE",{"name":218,"code":219},"Johannesburg, South Africa - (JNB)","JNB",{"name":221,"code":222},"Kuwait City, Kuwait - (KWI)","KWI",{"name":224,"code":225},"Dublin, Ireland - (DUB)","DUB",{"name":227,"code":228},"Columbus, OH, United States - (CMH)","CMH",{"name":230,"code":231},"Suva, Fiji - (SUV)","SUV",{"name":233,"code":234},"Dallas, TX, United States - (DFW)","DFW",{"name":236,"code":237},"Manama, Bahrain - (BAH)","BAH",{"name":239,"code":240},"Kigali, Rwanda - (KGL)","KGL",{"name":242,"code":243},"Chittagong, Bangladesh - (CGP)","CGP",{"name":245,"code":246},"Hobart, Australia - (HBA)","HBA",{"name":248,"code":249},"Frankfurt, Germany - (FRA)","FRA",{"name":251,"code":252},"Muscat, Oman - (MCT)","MCT",{"name":254,"code":255},"Denver, CO, United States - (DEN)","DEN",{"name":257,"code":258},"Colombo, Sri Lanka - (CMB)","CMB",{"name":260,"code":261},"Lagos, Nigeria - (LOS)","LOS",{"name":263,"code":264},"Curitiba, Brazil - (CWB)","CWB",{"name":266,"code":267},"Wellington, New Zealand - (WLG)","WLG",{"name":269,"code":270},"Luanda, Angola - (LAD)","LAD",{"name":272,"code":273},"Detroit, MI, United States - (DTW)","DTW",{"name":275,"code":276},"Geneva, Switzerland - (GVA)","GVA",{"name":278,"code":279},"Dhaka, Bangladesh - (DAC)","DAC",{"name":281,"code":282},"Florianopolis, Brazil - (FLN)","FLN",{"name":284,"code":285},"Najaf, Iraq - (NJF)","NJF",{"name":287,"code":288},"Fortaleza, Brazil - (FOR)","FOR",{"name":290,"code":291},"Gothenburg, Sweden - (GOT)","GOT",{"name":293,"code":294},"Maputo, Mozambique - (MPM)","MPM",{"name":296,"code":297},"Foshan, China - (FUO)","FUO",{"name":299,"code":300},"Nasiriyah, Iraq - (XNH)","XNH",{"name":302,"code":303},"Durham, NC, United States - (RDU)","RDU",{"name":305,"code":306},"Mombasa, Kenya - (MBA)","MBA",{"name":308,"code":309},"Hamburg, Germany - (HAM)","HAM",{"name":311,"code":312},"Ramallah - (ZDM)","ZDM",{"name":314,"code":315},"Georgetown, Guyana - (GEO)","GEO",{"name":317,"code":318},"Fukuoka, Japan - (FUK)","FUK",{"name":320,"code":321},"Guadalajara, Mexico - (GDL)","GDL",{"name":323,"code":324},"Helsinki, Finland - (HEL)","HEL",{"name":326,"code":327},"Riyadh, Saudi Arabia - (RUH)","RUH",{"name":329,"code":330},"Nairobi, Kenya - (NBO)","NBO",{"name":332,"code":333},"Fuzhou, China - (FOC)","FOC",{"name":335,"code":336},"Halifax, Canada - (YHZ)","YHZ",{"name":338,"code":339},"Guangzhou, China - (CAN)","CAN",{"name":341,"code":342},"Istanbul, Turkey - (IST)","IST",{"name":344,"code":345},"Guatemala City, Guatemala - (GUA)","GUA",{"name":347,"code":348},"Honolulu, HI, United States - (HNL)","HNL",{"name":350,"code":351},"Sulaymaniyah, Iraq - (ISU)","ISU",{"name":353,"code":354},"Oran, Algeria - (ORN)","ORN",{"name":356,"code":357},"Tel Aviv, Israel - (TLV)","TLV",{"name":359,"code":360},"Houston, TX, United States - (IAH)","IAH",{"name":362,"code":363},"Guayaquil, Ecuador - (GYE)","GYE",{"name":365,"code":366},"Haikou, China - (HAK)","HAK",{"name":368,"code":369},"Ouagadougou, Burkina Faso   - (OUA)","OUA",{"name":371,"code":372},"Izmir, Turkey - (ADB)","ADB",{"name":374,"code":375},"Kyiv, Ukraine - (KBP)","KBP",{"name":377,"code":378},"Port Louis, Mauritius - (MRU)","MRU",{"name":380,"code":381},"Indianapolis, IN, United States - (IND)","IND",{"name":383,"code":384},"Hanoi, Vietnam - (HAN)","HAN",{"name":386,"code":387},"Lisbon, Portugal - (LIS)","LIS",{"name":389,"code":390},"Jacksonville, FL, United States - (JAX)","JAX",{"name":392,"code":393},"Hengshui, China - (SJW)","SJW",{"name":395,"code":396},"Joinville, Brazil - (JOI)","JOI",{"name":398,"code":399},"London, United Kingdom - (LHR)","LHR",{"name":401,"code":402},"Kansas City, MO, United States - (MCI)","MCI",{"name":404,"code":405},"Ho Chi Minh City, Vietnam - (SGN)","SGN",{"name":407,"code":408},"Tunis, Tunisia - (TUN)","TUN",{"name":410,"code":411},"Juazeiro do Norte, Brazil - (JDO)","JDO",{"name":413,"code":414},"Hong Kong - (HKG)","HKG",{"name":416,"code":417},"Lima, Peru - (LIM)","LIM",{"name":419,"code":420},"Luxembourg City, Luxembourg - (LUX)","LUX",{"name":422,"code":423},"Kingston, Jamaica - (KIN)","KIN",{"name":425,"code":426},"Kinshasa, DR Congo - (FIH)","FIH",{"name":428,"code":429},"Las Vegas, NV, United States - (LAS)","LAS",{"name":431,"code":432},"Hyderabad, India - (HYD)","HYD",{"name":434,"code":435},"Manaus, Brazil - (MAO)","MAO",{"name":437,"code":438},"Lyon, France - (LYS)","LYS",{"name":440,"code":441},"Cairo, Egypt - (CAI)","CAI",{"name":443,"code":444},"Los Angeles, CA, United States - (LAX)","LAX",{"name":446,"code":447},"Madrid, Spain - (MAD)","MAD",{"name":449,"code":450},"Islamabad, Pakistan - (ISB)","ISB",{"name":452,"code":453},"Windhoek, Namibia - (WDH)","WDH",{"name":455,"code":456},"Manchester, United Kingdom - (MAN)","MAN",{"name":458,"code":459},"Memphis, TN, United States - (MEM)","MEM",{"name":461,"code":462},"Jakarta, Indonesia - (CGK)","CGK",{"name":464,"code":465},"Yamoussoukro, Ivory Coast - (ASK)","ASK",{"name":467,"code":468},"Marseille, France - (MRS)","MRS",{"name":470,"code":471},"Panama City, Panama - (PTY)","PTY",{"name":473,"code":474},"Mexico City, Mexico - (MEX)","MEX",{"name":476,"code":477},"Abidjan, Ivory Coast - (ABJ)","ABJ",{"name":479,"code":480},"Miami, FL, United States - (MIA)","MIA",{"name":482,"code":483},"Milan, Italy - (MXP)","MXP",{"name":485,"code":486},"Jinan, China - (TNA)","TNA",{"name":488,"code":489},"Paramaribo, Suriname - (PBM)","PBM",{"name":491,"code":492},"Kampala, Uganda - (EBB)","EBB",{"name":494,"code":495},"Minneapolis, MN, United States - (MSP)","MSP",{"name":497,"code":498},"Porto Alegre, Brazil - (POA)","POA",{"name":500,"code":501},"Minsk, Belarus - (MSQ)","MSQ",{"name":503,"code":504},"Moscow, Russia - (DME)","DME",{"name":506,"code":507},"Johor Bahru, Malaysia - (JHB)","JHB",{"name":509,"code":510},"Quito, Ecuador - (UIO)","UIO",{"name":512,"code":513},"Munich, Germany - (MUC)","MUC",{"name":515,"code":516},"Kanpur, India - (KNU)","KNU",{"name":518,"code":519},"Nashville, United States - (BNA)","BNA",{"name":521,"code":522},"Newark, NJ, United States - (EWR)","EWR",{"name":524,"code":525},"Nicosia, Cyprus - (LCA)","LCA",{"name":527,"code":528},"Kaohsiung City - (KHH)","KHH",{"name":530,"code":531},"Recife, Brazil - (REC)","REC",{"name":533,"code":534},"Oslo, Norway - (OSL)","OSL",{"name":536,"code":537},"Norfolk, VA, United States - (ORF)","ORF",{"name":539,"code":540},"Karachi, Pakistan - (KHI)","KHI",{"name":542,"code":543},"Ribeirao Preto, Brazil - (RAO)","RAO",{"name":545,"code":546},"Rio de Janeiro, Brazil - (GIG)","GIG",{"name":548,"code":549},"Kathmandu, Nepal - (KTM)","KTM",{"name":551,"code":552},"Palermo, Italy - (PMO)","PMO",{"name":554,"code":555},"Oklahoma City, OK, United States - (OKC)","OKC",{"name":557,"code":558},"Paris, France - (CDG)","CDG",{"name":560,"code":561},"Omaha, NE, United States - (OMA)","OMA",{"name":563,"code":564},"Prague, Czech Republic - (PRG)","PRG",{"name":566,"code":567},"Kolkata, India - (CCU)","CCU",{"name":569,"code":570},"Santiago, Chile - (SCL)","SCL",{"name":572,"code":573},"Philadelphia, United States - (PHL)","PHL",{"name":575,"code":576},"Lusaka, Zambia - (LUN)","LUN",{"name":578,"code":579},"Kuala Lumpur, Malaysia - (KUL)","KUL",{"name":581,"code":582},"Phoenix, AZ, United States - (PHX)","PHX",{"name":584,"code":585},"Riga, Latvia - (RIX)","RIX",{"name":587,"code":588},"Santo Domingo, Dominican Republic - (SDQ)","SDQ",{"name":590,"code":591},"Addis Ababa, Ethiopia - (ADD)","ADD",{"name":593,"code":594},"Rome, Italy - (FCO)","FCO",{"name":596,"code":597},"Pittsburgh, PA, United States - (PIT)","PIT",{"name":599,"code":600},"Portland, OR, United States - (PDX)","PDX",{"name":602,"code":603},"Saint Petersburg, Russia - (LED)","LED",{"name":605,"code":606},"Langfang, China - (PKX)","PKX",{"name":608,"code":609},"Constantine, Algeria - (CZL)","CZL",{"name":611,"code":612},"Queretaro, MX, Mexico - (QRO)","QRO",{"name":614,"code":615},"Santa Venera, Malta - (MLA)","MLA",{"name":617,"code":618},"Douala, Cameroon - (DLA)","DLA",{"name":620,"code":621},"Richmond, VA, United States - (RIC)","RIC",{"name":623,"code":624},"Sorocaba, Brazil - (SOD)","SOD",{"name":626,"code":627},"Skopje, North Macedonia - (SKP)","SKP",{"name":629,"code":630},"Lilongwe, Malawi - (LLW)","LLW",{"name":632,"code":633},"Sofia, Bulgaria - (SOF)","SOF",{"name":635,"code":636},"Macau - (MFM)","MFM",{"name":638,"code":639},"Sacramento, CA, United States - (SMF)","SMF",{"name":641,"code":642},"Stockholm, Sweden - (ARN)","ARN",{"name":644,"code":645},"Salt Lake City, UT, United States - (SLC)","SLC",{"name":647,"code":648},"Tegucigalpa, Honduras - (TGU)","TGU",{"name":650,"code":651},"Stuttgart, Germany   - (STR)","STR",{"name":653,"code":654},"San Antonio, TX, United States - (SAT)","SAT",{"name":656,"code":657},"Manila, Philippines - (MNL)","MNL",{"name":659,"code":660},"San Diego, CA, United States - (SAN)","SAN",{"name":662,"code":663},"Tallinn, Estonia - (TLL)","TLL",{"name":665,"code":666},"Mumbai, India - (BOM)","BOM",{"name":668,"code":669},"Tbilisi, Georgia - (TBS)","TBS",{"name":671,"code":672},"San Francisco, CA, United States - (SFO)","SFO",{"name":674,"code":675},"Vitoria, Brazil - (VIX)","VIX",{"name":677,"code":678},"San Jose, CA, United States - (SJC)","SJC",{"name":680,"code":681},"Thessaloniki, Greece - (SKG)","SKG",{"name":683,"code":684},"Nagpur, India - (NAG)","NAG",{"name":686,"code":687},"Saskatoon, SK, Canada - (YXE)","YXE",{"name":689,"code":690},"Naha, Japan  - (OKA)","OKA",{"name":692,"code":693},"Tirana, Albania - (TIA)","TIA",{"name":695,"code":696},"Campos dos Goytacazes, Brazil - (CAW)","CAW",{"name":698,"code":699},"Seattle, WA, United States - (SEA)","SEA",{"name":701,"code":702},"Vienna, Austria - (VIE)","VIE",{"name":704,"code":705},"New Delhi, India - (DEL)","DEL",{"name":707,"code":708},"Chapeco, Brazil - (XAP)","XAP",{"name":710,"code":711},"Osaka, Japan - (KIX)","KIX",{"name":713,"code":714},"Vilnius, Lithuania - (VNO)","VNO",{"name":716,"code":717},"Sioux Falls, SD, United States - (FSD)","FSD",{"name":719,"code":720},"Bridgetown, Barbados - (BGI)","BGI",{"name":722,"code":723},"Warsaw, Poland - (WAW)","WAW",{"name":725,"code":726},"Patna, India - (PAT)","PAT",{"name":728,"code":729},"Phnom Penh, Cambodia - (PNH)","PNH",{"name":731,"code":732},"Tallahassee, FL, United States - (TLH)","TLH",{"name":734,"code":735},"Santiago de los Caballeros, Dominican Republic - (STI)","STI",{"name":737,"code":738},"Wroclaw, Poland - (WRO)","WRO",{"name":740,"code":741},"Zagreb, Croatia - (ZAG)","ZAG",{"name":743,"code":744},"Qingdao, China - (TAO)","TAO",{"name":746,"code":747},"Tampa, FL, United States - (TPA)","TPA",{"name":749,"code":750},"La Paz, Bolivia - (LPB)","LPB",{"name":752,"code":753},"Toronto, ON, Canada - (YYZ)","YYZ",{"name":755,"code":756},"Seoul, South Korea - (ICN)","ICN",{"name":758,"code":759},"Zurich, Switzerland - (ZRH)","ZRH",{"name":761,"code":762},"Vancouver, BC, Canada - (YVR)","YVR",{"name":764,"code":765},"Shanghai, China - (SHA)","SHA",{"name":767,"code":768},"San Juan, Puerto Rico - (SJU)","SJU",{"name":770,"code":771},"Singapore, Singapore - (SIN)","SIN",{"name":773,"code":774},"Winnipeg, MB, Canada - (YWG)","YWG",{"name":776,"code":777},"Barranquilla, Colombia - (BAQ)","BAQ",{"name":779,"code":780},"Surat Thani, Thailand - (URT)","URT",{"name":782,"code":783},"Palmas, Brazil - (PMW)","PMW",{"name":785,"code":786},"Cincinnati, United States - (CVG)","CVG",{"name":788,"code":789},"Taipei - (TPE)","TPE",{"name":791,"code":792},"Aracatuba, Brazil - (ARU)","ARU",{"name":794,"code":795},"Port of Spain, Trinidad and Tobago - (POS)","POS",{"name":797,"code":798},"Salvador, Brazil - (SSA)","SSA",{"name":800,"code":801},"Tokyo, Japan - (NRT)","NRT",{"name":803,"code":804},"Ulaanbaatar, Mongolia - (ULN)","ULN",{"name":806,"code":807},"Vientiane, Laos - (VTE)","VTE",{"name":809,"code":810},"Xinyu, China - (KHN)","KHN",{"name":812,"code":813},"Yerevan, Armenia - (EVN)","EVN",{"name":815,"code":816},"Yogyakarta, Indonesia - (JOG)","JOG",{"name":818,"code":819},"Cagayan de Oro, Philippines - (CGY)","CGY",{"name":821,"code":822},"Ljubljana, Slovenia - (LJU)","LJU",{"name":824,"code":825},"Kochi, India - (COK)","COK",{"name":827,"code":828},"Cuiaba, Brazil - (CGB)","CGB",{"name":830,"code":831},"Denpasar, Indonesia - (DPS)","DPS",{"name":833,"code":834},"Kannur, India - (CNN)","CNN",{"name":836,"code":837},"Cali, Colombia - (CLO)","CLO",{"name":839,"code":840},"San Pedro Sula, Honduras - (SAP)","SAP",{"name":842,"code":843},"Shenzhen, China - (SZX)","SZX",{"name":845,"code":846},"Guiyang, China - (KWE)","KWE",{"name":848,"code":849},"Shaoxing, China - (HGH)","HGH",{"name":851,"code":852},"Changzhou, China - (CZX)","CZX",{"name":854,"code":855},"Kunming, China - (KMG)","KMG",{"name":857,"code":858},"Chiang Mai, Thailand - (CNX)","CNX",{"name":860,"code":861},"Zhengzhou, China - (CGO)","CGO",{"name":863,"code":864},"Yangquan, China - (TYN)","TYN",{"name":866,"code":867},"Changsha, China - (CSX)","CSX",{"name":869,"code":870},"Dalian, China - (DLC)","DLC",{"name":872,"code":873},"Chongqing, China - (CKG)","CKG",{"name":875,"code":876},"Xiangyang, China - (XFN)","XFN",{"name":878,"code":879},"Da Nang, Vietnam - (DAD)","DAD",{"name":881,"code":882},"Jiaxing, China - (JXG)","JXG",{"name":884,"code":885},"Tarlac City, Philippines - (CRK)","CRK",{"name":887,"code":888},"Thimphu, Bhutan - (PBH)","PBH",{"name":890,"code":891},"Baoji, China - (XIY)","XIY",{"name":893,"code":894},"Kuching, Malaysia - (KCH)","KCH",{"name":896,"code":897},"Aktobe, Kazakhstan - (AKX)","AKX",{"name":899,"code":900},"Tongren, China - (TEN)","TEN",{"name":902,"code":903},"Taizhou, China - (HYN)","HYN",{"name":905,"code":906},"Bishkek, Kyrgyzstan - (FRU)","FRU",{"name":908,"code":909},"Malang, Indonesia - (MLG)","MLG",{"name":911,"code":912},"Lahore, Pakistan - (LHE)","LHE",{"name":914,"code":915},"Chengdu, China - (CTU)","CTU",{"name":917,"code":918},"Agra, India - (AGR)","AGR",{"name":920,"code":921},"Coimbatore, India - (CJB)","CJB",{"name":923,"code":924},"Xingyi, China - (ACX)","ACX",{"name":926,"code":927},"Bhubaneswar, India - (BBI)","BBI",{"name":929,"code":930},"Luoyang, China - (LYA)","LYA",{"name":932,"code":933},"Pune, India - (PNQ)","PNQ",{"name":935,"code":936},"Sambalpur, India - (JRG)","JRG",{"name":938,"code":939},"Anshun, China - (AVA)","AVA",{"name":941,"code":942},"Astana, Kazakhstan - (NQZ)","NQZ",{"name":944,"code":945},"Jamnagar, India - (BDQ)","BDQ",{"name":947,"code":948},"Udaipur, India - (UDR)","UDR",{"name":950,"code":951},"Jalandhar, India - (AIP)","AIP",{"name":953,"code":954},"Lanzhou, China - (LHW)","LHW",{"name":956,"code":957},"Dushanbe, Tajikistan - (DYU)","DYU",{"name":959,"code":960},"Ludhiana, India - (LUH)","LUH",[962,1028,1088,1146],{"slug":963,"title":964,"lang":965,"desc":966,"date":967,"tags":968,"edit":967,"aiGenerated":969,"aiReview":970,"aiTranslated":969,"readingTime":971,"toc":976,"content":1027},"ace5-back-to-coloros","Flashing a OnePlus Ace 5 Back to ColorOS 16 from an Unofficial ROM, and Fixing the Camera Along the Way","en","A full write-up of flashing back to ColorOS 16 from an unofficial ROM: three failures, a camera crash, and the unavoidable mess around root and OTA.","2026-10-19T00:00:00.000Z",[],true,"基本核对",{"text":972,"minutes":973,"time":974,"words":975},"11 min read",10.805,648300,2161,[977,981,984,987,990,993,996,999,1002,1005,1009,1012,1015,1018,1021,1024],{"level":978,"text":979,"slug":980},2,"Background","background",{"level":978,"text":982,"slug":983},"Two iron rules","two-iron-rules",{"level":978,"text":985,"slug":986},"Verifying the package: check the version without extracting 8 GB","verifying-the-package-check-the-version-without-extracting-8-gb",{"level":978,"text":988,"slug":989},"Identifying images: don't mistake EROFS for a corrupt package","identifying-images-don-t-mistake-erofs-for-a-corrupt-package",{"level":978,"text":991,"slug":992},"Flashing: two modes, each doing its own job","flashing-two-modes-each-doing-its-own-job",{"level":978,"text":994,"slug":995},"Trap 1: modem won't accept writes in fastbootd","trap-1-modem-won-t-accept-writes-in-fastbootd",{"level":978,"text":997,"slug":998},"Trap 2: OnePlus's `my_*` partitions don't exist at all","trap-2-oneplus-s-my_-partitions-don-t-exist-at-all",{"level":978,"text":1000,"slug":1001},"Trap 3: `getvar` has three ways of saying \"doesn't exist\"","trap-3-getvar-has-three-ways-of-saying-doesn-t-exist",{"level":978,"text":1003,"slug":1004},"The flash-back is done, now what","the-flash-back-is-done-now-what",{"level":1006,"text":1007,"slug":1008},3,"Root disappears after an OTA","root-disappears-after-an-ota",{"level":1006,"text":1010,"slug":1011},"Re-rooting: the window of opportunity","re-rooting-the-window-of-opportunity",{"level":1006,"text":1013,"slug":1014},"The easier approach: install to the unused slot","the-easier-approach-install-to-the-unused-slot",{"level":978,"text":1016,"slug":1017},"Camera crash: the real long tail","camera-crash-the-real-long-tail",{"level":1006,"text":1019,"slug":1020},"Why is `my_preload` missing in particular?","why-is-my_preload-missing-in-particular",{"level":1006,"text":1022,"slug":1023},"Rescue: extract just one partition","rescue-extract-just-one-partition",{"level":978,"text":1025,"slug":1026},"A few random thoughts","a-few-random-thoughts","\u003Ch2 id=\"background\">Background\u003C\u002Fh2>\n\u003Cp>After nearly two months as an Apple ecosystem user, I finally decided to leave the Apple ecosystem and flash my old backup phone — this China-market OnePlus Ace 5 (codename \u003Ccode>giuliac\u003C\u002Fcode>, model \u003Ccode>PKG110\u003C\u002Fcode>) — back to ColorOS 16 as a stopgap while waiting for the new phone to go on sale.\u003C\u002Fp>\n\u003Cp>At first I thought the job would be nothing more than &quot;download a package, boot into fastboot, flash, reboot.&quot; But the part that actually took time wasn't the flashing — it was \u003Cstrong>figuring out why the flash kept failing\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>This post records the whole process, tidies up the root and OTA situation along the way, and finally covers the camera crash that kept me up until the early morning.\u003C\u002Fp>\n\u003Ch2 id=\"two-iron-rules\">Two iron rules\u003C\u002Fh2>\n\u003Cp>Let me put the conclusions first. These two rules cost me real money this time:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Firmware partitions are flashed in the bootloader; logical partitions are flashed in fastbootd.\u003C\u002Fstrong> Mixing them produces the paranormal symptom of &quot;the partition is clearly there, but it simply won't accept writes.&quot;\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Leave relocking for last.\u003C\u002Fstrong> Only consider \u003Ccode>fastboot flashing lock\u003C\u002Fcode> after the phone boots, everything works, and OTA detection succeeds. I did not relock this time; the bootloader remains unlocked.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"verifying-the-package-check-the-version-without-extracting-8-gb\">Verifying the package: check the version without extracting 8 GB\u003C\u002Fh2>\n\u003Cp>The package I had was a full ColorOS 16 OTA: 8.7 GB, 51 partitions. Before flashing, I wanted to confirm the package was correct, but I didn't want to extract it first.\u003C\u002Fp>\n\u003Cp>Fortunately, \u003Ccode>payload.bin\u003C\u002Fcode> is stored \u003Cstrong>uncompressed\u003C\u002Fstrong> in the OTA zip, so it can be read directly by offset:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># Read the metadata inside the zip first\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">tar \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">xOf .\\86d1d2fe214c4a87804106baa2d4af4a.zip \u003C\u002Fspan>\u003Cspan style=\"color:#98C379\">'META-INF\u002Fcom\u002Fandroid\u002Fmetadata'\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># The starting offset of payload.bin is given by ota-property-files in the metadata\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># Read the protobuf manifest at the beginning byte by byte, then whole-word match partition names\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Only a few fields matter: \u003Ccode>android_version\u003C\u002Fcode>, \u003Ccode>version_name\u003C\u002Fcode>, \u003Ccode>product_name\u003C\u002Fcode>, \u003Ccode>pre-device\u003C\u002Fcode>, \u003Ccode>post-build\u003C\u002Fcode>. This phone's \u003Ccode>pre-device\u003C\u002Fcode> is \u003Ccode>OP5D2BL1\u003C\u002Fcode>; only after it matched did I dare proceed.\u003C\u002Fp>\n\u003Cp>I used \u003Ccode>payload-dumper-go\u003C\u002Fcode> to extract:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">.\\\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">payload-dumper-go.exe\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">c \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">8\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">o .\\cos16_img .\\86d1d2fe214c4a87804106baa2d4af4a.zip\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The payload lists 51 partitions, but the dumper only produced 48 images. \u003Ccode>checkpoint_gc\u003C\u002Fcode>, \u003Ccode>otapreopt_script\u003C\u002Fcode>, and \u003Ccode>qti_dynamic_partitions\u003C\u002Fcode> should never have been flashed directly anyway.\u003C\u002Fp>\n\u003Ch2 id=\"identifying-images-don-t-mistake-erofs-for-a-corrupt-package\">Identifying images: don't mistake EROFS for a corrupt package\u003C\u002Fh2>\n\u003Cblockquote>\n\u003Cp>I didn't do this section by hand; it came up when I had \u003Ca href=\"https:\u002F\u002Fomp.sh\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">omp\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" viewBox=\"0 0 24 24\" width=\"1em\" height=\"1em\" fill=\"currentColor\" aria-hidden=\"true\" style=\"display:inline;vertical-align:middle;margin-left:2px;\">\u003Cpath d=\"M5 21q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h7v2H5v14h14v-7h2v7q0 .825-.587 1.413T19 21zm4.7-5.3l-1.4-1.4L17.6 5H14V3h7v7h-2V6.4z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fa> verify the images during the flashing process.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>After extracting, I wanted to verify them, and almost concluded I had downloaded a corrupt package.\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Image\u003C\u002Fth>\n\u003Cth>Offset\u003C\u002Fth>\n\u003Cth>Magic\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>boot\u003C\u002Fcode> \u002F \u003Ccode>init_boot\u003C\u002Fcode> \u002F \u003Ccode>recovery\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>414E44524F494421\u003C\u002Fcode> (\u003Ccode>ANDROID!\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>vendor_boot\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>564E4452424F4F54\u003C\u002Fcode> (\u003Ccode>VNDRBOOT\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>dtbo\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>D7B7AB1E\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>vbmeta*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>41564230\u003C\u002Fcode> (\u003Ccode>AVB0\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>system\u003C\u002Fcode> \u002F \u003Ccode>vendor\u003C\u002Fcode> \u002F \u003Ccode>odm\u003C\u002Fcode> \u002F \u003Ccode>my_*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>1024\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>E2E1F5E0\u003C\u002Fcode> (\u003Cstrong>EROFS\u003C\u002Fstrong>)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>The trap is in the last row: Android 16's system partitions are \u003Cstrong>EROFS\u003C\u002Fstrong>, not ext4. If you look for ext4's \u003Ccode>53EF\u003C\u002Fcode>, you'll only conclude that &quot;all the images are broken.&quot; Also, \u003Ccode>dtbo\u003C\u002Fcode>'s \u003Ccode>D7B7AB1E\u003C\u002Fcode> looks a bit like ext4's magic, so don't mix them up.\u003C\u002Fp>\n\u003Ch2 id=\"flashing-two-modes-each-doing-its-own-job\">Flashing: two modes, each doing its own job\u003C\u002Fh2>\n\u003Cp>After entering fastbootd, confirm identity first:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot devices\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar product      \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># expect OP5D2BL1 \u002F giuliac \u002F PKG110\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar is\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">userspace \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># expect yes\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar current\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># expect a\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Then flash one by one. Logical partitions and most physical partitions accept \u003Ccode>--slot=all\u003C\u002Fcode>, but this phone's logical partitions \u003Cstrong>only exist on \u003Ccode>_a\u003C\u002Fcode>\u003C\u002Fstrong>, so using \u003Ccode>--slot=all\u003C\u002Fcode> on that group is rejected; fall back to the single slot.\u003C\u002Fp>\n\u003Cp>After flashing, wipe data and boot for the first time:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot bootloader\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">w\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot erase metadata\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"trap-1-modem-won-t-accept-writes-in-fastbootd\">Trap 1: modem won't accept writes in fastbootd\u003C\u002Fh2>\n\u003Cp>The first error that made me question my life:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Sending sparse 'modem_a' 1\u002F1 (219896 KB)   OKAY\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Writing 'modem_a'                          FAILED (remote: 'No such file or directory')\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>getvar\u003C\u002Fcode> was even more confusing:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>partition-size:modem_a  → FAILED (remote: 'Could not open partition')\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>partition-type:modem_a  → raw        ← this one actually had a value\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>is-logical:modem_a      → FAILED (remote: 'Partition not found')\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The partition appears to &quot;exist&quot;, yet it just won't accept writes.\u003C\u002Fp>\n\u003Cp>The root cause is simple: \u003Cstrong>fastbootd runs in userspace and has no block device node for \u003Ccode>modem_a\u003C\u002Fcode>\u003C\u002Fstrong>; the partition isn't broken. Switching back to the bootloader and checking, \u003Ccode>partition-size:modem_a\u003C\u002Fcode> is \u003Ccode>0x15E00000\u003C\u002Fcode>, perfectly fine.\u003C\u002Fp>\n\u003Cp>So modem must be flashed in the bootloader:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot bootloader\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a modem modem.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>First try.\u003C\u002Fp>\n\u003Ch2 id=\"trap-2-oneplus-s-my_-partitions-don-t-exist-at-all\">Trap 2: OnePlus's \u003Ccode>my_*\u003C\u002Fcode> partitions don't exist at all\u003C\u002Fh2>\n\u003Cp>The second trap was even more absurd. All eight \u003Ccode>my_*\u003C\u002Fcode> partitions failed to flash, and at first I thought super space was insufficient.\u003C\u002Fp>\n\u003Cp>Then I realized: \u003Cstrong>when flashing the unofficial ROM, the super partition table had been rebuilt following the AOSP layout\u003C\u002Fstrong>, leaving only the standard layout of \u003Ccode>system\u003C\u002Fcode>, \u003Ccode>system_ext\u003C\u002Fcode>, \u003Ccode>product\u003C\u002Fcode>, \u003Ccode>vendor\u003C\u002Fcode>, \u003Ccode>odm\u003C\u002Fcode>, \u003Ccode>system_dlkm\u003C\u002Fcode>, \u003Ccode>vendor_dlkm\u003C\u002Fcode>. OnePlus's own \u003Ccode>my_product\u003C\u002Fcode>, \u003Ccode>my_stock\u003C\u002Fcode>, \u003Ccode>my_bigball\u003C\u002Fcode>, \u003Ccode>my_carrier\u003C\u002Fcode>, \u003Ccode>my_engineering\u003C\u002Fcode>, \u003Ccode>my_heytap\u003C\u002Fcode>, \u003Ccode>my_manifest\u003C\u002Fcode>, \u003Ccode>my_region\u003C\u002Fcode> were never created.\u003C\u002Fp>\n\u003Cp>It wasn't a lack of space; the partitions \u003Cstrong>simply didn't exist\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>The fix is to create them first, then flash:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot create\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">logical\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">partition my_stock_a \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0x100000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a my_stock my_stock.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The placeholder size can be arbitrary; \u003Ccode>fastboot flash\u003C\u002Fcode> will resize it to the image's actual size.\u003C\u002Fp>\n\u003Cp>Let me also note this phone's final sizes, in case they're useful later:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>my_product      1520168960 → 0x5A9BF000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_stock        4491419648 → 0x10BB5A000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_region        7057408  → 0x6BB000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_manifest       729088  → 0xB2000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>modem           314667008 → 0x15E00000\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"trap-3-getvar-has-three-ways-of-saying-doesn-t-exist\">Trap 3: \u003Ccode>getvar\u003C\u002Fcode> has three ways of saying &quot;doesn't exist&quot;\u003C\u002Fh2>\n\u003Cp>This one isn't fatal, but it's very easy to misjudge. I calibrated the semantics with a partition name that definitely doesn't exist:\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Query\u003C\u002Fth>\n\u003Cth>Partition exists\u003C\u002Fth>\n\u003Cth>Partition missing\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>partition-type:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>raw\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Invalid partition')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>partition-size:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>0x...\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Could not open partition')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>has-slot:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>yes\u003C\u002Fcode> \u002F \u003Ccode>no\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>no\u003C\u002Fcode> (\u003Cstrong>no error\u003C\u002Fstrong>)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>is-logical:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>yes\u003C\u002Fcode> \u002F \u003Ccode>no\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Partition not found')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>So to determine whether a partition exists, \u003Cstrong>look only at \u003Ccode>partition-type\u003C\u002Fcode> and \u003Ccode>is-logical\u003C\u002Fcode>\u003C\u002Fstrong>. The \u003Ccode>has-slot\u003C\u002Fcode> behavior of returning \u003Ccode>no\u003C\u002Fcode> even when the partition doesn't exist is a merciless trap.\u003C\u002Fp>\n\u003Ch2 id=\"the-flash-back-is-done-now-what\">The flash-back is done, now what\u003C\u002Fh2>\n\u003Cp>After flashing, the phone booted, and calls, SMS, dual SIM, Wi-Fi, Bluetooth, NFC, fingerprint, and charging all worked; the third-party camera worked too. The strongest integrity evidence was that \u003Cstrong>system updates could detect an OTA\u003C\u002Fstrong>. At this point, the flash-back was a success.\u003C\u002Fp>\n\u003Cp>Let me also lay out the relationship between root and OTA here, so I don't forget next time.\u003C\u002Fp>\n\u003Ch3 id=\"root-disappears-after-an-ota\">Root disappears after an OTA\u003C\u002Fh3>\n\u003Cp>An OTA overwrites \u003Ccode>boot\u003C\u002Fcode>, so KernelSU's patch disappears too. That's not a risk; just patch again.\u003C\u002Fp>\n\u003Ch3 id=\"re-rooting-the-window-of-opportunity\">Re-rooting: the window of opportunity\u003C\u002Fh3>\n\u003Cp>After flashing back to ColorOS, the latest official OTA update arrived quickly. After installing it, don't tap &quot;restart now&quot; yet — at this point the new image has already been written to the other slot, while root remains on the old system. Just dump it:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb shell su \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">c \u003C\u002Fspan>\u003Cspan style=\"color:#98C379\">\"dd if=\u002Fdev\u002Fblock\u002Fby-name\u002Fboot_b of=\u002Fdata\u002Flocal\u002Ftmp\u002Fboot_b.img\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb pull \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">data\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">local\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">tmp\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">boot_b.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The advantage is that you don't have to wait for a full package, and the image is guaranteed to match. The disadvantage is that \u003Cstrong>the window closes as soon as you reboot\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>The Ace 5 is an Android 13+ GKI device; KernelSU patches \u003Ccode>boot\u003C\u002Fcode>, so don't flash it to \u003Ccode>init_boot\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch3 id=\"the-easier-approach-install-to-the-unused-slot\">The easier approach: install to the unused slot\u003C\u002Fh3>\n\u003Cp>KernelSU Manager has an &quot;install to unused slot (after OTA)&quot; option that writes the patched image directly into the other slot; after rebooting you're on the new system with root. Much easier than manual dd.\u003C\u002Fp>\n\u003Cp>The cost is that it won't leave a backup of the stock image on your computer. If something goes wrong and you want to roll back, you won't have that dumped \u003Ccode>boot\u003C\u002Fcode>. So this time I still manually dd'd a copy first, at least for peace of mind.\u003C\u002Fp>\n\u003Cp>One more small detail: under KernelSU, not finding \u003Ccode>ls \u002Fsystem\u002Fbin\u002Fsu\u003C\u002Fcode> is \u003Cstrong>normal\u003C\u002Fstrong>. It relies on an execve hook and doesn't actually place a su file. Don't use that to judge whether root is gone.\u003C\u002Fp>\n\u003Ch2 id=\"camera-crash-the-real-long-tail\">Camera crash: the real long tail\u003C\u002Fh2>\n\u003Cp>The system was flashed and the root situation was clear, but when I opened the stock camera — \u003Cstrong>instant crash\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>Third-party cameras worked fine, meaning the camera hardware, HAL, and permissions were all fine. The crash log was very consistent:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>java.lang.UnsatisfiedLinkError: dlopen failed: library \"libCombineLutJni.so\" not found\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.camera.combinelut.CombineLutJNI.&#x3C;clinit>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.ocs.camera.CombineLutHelper.&#x3C;init>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.camera.common.utils.Lut3dDataManager.&#x3C;init>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Loading the native library failed. So I first looked for where the library was:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>$ adb shell find \u002F -name libCombineLutJni.so\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u002Fmy_product\u002Flib64\u002FlibCombineLutJni.so\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The library is in \u003Ccode>\u002Fmy_product\u002Flib64\u003C\u002Fcode>, but the camera is a PRODUCT App, and its linker namespace only recognizes \u003Ccode>\u002Fproduct\u002Flib64:\u002Fsystem\u002Fproduct\u002Flib64\u003C\u002Fcode>; it won't look directly at \u003Ccode>\u002Fmy_product\u002Flib64\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>Normally an overlay should merge the two. \u003Ccode>\u002Fvendor\u002Fetc\u002Ffstab.qcom\u003C\u002Fcode> indeed contains:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlay-overlay \u002Fproduct\u002Flib64 overlay ... lowerdir=\u002Fmy_region\u002Flib64:\u002Fmy_preload\u002Flib64:\u002Fmy_product\u002Flib64:\u002Fmy_heytap\u002Flib64:\u002Fmy_stock\u002Flib64:\u002Fmy_engineering\u002Flib64:\u002Fproduct\u002Flib64\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>But checking \u003Ccode>\u002Fproc\u002Fmounts\u003C\u002Fcode>, \u003Cstrong>all four overlays\u003C\u002Fstrong> — \u003Ccode>\u002Fproduct\u002Fapp\u003C\u002Fcode>, \u003Ccode>\u002Fproduct\u002Fpriv-app\u003C\u002Fcode>, \u003Ccode>\u002Fproduct\u002Flib64\u003C\u002Fcode>, \u003Ccode>\u002Fproduct\u002Flib\u003C\u002Fcode> — \u003Cstrong>were not mounted\u003C\u002Fstrong>. In the same fstab, \u003Ccode>\u002Fproduct\u002Fetc\u002Fpermissions\u003C\u002Fcode> and \u003Ccode>\u002Fproduct\u002Fframework\u003C\u002Fcode> were fine.\u003C\u002Fp>\n\u003Cp>I reproduced the mount manually, and the kernel told me why:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlayfs: failed to resolve '\u002Fmy_preload\u002Flib64': -2\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>-2\u003C\u002Fcode> is ENOENT. \u003Cstrong>The \u003Ccode>\u002Fmy_preload\u003C\u002Fcode> partition does not exist.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>Removing it and mounting again succeeded immediately; \u003Ccode>\u002Fproduct\u002Flib64\u002FlibCombineLutJni.so\u003C\u002Fcode> appeared, and the camera launched.\u003C\u002Fp>\n\u003Cp>The full causal chain:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>my_preload partition missing\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → all four overlays under \u002Fproduct\u002F{app,priv-app,lib64,lib} fail to mount\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → \u002Fproduct\u002Flib64 cannot see the libraries under \u002Fmy_product\u002Flib64\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → the camera fails to load libCombineLutJni.so → crash\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The fstab actually contains \u003Ccode>overlayfs_remove_missing_lowerdir\u003C\u002Fcode>, which should automatically skip missing directories. But the boot path on this device executes the raw lowerdir list; that filter function is never called, so a missing directory is a hard ENOENT.\u003C\u002Fp>\n\u003Ch3 id=\"why-is-my_preload-missing-in-particular\">Why is \u003Ccode>my_preload\u003C\u002Fcode> missing in particular?\u003C\u002Fh3>\n\u003Cp>Back to the earlier trap: when flashing the unofficial ROM, the super partition table was rebuilt following the AOSP layout, and the stock \u003Ccode>my_preload\u003C\u002Fcode> and \u003Ccode>my_company\u003C\u002Fcode> were deleted along with it. When flashing back, I only created partitions that exist in the full ColorOS 16 OTA, and \u003Cstrong>the full package doesn't include \u003Ccode>my_preload\u003C\u002Fcode>\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Cp>These two are OPlus preinstalled partitions (the community calls them &quot;双广分区&quot;, the Shuāngguǎng partitions); they aren't updated by full OTAs and only exist in downgrade packages \u002F rollback packages \u002F factory images.\u003C\u002Fp>\n\u003Cp>There's still evidence on this device: \u003Ccode>\u002Fmy_manifest\u002Fetc\u002Fmy_preload.map\u003C\u002Fcode> is full of \u003Ccode>\u002Fmy_preload\u002Fdel-app\u002F...\u003C\u002Fcode> preinstall lists, proving the factory layout originally had it.\u003C\u002Fp>\n\u003Ch3 id=\"rescue-extract-just-one-partition\">Rescue: extract just one partition\u003C\u002Fh3>\n\u003Cp>I downloaded an old full downgrade package \u003Ccode>PKG110_16.0.9.400(CN01)\u003C\u002Fcode>, which contains \u003Ccode>my_preload\u003C\u002Fcode> (1.8 GB) and \u003Ccode>my_company\u003C\u002Fcode> (336 kB).\u003C\u002Fp>\n\u003Cp>Note: \u003Cstrong>extract only the partitions, don't flash the whole package\u003C\u002Fstrong>. This package is older than the current system and carries \u003Ccode>wipe=1\u003C\u002Fcode> and \u003Ccode>ota-downgrade=yes\u003C\u002Fcode>; flashing it wholesale would be deliberately stepping on ARB.\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">.\\\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">payload-dumper-go.exe\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">p my_preload \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">o .\\my_preload_16.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0.9\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">400\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> .\\670f63e2a29741578637222c6b7be73b.zip\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The extracted image is 1,784,397,824 bytes, EROFS, with an AVB public key SHA-1 of \u003Ccode>1a130f9a...66f6\u003C\u002Fcode>, exactly matching \u003Ccode>\u002Fvendor\u002Fetc\u002Foplus_avb.pubkey\u003C\u002Fcode> on the device — official signature, passes \u003Ccode>avb_keys\u003C\u002Fcode> verification.\u003C\u002Fp>\n\u003Cp>Before flashing, check the space: super's total capacity is 13.58 GiB, about 10.0 GiB used, 3.58 GiB left, plenty for 1.66 GiB.\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb reboot fastboot\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar is\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">userspace   \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># expect yes\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot create\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">logical\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">partition my_preload_a \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1784397824\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a my_preload .\\my_preload_16.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0.9\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">400\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">\\my_preload.img\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>After flashing, \u003Ccode>my_preload_a\u003C\u002Fcode> mounts on dm-verity (\u003Ccode>dm-29\u003C\u002Fcode>, erofs), meaning the hashtree verification passed. Checking the overlay again:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlay-overlay \u002Fproduct\u002Flib64 ... lowerdir=\u002Fmy_region\u002Flib64:\u002Fmy_preload\u002Flib64:\u002Fmy_product\u002Flib64:...\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>All four overlays are back, the library is visible, the camera process stays alive after launch, \u003Ccode>dumpsys media.camera\u003C\u002Fcode> shows an active session, and there are no new records in the crash buffer.\u003C\u002Fp>\n\u003Cp>Done.\u003C\u002Fp>\n\u003Ch2 id=\"a-few-random-thoughts\">A few random thoughts\u003C\u002Fh2>\n\u003Cp>The biggest takeaway from this whole ordeal: \u003Cstrong>the hard part of flashing isn't the flashing; it's understanding &quot;why this partition won't accept writes.&quot;\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>A few concrete lessons:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Don't just look at the error; look at the pattern behind it.\u003C\u002Fstrong> \u003Ccode>modem\u003C\u002Fcode> failing to write and \u003Ccode>my_*\u003C\u002Fcode> failing to write both produce ambiguous errors, but one is because fastbootd has no node, and the other is because the partition simply doesn't exist. The solutions are completely different.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>The &quot;other slot&quot; on A\u002FB devices is a free time machine.\u003C\u002Fstrong> Don't rush to reboot after an OTA install; dump the new image first, and you'll save the hours spent waiting for a full package later.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>On a device whose super has been rebuilt, the road back to stock is more than &quot;flash the official package.&quot;\u003C\u002Fstrong> Partitions absent from the official full package have to be fished out of a downgrade package separately.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Remember to keep \u003Ccode>my_preload.img\u003C\u002Fcode>.\u003C\u002Fstrong> Any time super is rebuilt (flashing an unofficial ROM again, full-package flashing, reflashing before relocking), you'll need to recreate and flash this partition, or the camera will crash again. Regular OTAs won't overwrite it, so don't worry.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Finally, flashing carries risk; bricking and data loss are your own responsibility. I actually ran all the commands, but your device may not be exactly the same as mine. Back up before you start.\u003C\u002Fp>\n\u003Cp>That's it. I'm going to sleep.\u003C\u002Fp>\n",{"slug":963,"title":1029,"lang":1030,"desc":1031,"date":967,"tags":1032,"edit":967,"aiGenerated":969,"aiReview":970,"aiTranslated":969,"readingTime":1033,"toc":1038,"content":1087},"OnePlus Ace 5 を非公式 ROM から ColorOS 16 に戻し、ついでにカメラも復活させた話","ja","非公式 ROM から ColorOS 16 に戻した全記録：3 度の失敗、カメラのクラッシュ、そして root と OTA の避けて通れない話。",[],{"text":1034,"minutes":1035,"time":1036,"words":1037},"18 min read",17.34,1040400,3468,[1039,1042,1045,1048,1051,1054,1057,1060,1063,1066,1069,1072,1075,1078,1081,1084],{"level":978,"text":1040,"slug":1041},"きっかけ","",{"level":978,"text":1043,"slug":1044},"2 つの鉄則","2-鉄則",{"level":978,"text":1046,"slug":1047},"パッケージ検証：8GB を展開せずにバージョンを見る","検証-8gb-展開-見",{"level":978,"text":1049,"slug":1050},"イメージの見分け：EROFS を壊れたパッケージと間違えるな","見分-erofs-壊-間違",{"level":978,"text":1052,"slug":1053},"書き込み：2 つのモード、それぞれの役割","書-込-2-役割",{"level":978,"text":1055,"slug":1056},"罠 1：modem は fastbootd で書き込めない","罠-1-modem-fastbootd-書-込",{"level":978,"text":1058,"slug":1059},"罠 2：OnePlus の `my_*` パーティションはそもそも存在しない","罠-2-oneplus-my_-存在",{"level":978,"text":1061,"slug":1062},"罠 3：`getvar` の「存在しない」には 3 通りの言い方がある","罠-3-getvar-存在-3-通-言-方",{"level":978,"text":1064,"slug":1065},"戻し終わった、それで？","戻-終",{"level":1006,"text":1067,"slug":1068},"OTA のあと root は消える","ota-root-消",{"level":1006,"text":1070,"slug":1071},"再 root：猶予の窓","再-root-猶予-窓",{"level":1006,"text":1073,"slug":1074},"もっと楽な方法：未使用スロットにインストール","楽-方法-未使用",{"level":978,"text":1076,"slug":1077},"カメラのクラッシュ：本当の長い尾","本当-長-尾",{"level":1006,"text":1079,"slug":1080},"なぜ `my_preload` だけが欠けているのか","my_preload-欠",{"level":1006,"text":1082,"slug":1083},"救出：パーティションを 1 つだけ取り出す","救出-1-取-出",{"level":978,"text":1085,"slug":1086},"いくつかの雑感","雑感","\u003Ch2 id=\"\">きっかけ\u003C\u002Fh2>\n\u003Cp>約 2 か月間 Apple エコシステムのユーザーをやったあと、結局 Apple エコシステムを手放すことにして、もとのサブ機 —— この OnePlus Ace 5 中国版（コードネーム \u003Ccode>giuliac\u003C\u002Fcode>、型番 \u003Ccode>PKG110\u003C\u002Fcode>）—— を ColorOS 16 に戻してつなぎで使おうと考えた。新しい端末が発売されたら、そのときに改めて決めるつもりだ。\u003C\u002Fp>\n\u003Cp>最初は「パッケージを落として、fastboot に入って、焼いて再起動するだけ」くらいに思っていた。ところが実際に時間を食ったのは焼くことではなく、\u003Cstrong>なぜ焼けないのかを突き止めること\u003C\u002Fstrong>だった。\u003C\u002Fp>\n\u003Cp>この記事では一連の流れを記録し、ついでに root と OTA の話も整理する。最後に、明け方まで格闘することになったカメラのクラッシュについても書く。\u003C\u002Fp>\n\u003Ch2 id=\"2-鉄則\">2 つの鉄則\u003C\u002Fh2>\n\u003Cp>結論を先に書く。この 2 つは今回、実際に痛い目を見て得たものだ：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>ファームウェアパーティションは bootloader で、論理パーティションは fastbootd で焼く\u003C\u002Fstrong>。混ぜると「パーティションは確かにあるのに、書き込めない」という怪奇現象が起きる。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>再ロックは最後\u003C\u002Fstrong>。起動できて、機能が正常で、OTA を検出できてから初めて \u003Ccode>fastboot flashing lock\u003C\u002Fcode> を検討する。今回は再ロックせず、bootloader は unlocked のままにした。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"検証-8gb-展開-見\">パッケージ検証：8GB を展開せずにバージョンを見る\u003C\u002Fh2>\n\u003Cp>手元のパッケージは ColorOS 16 のフル OTA で、8.7 GB、51 パーティション。焼く前にパッケージが正しいか確認したいが、先に展開するのは面倒だ。\u003C\u002Fp>\n\u003Cp>幸い \u003Ccode>payload.bin\u003C\u002Fcode> は OTA zip の中で\u003Cstrong>非圧縮で保存\u003C\u002Fstrong>されているので、オフセットから直接読める：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># まず zip 内の metadata を読む\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">tar \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">xOf .\\86d1d2fe214c4a87804106baa2d4af4a.zip \u003C\u002Fspan>\u003Cspan style=\"color:#98C379\">'META-INF\u002Fcom\u002Fandroid\u002Fmetadata'\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># payload.bin の開始オフセットは metadata の ota-property-files で示される\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># 先頭の protobuf manifest をバイト単位で読み、パーティション名を完全一致で照合する\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>見るべきフィールドはわずか：\u003Ccode>android_version\u003C\u002Fcode>、\u003Ccode>version_name\u003C\u002Fcode>、\u003Ccode>product_name\u003C\u002Fcode>、\u003Ccode>pre-device\u003C\u002Fcode>、\u003Ccode>post-build\u003C\u002Fcode>。この端末の \u003Ccode>pre-device\u003C\u002Fcode> は \u003Ccode>OP5D2BL1\u003C\u002Fcode> で、一致したので先に進めた。\u003C\u002Fp>\n\u003Cp>展開には \u003Ccode>payload-dumper-go\u003C\u002Fcode> を使った：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">.\\\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">payload-dumper-go.exe\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">c \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">8\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">o .\\cos16_img .\\86d1d2fe214c4a87804106baa2d4af4a.zip\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>payload には 51 パーティションが書かれているが、dumper が生成したイメージは 48 個だけ。\u003Ccode>checkpoint_gc\u003C\u002Fcode>、\u003Ccode>otapreopt_script\u003C\u002Fcode>、\u003Ccode>qti_dynamic_partitions\u003C\u002Fcode> の 3 つはそもそも直接焼くべきものではない。\u003C\u002Fp>\n\u003Ch2 id=\"見分-erofs-壊-間違\">イメージの見分け：EROFS を壊れたパッケージと間違えるな\u003C\u002Fh2>\n\u003Cblockquote>\n\u003Cp>この節は手作業ではなく、焼いている最中に \u003Ca href=\"https:\u002F\u002Fomp.sh\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">omp\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" viewBox=\"0 0 24 24\" width=\"1em\" height=\"1em\" fill=\"currentColor\" aria-hidden=\"true\" style=\"display:inline;vertical-align:middle;margin-left:2px;\">\u003Cpath d=\"M5 21q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h7v2H5v14h14v-7h2v7q0 .825-.587 1.413T19 21zm4.7-5.3l-1.4-1.4L17.6 5H14V3h7v7h-2V6.4z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fa> にイメージ検証をさせたときに踏んだものだ。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>展開したあと検証しようとして、危うく壊れたパッケージを落としたと思い込むところだった。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>イメージ\u003C\u002Fth>\n\u003Cth>オフセット\u003C\u002Fth>\n\u003Cth>マジック\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>boot\u003C\u002Fcode> \u002F \u003Ccode>init_boot\u003C\u002Fcode> \u002F \u003Ccode>recovery\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>414E44524F494421\u003C\u002Fcode>（\u003Ccode>ANDROID!\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>vendor_boot\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>564E4452424F4F54\u003C\u002Fcode>（\u003Ccode>VNDRBOOT\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>dtbo\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>D7B7AB1E\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>vbmeta*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>41564230\u003C\u002Fcode>（\u003Ccode>AVB0\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>system\u003C\u002Fcode> \u002F \u003Ccode>vendor\u003C\u002Fcode> \u002F \u003Ccode>odm\u003C\u002Fcode> \u002F \u003Ccode>my_*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>1024\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>E2E1F5E0\u003C\u002Fcode>（\u003Cstrong>EROFS\u003C\u002Fstrong>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>罠は最後の行にある：Android 16 のシステムパーティションは \u003Cstrong>EROFS\u003C\u002Fstrong> で、ext4 ではない。ext4 の \u003Ccode>53EF\u003C\u002Fcode> を探すと「イメージが全部壊れている」という結論しか出ない。しかも \u003Ccode>dtbo\u003C\u002Fcode> の \u003Ccode>D7B7AB1E\u003C\u002Fcode> は ext4 のマジックにも少し似ているので、混同しないように。\u003C\u002Fp>\n\u003Ch2 id=\"書-込-2-役割\">書き込み：2 つのモード、それぞれの役割\u003C\u002Fh2>\n\u003Cp>fastbootd に入ったらまず身元を確認する：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot devices\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar product      \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期待値 OP5D2BL1 \u002F giuliac \u002F PKG110\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar is\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">userspace \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期待値 yes\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar current\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期待値 a\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>あとは 1 つずつ焼いていく。論理パーティションと大部分の物理パーティションは \u003Ccode>--slot=all\u003C\u002Fcode> を受け付けるが、この端末の論理パーティションは**\u003Ccode>_a\u003C\u002Fcode> にしか存在しない**ので、その一群に \u003Ccode>--slot=all\u003C\u002Fcode> を使うと拒否される。単一スロットに戻せばよい。\u003C\u002Fp>\n\u003Cp>焼き終わったらデータを消して初回起動：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot bootloader\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">w\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot erase metadata\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"罠-1-modem-fastbootd-書-込\">罠 1：modem は fastbootd で書き込めない\u003C\u002Fh2>\n\u003Cp>人生を疑い始めた最初のエラー：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Sending sparse 'modem_a' 1\u002F1 (219896 KB)   OKAY\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Writing 'modem_a'                          FAILED (remote: 'No such file or directory')\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>getvar\u003C\u002Fcode> の挙動はもっと不可解だった：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>partition-size:modem_a  → FAILED (remote: 'Could not open partition')\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>partition-type:modem_a  → raw        ← これにはなぜか値がある\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>is-logical:modem_a      → FAILED (remote: 'Partition not found')\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>パーティションは一見「存在する」のに、書き込めない。\u003C\u002Fp>\n\u003Cp>原因は単純だった：\u003Cstrong>fastbootd はユーザー空間で動いており、\u003Ccode>modem_a\u003C\u002Fcode> のブロックデバイスノードを持っていない\u003C\u002Fstrong>。パーティションが壊れているわけではない。bootloader に戻って確認すると、\u003Ccode>partition-size:modem_a\u003C\u002Fcode> は \u003Ccode>0x15E00000\u003C\u002Fcode> でまったく問題ない。\u003C\u002Fp>\n\u003Cp>つまり modem は bootloader で焼く必要がある：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot bootloader\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a modem modem.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>一発で通った。\u003C\u002Fp>\n\u003Ch2 id=\"罠-2-oneplus-my_-存在\">罠 2：OnePlus の \u003Ccode>my_*\u003C\u002Fcode> パーティションはそもそも存在しない\u003C\u002Fh2>\n\u003Cp>2 つ目の罠はさらにひどい。8 つの \u003Ccode>my_*\u003C\u002Fcode> パーティションを焼くと全部失敗し、最初は super の空き容量不足だと思った。\u003C\u002Fp>\n\u003Cp>あとで気づいた：\u003Cstrong>非公式 ROM を焼くときに super パーティションテーブルが AOSP レイアウトで再構築され\u003C\u002Fstrong>、\u003Ccode>system\u003C\u002Fcode>、\u003Ccode>system_ext\u003C\u002Fcode>、\u003Ccode>product\u003C\u002Fcode>、\u003Ccode>vendor\u003C\u002Fcode>、\u003Ccode>odm\u003C\u002Fcode>、\u003Ccode>system_dlkm\u003C\u002Fcode>、\u003Ccode>vendor_dlkm\u003C\u002Fcode> という標準レイアウトだけが残っていた。OnePlus 独自の \u003Ccode>my_product\u003C\u002Fcode>、\u003Ccode>my_stock\u003C\u002Fcode>、\u003Ccode>my_bigball\u003C\u002Fcode>、\u003Ccode>my_carrier\u003C\u002Fcode>、\u003Ccode>my_engineering\u003C\u002Fcode>、\u003Ccode>my_heytap\u003C\u002Fcode>、\u003Ccode>my_manifest\u003C\u002Fcode>、\u003Ccode>my_region\u003C\u002Fcode> はすべて作られていない。\u003C\u002Fp>\n\u003Cp>容量不足ではなく、\u003Cstrong>そもそもそのパーティションが存在しない\u003C\u002Fstrong>のだ。\u003C\u002Fp>\n\u003Cp>解決策は先に作成してから焼く：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot create\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">logical\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">partition my_stock_a \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0x100000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a my_stock my_stock.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>プレースホルダのサイズは適当でよい。\u003Ccode>fastboot flash\u003C\u002Fcode> がイメージの実際のサイズに自動でリサイズしてくれる。\u003C\u002Fp>\n\u003Cp>ついでにこの端末の最終サイズを記録しておく。あとで照合するとき用：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>my_product      1520168960 → 0x5A9BF000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_stock        4491419648 → 0x10BB5A000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_region        7057408  → 0x6BB000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_manifest       729088  → 0xB2000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>modem           314667008 → 0x15E00000\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"罠-3-getvar-存在-3-通-言-方\">罠 3：\u003Ccode>getvar\u003C\u002Fcode> の「存在しない」には 3 通りの言い方がある\u003C\u002Fh2>\n\u003Cp>これは致命的ではないが、非常に誤判定しやすい。確実に存在しないパーティション名で意味を較正してみた：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>クエリ\u003C\u002Fth>\n\u003Cth>パーティションが存在する\u003C\u002Fth>\n\u003Cth>存在しない\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>partition-type:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>raw\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Invalid partition')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>partition-size:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>0x...\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Could not open partition')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>has-slot:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>yes\u003C\u002Fcode> \u002F \u003Ccode>no\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>no\u003C\u002Fcode>（\u003Cstrong>エラーなし\u003C\u002Fstrong>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>is-logical:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>yes\u003C\u002Fcode> \u002F \u003Ccode>no\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Partition not found')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>つまりパーティションが存在するかどうかは、\u003Cstrong>\u003Ccode>partition-type\u003C\u002Fcode> と \u003Ccode>is-logical\u003C\u002Fcode> だけを見る\u003C\u002Fstrong>。\u003Ccode>has-slot\u003C\u002Fcode> が存在しなくても \u003Ccode>no\u003C\u002Fcode> を返す仕様は、人を殺す気でできている。\u003C\u002Fp>\n\u003Ch2 id=\"戻-終\">戻し終わった、それで？\u003C\u002Fh2>\n\u003Cp>焼き終わって起動すると、電話、SMS、デュアル SIM、Wi-Fi、Bluetooth、NFC、指紋、充電はすべて正常で、カメラ（サードパーティ製）も使えた。最も強い完全性の証拠は、\u003Cstrong>システム更新で OTA を検出できた\u003C\u002Fstrong>ことだ。ここまで来れば、戻しは成功と言える。\u003C\u002Fp>\n\u003Cp>ここで root と OTA の関係も整理しておく。次回忘れないように。\u003C\u002Fp>\n\u003Ch3 id=\"ota-root-消\">OTA のあと root は消える\u003C\u002Fh3>\n\u003Cp>OTA は \u003Ccode>boot\u003C\u002Fcode> を上書きするので、KernelSU のパッチも当然消える。これはリスクではなく、再度パッチを当てればよい。\u003C\u002Fp>\n\u003Ch3 id=\"再-root-猶予-窓\">再 root：猶予の窓\u003C\u002Fh3>\n\u003Cp>ColorOS に戻したあと、ほどなく最新の公式 OTA アップデートが届いた。インストール後に「今すぐ再起動」を押す前に待て —— この時点で新しいイメージはすでに別スロットに書き込まれており、旧システムの root はまだ残っている。そのまま読み出せばよい：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb shell su \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">c \u003C\u002Fspan>\u003Cspan style=\"color:#98C379\">\"dd if=\u002Fdev\u002Fblock\u002Fby-name\u002Fboot_b of=\u002Fdata\u002Flocal\u002Ftmp\u002Fboot_b.img\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb pull \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">data\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">local\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">tmp\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">boot_b.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>利点はフルパッケージを待たなくてよく、イメージが 100% 一致すること。欠点は\u003Cstrong>再起動した瞬間に窓が閉じる\u003C\u002Fstrong>ことだ。\u003C\u002Fp>\n\u003Cp>Ace 5 は Android 13+ の GKI 端末で、KernelSU がパッチするのは \u003Ccode>boot\u003C\u002Fcode> なので、\u003Ccode>init_boot\u003C\u002Fcode> に焼かないように。\u003C\u002Fp>\n\u003Ch3 id=\"楽-方法-未使用\">もっと楽な方法：未使用スロットにインストール\u003C\u002Fh3>\n\u003Cp>KernelSU Manager には「未使用のスロットにインストール（OTA 後）」があり、パッチ済みイメージをそのまま別スロットに書き込める。再起動すれば root 付きの新システムになる。手動 dd よりずっと楽だ。\u003C\u002Fp>\n\u003Cp>代償は、パソコンに純正イメージのバックアップが残らないこと。いざというときロールバックしたくても、dump した \u003Ccode>boot\u003C\u002Fcode> が手元にない。だから今回は先に手動で dd しておいた。少なくとも安心のためだ。\u003C\u002Fp>\n\u003Cp>もう 1 つ小さな話：KernelSU では \u003Ccode>ls \u002Fsystem\u002Fbin\u002Fsu\u003C\u002Fcode> で見つからないのが\u003Cstrong>正常\u003C\u002Fstrong>だ。execve フックで動いており、実際に su ファイルを置いているわけではない。これで root が消えたかどうかを判断しないように。\u003C\u002Fp>\n\u003Ch2 id=\"本当-長-尾\">カメラのクラッシュ：本当の長い尾\u003C\u002Fh2>\n\u003Cp>システムは焼けて root の話も整理できた。ところが純正カメラを開くと —— \u003Cstrong>即落ち\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>サードパーティ製カメラはすべて正常なので、カメラのハードウェア、HAL、権限に問題はない。クラッシュログも非常に安定していた：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>java.lang.UnsatisfiedLinkError: dlopen failed: library \"libCombineLutJni.so\" not found\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.camera.combinelut.CombineLutJNI.&#x3C;clinit>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.ocs.camera.CombineLutHelper.&#x3C;init>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.camera.common.utils.Lut3dDataManager.&#x3C;init>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>ネイティブライブラリの読み込み失敗。まずこのライブラリの所在を探す：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>$ adb shell find \u002F -name libCombineLutJni.so\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u002Fmy_product\u002Flib64\u002FlibCombineLutJni.so\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>ライブラリは \u003Ccode>\u002Fmy_product\u002Flib64\u003C\u002Fcode> にあるが、カメラは PRODUCT App で、そのリンカ名前空間は \u003Ccode>\u002Fproduct\u002Flib64:\u002Fsystem\u002Fproduct\u002Flib64\u003C\u002Fcode> しか認識せず、\u003Ccode>\u002Fmy_product\u002Flib64\u003C\u002Fcode> を直接見にはいかない。\u003C\u002Fp>\n\u003Cp>通常は overlay が両者をマージするはずだ。\u003Ccode>\u002Fvendor\u002Fetc\u002Ffstab.qcom\u003C\u002Fcode> にも確かに書かれている：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlay-overlay \u002Fproduct\u002Flib64 overlay ... lowerdir=\u002Fmy_region\u002Flib64:\u002Fmy_preload\u002Flib64:\u002Fmy_product\u002Flib64:\u002Fmy_heytap\u002Flib64:\u002Fmy_stock\u002Flib64:\u002Fmy_engineering\u002Flib64:\u002Fproduct\u002Flib64\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>ところが \u003Ccode>\u002Fproc\u002Fmounts\u003C\u002Fcode> を確認すると、\u003Ccode>\u002Fproduct\u002Fapp\u003C\u002Fcode>、\u003Ccode>\u002Fproduct\u002Fpriv-app\u003C\u002Fcode>、\u003Ccode>\u002Fproduct\u002Flib64\u003C\u002Fcode>、\u003Ccode>\u002Fproduct\u002Flib\u003C\u002Fcode> の\u003Cstrong>4 つの overlay がすべてマウントされていなかった\u003C\u002Fstrong>。同じ fstab にある \u003Ccode>\u002Fproduct\u002Fetc\u002Fpermissions\u003C\u002Fcode> と \u003Ccode>\u002Fproduct\u002Fframework\u003C\u002Fcode> は正常だった。\u003C\u002Fp>\n\u003Cp>手動でマウントを再現すると、カーネルが原因を教えてくれた：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlayfs: failed to resolve '\u002Fmy_preload\u002Flib64': -2\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>-2\u003C\u002Fcode> は ENOENT。\u003Cstrong>\u003Ccode>\u002Fmy_preload\u003C\u002Fcode> パーティションが存在しない。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>それを外して再度マウントすると即座に成功し、\u003Ccode>\u002Fproduct\u002Flib64\u002FlibCombineLutJni.so\u003C\u002Fcode> が現れ、カメラも起動できるようになった。\u003C\u002Fp>\n\u003Cp>完全な因果連鎖はこうだ：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>my_preload パーティションが欠落\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → \u002Fproduct\u002F{app,priv-app,lib64,lib} の 4 つの overlay がマウント失敗\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → \u002Fproduct\u002Flib64 から \u002Fmy_product\u002Flib64 内のライブラリが見えない\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → カメラが libCombineLutJni.so の読み込みに失敗 → クラッシュ\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>fstab には \u003Ccode>overlayfs_remove_missing_lowerdir\u003C\u002Fcode> が書かれており、本来は欠けたディレクトリを自動で飛ばすはずだ。しかしこの端末の起動経路は実際には生の lowerdir リストを実行しており、そのフィルタ関数は呼ばれていない。だから欠けたディレクトリは容赦なく ENOENT になる。\u003C\u002Fp>\n\u003Ch3 id=\"my_preload-欠\">なぜ \u003Ccode>my_preload\u003C\u002Fcode> だけが欠けているのか\u003C\u002Fh3>\n\u003Cp>前述の罠に戻る：非公式 ROM を焼くときに super パーティションテーブルが AOSP レイアウトで再構築され、純正の \u003Ccode>my_preload\u003C\u002Fcode>、\u003Ccode>my_company\u003C\u002Fcode> も一緒に削除された。戻すときは ColorOS 16 フル OTA に存在するパーティションだけを作成したが、\u003Cstrong>フルパッケージには \u003Ccode>my_preload\u003C\u002Fcode> が含まれていない\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>この 2 つは欧加（OPPO\u002FOnePlus）のプリインストールパーティション（コミュニティでは「双広分区」と呼ばれる）で、フル OTA では更新されず、ダウングレードパッケージ \u002F ロールバックパッケージ \u002F 工場出荷イメージにしか入っていない。\u003C\u002Fp>\n\u003Cp>この端末には証拠も残っている：\u003Ccode>\u002Fmy_manifest\u002Fetc\u002Fmy_preload.map\u003C\u002Fcode> に \u003Ccode>\u002Fmy_preload\u002Fdel-app\u002F...\u003C\u002Fcode> のプリインストール一覧が並んでおり、工場出荷時のレイアウトには元々存在していたことがわかる。\u003C\u002Fp>\n\u003Ch3 id=\"救出-1-取-出\">救出：パーティションを 1 つだけ取り出す\u003C\u002Fh3>\n\u003Cp>古いフルダウングレードパッケージ \u003Ccode>PKG110_16.0.9.400(CN01)\u003C\u002Fcode> を改めてダウンロードした。中に \u003Ccode>my_preload\u003C\u002Fcode>（1.8 GB）と \u003Ccode>my_company\u003C\u002Fcode>（336 kB）が入っている。\u003C\u002Fp>\n\u003Cp>注意：\u003Cstrong>パーティションだけを抽出し、パッケージ全体を焼かない\u003C\u002Fstrong>。このパッケージは現在のシステムより古く、\u003Ccode>wipe=1\u003C\u002Fcode> と \u003Ccode>ota-downgrade=yes\u003C\u002Fcode> も付いている。丸ごと焼くのは自分から ARB を踏みに行くようなものだ。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">.\\\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">payload-dumper-go.exe\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">p my_preload \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">o .\\my_preload_16.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0.9\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">400\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> .\\670f63e2a29741578637222c6b7be73b.zip\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>抽出したイメージは 1,784,397,824 バイト、EROFS、AVB 公開鍵の SHA-1 は \u003Ccode>1a130f9a...66f6\u003C\u002Fcode> で、端末上の \u003Ccode>\u002Fvendor\u002Fetc\u002Foplus_avb.pubkey\u003C\u002Fcode> と完全に一致 —— 公式署名であり、\u003Ccode>avb_keys\u003C\u002Fcode> 検証を通せる。\u003C\u002Fp>\n\u003Cp>焼く前に空き容量を確認：super の総容量は 13.58 GiB、使用済みは約 10.0 GiB、残り 3.58 GiB で、1.66 GiB を置くには十分すぎる。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb reboot fastboot\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar is\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">userspace   \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期待値 yes\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot create\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">logical\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">partition my_preload_a \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1784397824\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a my_preload .\\my_preload_16.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0.9\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">400\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">\\my_preload.img\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>焼いたあと \u003Ccode>my_preload_a\u003C\u002Fcode> は dm-verity（\u003Ccode>dm-29\u003C\u002Fcode>、erofs）上にマウントされ、hashtree 検証を通ったことがわかる。さらに overlay を確認：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlay-overlay \u002Fproduct\u002Flib64 ... lowerdir=\u002Fmy_region\u002Flib64:\u002Fmy_preload\u002Flib64:\u002Fmy_product\u002Flib64:...\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>4 つの overlay がすべて戻り、ライブラリが見え、カメラ起動後もプロセスは安定して生存し、\u003Ccode>dumpsys media.camera\u003C\u002Fcode> に active session が見え、crash buffer にも新しい記録はない。\u003C\u002Fp>\n\u003Cp>完了。\u003C\u002Fp>\n\u003Ch2 id=\"雑感\">いくつかの雑感\u003C\u002Fh2>\n\u003Cp>今回の格闘で最大の感想は：\u003Cstrong>フラッシュの本当の難しさは焼くことではなく、「なぜこのパーティションが書き込めないのか」を理解すること\u003C\u002Fstrong>だ。\u003C\u002Fp>\n\u003Cp>いくつかの具体的な教訓：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>エラーだけを見ず、その背後にあるパターンを見る\u003C\u002Fstrong>。\u003Ccode>modem\u003C\u002Fcode> が書き込めないのも \u003Ccode>my_*\u003C\u002Fcode> が書き込めないのも、エラーは曖昧だが、前者は fastbootd にノードがないから、後者はパーティションがそもそも存在しないからで、解決策はまったく違う。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>A\u002FB 端末の「もう 1 つのスロット」は無料のタイムマシン\u003C\u002Fstrong>。OTA インストール後は慌てて再起動せず、まず新しいイメージを dump すれば、あとでフルパッケージを待つ数時間を節約できる。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>super を再構築した端末で純正に戻る道は「公式パッケージを焼く」だけではない\u003C\u002Fstrong>。公式フルパッケージにないパーティションは、ダウングレードパッケージから個別に取り出す必要がある。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode>my_preload.img\u003C\u002Fcode> は取っておくこと\u003C\u002Fstrong>。今後 super を再構築するたび（非公式 ROM の再焼き、フルパッケージ書き込み、再ロック前の再焼き）に、このパーティションを作成して焼き直す必要がある。そうしないとカメラがまたクラッシュする。通常の OTA では上書きされないので心配はいらない。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>最後に、フラッシュにはリスクがあり、文鎮化やデータ消失は自己責任だ。コマンドはすべて実際に実行したが、あなたの端末が私のものと完全に同じとは限らない。作業前にバックアップを。\u003C\u002Fp>\n\u003Cp>では、寝る。\u003C\u002Fp>\n",{"slug":963,"title":1089,"lang":1090,"desc":1091,"date":967,"tags":1092,"edit":967,"aiGenerated":969,"aiReview":970,"aiTranslated":1093,"readingTime":1094,"toc":1099,"content":1145},"把一加 Ace 5 从非官方系统刷回 ColorOS 16，顺手把相机救了回来","zh-Hans","从非官方系统回刷 ColorOS 16 的完整踩坑记录：三次翻车、一次相机闪退，以及 root 和 OTA 之间那点绕不开的事。",[],false,{"text":1095,"minutes":1096,"time":1097,"words":1098},"14 min read",13.115,786900,2623,[1100,1102,1104,1107,1110,1113,1116,1119,1122,1125,1128,1131,1134,1137,1140,1143],{"level":978,"text":1101,"slug":1101},"起因",{"level":978,"text":1103,"slug":1103},"两条铁律",{"level":978,"text":1105,"slug":1106},"验包：不解 8GB 也能先看版本","验包-不解-8gb-也能先看版本",{"level":978,"text":1108,"slug":1109},"认镜像：别把 EROFS 当坏包","认镜像-别把-erofs-当坏包",{"level":978,"text":1111,"slug":1112},"刷写：两个模式，各干各的","刷写-两个模式-各干各的",{"level":978,"text":1114,"slug":1115},"坑一：modem 在 fastbootd 里写不进去","坑一-modem-在-fastbootd-里写不进去",{"level":978,"text":1117,"slug":1118},"坑二：一加的 `my_*` 分区压根不存在","坑二-一加的-my_-分区压根不存在",{"level":978,"text":1120,"slug":1121},"坑三：`getvar` 的「不存在」有三种说法","坑三-getvar-的-不存在-有三种说法",{"level":978,"text":1123,"slug":1124},"回刷完了，然后呢","回刷完了-然后呢",{"level":1006,"text":1126,"slug":1127},"OTA 之后 root 会没","ota-之后-root-会没",{"level":1006,"text":1129,"slug":1130},"重新 root：窗口期","重新-root-窗口期",{"level":1006,"text":1132,"slug":1133},"更省事的做法：安装到未使用的插槽","更省事的做法-安装到未使用的插槽",{"level":978,"text":1135,"slug":1136},"相机闪退：真正的长尾","相机闪退-真正的长尾",{"level":1006,"text":1138,"slug":1139},"为什么偏偏缺 `my_preload`","为什么偏偏缺-my_preload",{"level":1006,"text":1141,"slug":1142},"抢救：只取一个分区","抢救-只取一个分区",{"level":978,"text":1144,"slug":1144},"几句碎碎念","\u003Ch2 id=\"起因\">起因\u003C\u002Fh2>\n\u003Cp>在做了近两个月的 Apple 生态用户之后，我最终还是决定放弃 Apple 生态，并打算把我原来的备用机 —— 这台一加 Ace 5 国行（代号 \u003Ccode>giuliac\u003C\u002Fcode>，型号 \u003Ccode>PKG110\u003C\u002Fcode>）—— 刷回 ColorOS 16 过渡使用，等待新机发售之后再做决定。\u003C\u002Fp>\n\u003Cp>我一开始以为这活儿顶多是「下个包、进 fastboot、刷完重启」，结果整个过程下来，真正花时间的不是刷，而是\u003Cstrong>搞清楚为什么刷不进去\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>这篇就把整个过程记一遍，顺便把 root 和 OTA 的事也理一理，最后再说那个让我折腾到凌晨的相机闪退。\u003C\u002Fp>\n\u003Ch2 id=\"两条铁律\">两条铁律\u003C\u002Fh2>\n\u003Cp>先把结论放前面，这两条是这次用真金白银换来的：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>固件分区在 bootloader 刷，逻辑分区在 fastbootd 刷\u003C\u002Fstrong>。混着来就会出现「分区明明在，就是写不进去」这种灵异现象。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>回锁放最后\u003C\u002Fstrong>。必须能开机、功能正常、能检测到 OTA 之后，才考虑 \u003Ccode>fastboot flashing lock\u003C\u002Fcode>。这次我没回锁，bootloader 保持 unlocked。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"验包-不解-8gb-也能先看版本\">验包：不解 8GB 也能先看版本\u003C\u002Fh2>\n\u003Cp>手上的包是 ColorOS 16 全量 OTA，8.7 GB，51 个分区。刷之前总得确认一下这包对不对，但又不想先解压一遍。\u003C\u002Fp>\n\u003Cp>好在 \u003Ccode>payload.bin\u003C\u002Fcode> 在 OTA zip 里是\u003Cstrong>未压缩存储\u003C\u002Fstrong>的，可以直接按偏移量读：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># 先读 zip 里的 metadata\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">tar \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">xOf .\\86d1d2fe214c4a87804106baa2d4af4a.zip \u003C\u002Fspan>\u003Cspan style=\"color:#98C379\">'META-INF\u002Fcom\u002Fandroid\u002Fmetadata'\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># payload.bin 的起始偏移由 metadata 里的 ota-property-files 给出\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># 直接按字节把开头那段 protobuf manifest 读出来，再全词匹配分区名\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>要看的字段就那几个：\u003Ccode>android_version\u003C\u002Fcode>、\u003Ccode>version_name\u003C\u002Fcode>、\u003Ccode>product_name\u003C\u002Fcode>、\u003Ccode>pre-device\u003C\u002Fcode>、\u003Ccode>post-build\u003C\u002Fcode>。这台机的 \u003Ccode>pre-device\u003C\u002Fcode> 是 \u003Ccode>OP5D2BL1\u003C\u002Fcode>，对上了才敢往下走。\u003C\u002Fp>\n\u003Cp>解包用的是 \u003Ccode>payload-dumper-go\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">.\\\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">payload-dumper-go.exe\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">c \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">8\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">o .\\cos16_img .\\86d1d2fe214c4a87804106baa2d4af4a.zip\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>payload 里写了 51 个分区，但 dumper 只产出 48 个镜像，\u003Ccode>checkpoint_gc\u003C\u002Fcode>、\u003Ccode>otapreopt_script\u003C\u002Fcode>、\u003Ccode>qti_dynamic_partitions\u003C\u002Fcode> 这三个本来就不该直接刷。\u003C\u002Fp>\n\u003Ch2 id=\"认镜像-别把-erofs-当坏包\">认镜像：别把 EROFS 当坏包\u003C\u002Fh2>\n\u003Cblockquote>\n\u003Cp>这一段不是我手动做的，是刷机过程中让 \u003Ca href=\"https:\u002F\u002Fomp.sh\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">omp\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" viewBox=\"0 0 24 24\" width=\"1em\" height=\"1em\" fill=\"currentColor\" aria-hidden=\"true\" style=\"display:inline;vertical-align:middle;margin-left:2px;\">\u003Cpath d=\"M5 21q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h7v2H5v14h14v-7h2v7q0 .825-.587 1.413T19 21zm4.7-5.3l-1.4-1.4L17.6 5H14V3h7v7h-2V6.4z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fa> 顺手校验镜像时踩到的。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>解出来之后本来想校验一下，结果差点以为下到了坏包。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>镜像\u003C\u002Fth>\n\u003Cth>偏移\u003C\u002Fth>\n\u003Cth>魔数\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>boot\u003C\u002Fcode> \u002F \u003Ccode>init_boot\u003C\u002Fcode> \u002F \u003Ccode>recovery\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>414E44524F494421\u003C\u002Fcode>（\u003Ccode>ANDROID!\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>vendor_boot\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>564E4452424F4F54\u003C\u002Fcode>（\u003Ccode>VNDRBOOT\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>dtbo\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>D7B7AB1E\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>vbmeta*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>41564230\u003C\u002Fcode>（\u003Ccode>AVB0\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>system\u003C\u002Fcode> \u002F \u003Ccode>vendor\u003C\u002Fcode> \u002F \u003Ccode>odm\u003C\u002Fcode> \u002F \u003Ccode>my_*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>1024\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>E2E1F5E0\u003C\u002Fcode>（\u003Cstrong>EROFS\u003C\u002Fstrong>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>坑就在最后一行：Android 16 的系统分区是 \u003Cstrong>EROFS\u003C\u002Fstrong>，不是 ext4。你要是按 ext4 的 \u003Ccode>53EF\u003C\u002Fcode> 去找，只会得出「镜像全坏了」的结论。而且 \u003Ccode>dtbo\u003C\u002Fcode> 的 \u003Ccode>D7B7AB1E\u003C\u002Fcode> 和 ext4 也长得有点像，别搞混。\u003C\u002Fp>\n\u003Ch2 id=\"刷写-两个模式-各干各的\">刷写：两个模式，各干各的\u003C\u002Fh2>\n\u003Cp>进 fastbootd 之后先确认身份：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot devices\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar product      \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期望 OP5D2BL1 \u002F giuliac \u002F PKG110\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar is\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">userspace \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期望 yes\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar current\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期望 a\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>然后就是逐个刷。逻辑分区和大部分物理分区都可以 \u003Ccode>--slot=all\u003C\u002Fcode>，但这台机的逻辑分区\u003Cstrong>只存在于 \u003Ccode>_a\u003C\u002Fcode>\u003C\u002Fstrong>，所以对这堆分区用 \u003Ccode>--slot=all\u003C\u002Fcode> 会被拒，退回单槽就好。\u003C\u002Fp>\n\u003Cp>刷完清数据、首启：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot bootloader\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">w\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot erase metadata\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"坑一-modem-在-fastbootd-里写不进去\">坑一：modem 在 fastbootd 里写不进去\u003C\u002Fh2>\n\u003Cp>第一个让我怀疑人生的报错：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Sending sparse 'modem_a' 1\u002F1 (219896 KB)   OKAY\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Writing 'modem_a'                          FAILED (remote: 'No such file or directory')\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>getvar\u003C\u002Fcode> 的表现更迷惑：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>partition-size:modem_a  → FAILED (remote: 'Could not open partition')\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>partition-type:modem_a  → raw        ← 这条居然有值\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>is-logical:modem_a      → FAILED (remote: 'Partition not found')\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>分区一眼「存在」，但就是写不进去。\u003C\u002Fp>\n\u003Cp>根因其实很简单：\u003Cstrong>fastbootd 是用户态，里面没有 \u003Ccode>modem_a\u003C\u002Fcode> 的块设备节点\u003C\u002Fstrong>，不是分区坏了。切回 bootloader 一查，\u003Ccode>partition-size:modem_a\u003C\u002Fcode> 是 \u003Ccode>0x15E00000\u003C\u002Fcode>，好得很。\u003C\u002Fp>\n\u003Cp>所以 modem 得在 bootloader 里刷：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot bootloader\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a modem modem.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>一次过。\u003C\u002Fp>\n\u003Ch2 id=\"坑二-一加的-my_-分区压根不存在\">坑二：一加的 \u003Ccode>my_*\u003C\u002Fcode> 分区压根不存在\u003C\u002Fh2>\n\u003Cp>第二个坑更离谱。刷那 8 个 \u003Ccode>my_*\u003C\u002Fcode> 分区时全部失败，一开始我以为是 super 空间不够。\u003C\u002Fp>\n\u003Cp>后来才想明白：\u003Cstrong>刷非官方系统时按 AOSP 布局重建了 super 分区表\u003C\u002Fstrong>，只留了 \u003Ccode>system\u003C\u002Fcode>、\u003Ccode>system_ext\u003C\u002Fcode>、\u003Ccode>product\u003C\u002Fcode>、\u003Ccode>vendor\u003C\u002Fcode>、\u003Ccode>odm\u003C\u002Fcode>、\u003Ccode>system_dlkm\u003C\u002Fcode>、\u003Ccode>vendor_dlkm\u003C\u002Fcode> 这套标准布局。一加自己那套 \u003Ccode>my_product\u003C\u002Fcode>、\u003Ccode>my_stock\u003C\u002Fcode>、\u003Ccode>my_bigball\u003C\u002Fcode>、\u003Ccode>my_carrier\u003C\u002Fcode>、\u003Ccode>my_engineering\u003C\u002Fcode>、\u003Ccode>my_heytap\u003C\u002Fcode>、\u003Ccode>my_manifest\u003C\u002Fcode>、\u003Ccode>my_region\u003C\u002Fcode> 全都没建。\u003C\u002Fp>\n\u003Cp>不是空间不足，是\u003Cstrong>根本没这个分区\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>解法是先创建再刷：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot create\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">logical\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">partition my_stock_a \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0x100000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a my_stock my_stock.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>占位尺寸随便给，\u003Ccode>fastboot flash\u003C\u002Fcode> 会自动 resize 到镜像实际大小。\u003C\u002Fp>\n\u003Cp>顺便记一下这台机最终的尺寸，以后对得上号：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>my_product      1520168960 → 0x5A9BF000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_stock        4491419648 → 0x10BB5A000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_region        7057408  → 0x6BB000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_manifest       729088  → 0xB2000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>modem           314667008 → 0x15E00000\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"坑三-getvar-的-不存在-有三种说法\">坑三：\u003Ccode>getvar\u003C\u002Fcode> 的「不存在」有三种说法\u003C\u002Fh2>\n\u003Cp>这个坑不致命，但特别容易误判。我拿一个肯定不存在的分区名校准了一下语义：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>查询\u003C\u002Fth>\n\u003Cth>分区存在\u003C\u002Fth>\n\u003Cth>分区不存在\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>partition-type:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>raw\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Invalid partition')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>partition-size:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>0x...\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Could not open partition')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>has-slot:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>yes\u003C\u002Fcode> \u002F \u003Ccode>no\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>no\u003C\u002Fcode>（\u003Cstrong>无报错\u003C\u002Fstrong>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>is-logical:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>yes\u003C\u002Fcode> \u002F \u003Ccode>no\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Partition not found')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>所以判断分区到底在不在，\u003Cstrong>只看 \u003Ccode>partition-type\u003C\u002Fcode> 和 \u003Ccode>is-logical\u003C\u002Fcode>\u003C\u002Fstrong>。\u003Ccode>has-slot\u003C\u002Fcode> 那个「不存在也返回 no」的设定，坑死人不偿命。\u003C\u002Fp>\n\u003Ch2 id=\"回刷完了-然后呢\">回刷完了，然后呢\u003C\u002Fh2>\n\u003Cp>刷完开机，电话、短信、双卡、Wi-Fi、蓝牙、NFC、指纹、充电都正常，相机（第三方）也能用。最硬的完整性证据是：\u003Cstrong>系统更新里能检测到 OTA\u003C\u002Fstrong>。到这一步，回刷算是成功了。\u003C\u002Fp>\n\u003Cp>这里顺便把 root 和 OTA 的关系理一下，免得下次又忘。\u003C\u002Fp>\n\u003Ch3 id=\"ota-之后-root-会没\">OTA 之后 root 会没\u003C\u002Fh3>\n\u003Cp>OTA 会覆盖 \u003Ccode>boot\u003C\u002Fcode>，KernelSU 的 patch 自然也就没了。这不是风险，重新 patch 就行。\u003C\u002Fp>\n\u003Ch3 id=\"重新-root-窗口期\">重新 root：窗口期\u003C\u002Fh3>\n\u003Cp>刷回 ColorOS 之后，很快就收到了最新的官方 OTA 更新。装完之后先别点「立即重启」—— 这时候新镜像已经写进另一个槽位，旧系统的 root 还在，直接读出来就是：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb shell su \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">c \u003C\u002Fspan>\u003Cspan style=\"color:#98C379\">\"dd if=\u002Fdev\u002Fblock\u002Fby-name\u002Fboot_b of=\u002Fdata\u002Flocal\u002Ftmp\u002Fboot_b.img\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb pull \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">data\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">local\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">tmp\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">boot_b.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>优点是不用等全量包，镜像 100% 对版。缺点是\u003Cstrong>一重启窗口就关了\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>Ace 5 是 Android 13+ 的 GKI 机型，KernelSU patch 的是 \u003Ccode>boot\u003C\u002Fcode>，别刷到 \u003Ccode>init_boot\u003C\u002Fcode> 上。\u003C\u002Fp>\n\u003Ch3 id=\"更省事的做法-安装到未使用的插槽\">更省事的做法：安装到未使用的插槽\u003C\u002Fh3>\n\u003Cp>KernelSU Manager 里有个「安装到未使用的插槽（OTA 后）」，直接把 patch 好的镜像写进另一个槽位，重启之后就是带 root 的新系统。比手动 dd 省事得多。\u003C\u002Fp>\n\u003Cp>代价是它不会在电脑上留下原厂镜像的备份。真出问题想回滚，手上就没有那份 dump 出来的 \u003Ccode>boot\u003C\u002Fcode>。所以我这次还是先手动 dd 了一份，至少心里踏实。\u003C\u002Fp>\n\u003Cp>还有个小细节：KernelSU 下 \u003Ccode>ls \u002Fsystem\u002Fbin\u002Fsu\u003C\u002Fcode> 找不到是\u003Cstrong>正常现象\u003C\u002Fstrong>，它靠 execve hook，不是真的放了个 su 文件。别拿这个判断 root 丢没丢。\u003C\u002Fp>\n\u003Ch2 id=\"相机闪退-真正的长尾\">相机闪退：真正的长尾\u003C\u002Fh2>\n\u003Cp>系统刷好了，root 的事也想明白了，结果打开自带相机——\u003Cstrong>秒退\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>第三方相机一切正常，说明摄像头硬件、HAL、权限都没问题。崩溃日志也很固定：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>java.lang.UnsatisfiedLinkError: dlopen failed: library \"libCombineLutJni.so\" not found\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.camera.combinelut.CombineLutJNI.&#x3C;clinit>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.ocs.camera.CombineLutHelper.&#x3C;init>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.camera.common.utils.Lut3dDataManager.&#x3C;init>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>加载原生库失败。那我先找这个库在哪：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>$ adb shell find \u002F -name libCombineLutJni.so\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u002Fmy_product\u002Flib64\u002FlibCombineLutJni.so\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>库在 \u003Ccode>\u002Fmy_product\u002Flib64\u003C\u002Fcode>，但相机是 PRODUCT App，它的 linker 命名空间只认 \u003Ccode>\u002Fproduct\u002Flib64:\u002Fsystem\u002Fproduct\u002Flib64\u003C\u002Fcode>，不会直接去看 \u003Ccode>\u002Fmy_product\u002Flib64\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>正常情况应该由 overlay 把两者合并起来。\u003Ccode>\u002Fvendor\u002Fetc\u002Ffstab.qcom\u003C\u002Fcode> 里也确实写了：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlay-overlay \u002Fproduct\u002Flib64 overlay ... lowerdir=\u002Fmy_region\u002Flib64:\u002Fmy_preload\u002Flib64:\u002Fmy_product\u002Flib64:\u002Fmy_heytap\u002Flib64:\u002Fmy_stock\u002Flib64:\u002Fmy_engineering\u002Flib64:\u002Fproduct\u002Flib64\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>结果一查 \u003Ccode>\u002Fproc\u002Fmounts\u003C\u002Fcode>，\u003Ccode>\u002Fproduct\u002Fapp\u003C\u002Fcode>、\u003Ccode>\u002Fproduct\u002Fpriv-app\u003C\u002Fcode>、\u003Ccode>\u002Fproduct\u002Flib64\u003C\u002Fcode>、\u003Ccode>\u002Fproduct\u002Flib\u003C\u002Fcode> 这\u003Cstrong>四个 overlay 全部没挂上\u003C\u002Fstrong>。同一份 fstab 里 \u003Ccode>\u002Fproduct\u002Fetc\u002Fpermissions\u003C\u002Fcode> 和 \u003Ccode>\u002Fproduct\u002Fframework\u003C\u002Fcode> 倒是正常的。\u003C\u002Fp>\n\u003Cp>手动复现挂载，内核直接告诉我原因：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlayfs: failed to resolve '\u002Fmy_preload\u002Flib64': -2\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>-2\u003C\u002Fcode> 就是 ENOENT。\u003Cstrong>\u003Ccode>\u002Fmy_preload\u003C\u002Fcode> 这个分区不存在。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>去掉它再挂，立刻成功，\u003Ccode>\u002Fproduct\u002Flib64\u002FlibCombineLutJni.so\u003C\u002Fcode> 马上出现，相机也就能启动了。\u003C\u002Fp>\n\u003Cp>完整因果链是这样的：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>my_preload 分区缺失\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → \u002Fproduct\u002F{app,priv-app,lib64,lib} 四个 overlay 挂载失败\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → \u002Fproduct\u002Flib64 看不到 \u002Fmy_product\u002Flib64 里的库\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → 相机加载 libCombineLutJni.so 失败 → 闪退\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>fstab 里其实写了 \u003Ccode>overlayfs_remove_missing_lowerdir\u003C\u002Fcode>，按理说缺目录应该自动跳过。但本机启动路径实际执行的是原始 lowerdir 列表，那个滤波函数压根没被调用，所以缺目录就是硬 ENOENT。\u003C\u002Fp>\n\u003Ch3 id=\"为什么偏偏缺-my_preload\">为什么偏偏缺 \u003Ccode>my_preload\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>回到前面那个坑：刷非官方系统时，super 分区表按 AOSP 布局被重建，原厂的 \u003Ccode>my_preload\u003C\u002Fcode>、\u003Ccode>my_company\u003C\u002Fcode> 一起被删了。回刷的时候我只创建了 ColorOS 16 全量 OTA 里存在的分区，而\u003Cstrong>全量包不包含 \u003Ccode>my_preload\u003C\u002Fcode>\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>这两个是欧加的预装分区（社区叫「双广分区」），不随全量 OTA 更新，只在降级包 \u002F 回退包 \u002F 出厂镜像里有。\u003C\u002Fp>\n\u003Cp>本机还留着证据：\u003Ccode>\u002Fmy_manifest\u002Fetc\u002Fmy_preload.map\u003C\u002Fcode> 里全是 \u003Ccode>\u002Fmy_preload\u002Fdel-app\u002F...\u003C\u002Fcode> 的预装清单，说明出厂布局里本来就有它。\u003C\u002Fp>\n\u003Ch3 id=\"抢救-只取一个分区\">抢救：只取一个分区\u003C\u002Fh3>\n\u003Cp>我重新去下载了一个旧的全量降级包 \u003Ccode>PKG110_16.0.9.400(CN01)\u003C\u002Fcode>，里面带 \u003Ccode>my_preload\u003C\u002Fcode>（1.8 GB）和 \u003Ccode>my_company\u003C\u002Fcode>（336 kB）。\u003C\u002Fp>\n\u003Cp>注意：\u003Cstrong>只抽分区，不刷整包\u003C\u002Fstrong>。这个包比当前系统旧，还带 \u003Ccode>wipe=1\u003C\u002Fcode> 和 \u003Ccode>ota-downgrade=yes\u003C\u002Fcode>，整包刷进去就是主动踩 ARB。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">.\\\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">payload-dumper-go.exe\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">p my_preload \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">o .\\my_preload_16.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0.9\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">400\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> .\\670f63e2a29741578637222c6b7be73b.zip\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>抽出来的镜像 1,784,397,824 字节，EROFS，AVB 公钥 SHA-1 是 \u003Ccode>1a130f9a...66f6\u003C\u002Fcode>，和设备上 \u003Ccode>\u002Fvendor\u002Fetc\u002Foplus_avb.pubkey\u003C\u002Fcode> 完全一致 —— 官方签名，能过 \u003Ccode>avb_keys\u003C\u002Fcode> 校验。\u003C\u002Fp>\n\u003Cp>刷之前先看空间：super 总容量 13.58 GiB，已用 10.0 GiB 左右，剩 3.58 GiB，放 1.66 GiB 绰绰有余。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb reboot fastboot\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar is\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">userspace   \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期望 yes\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot create\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">logical\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">partition my_preload_a \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1784397824\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a my_preload .\\my_preload_16.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0.9\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">400\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">\\my_preload.img\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>刷完 \u003Ccode>my_preload_a\u003C\u002Fcode> 挂在 dm-verity 上（\u003Ccode>dm-29\u003C\u002Fcode>，erofs），说明 hashtree 校验过了。再查 overlay：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlay-overlay \u002Fproduct\u002Flib64 ... lowerdir=\u002Fmy_region\u002Flib64:\u002Fmy_preload\u002Flib64:\u002Fmy_product\u002Flib64:...\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>四个 overlay 全回来了，库可见，相机启动后进程稳定存活，\u003Ccode>dumpsys media.camera\u003C\u002Fcode> 里能看到 active session，crash buffer 也没有新增记录。\u003C\u002Fp>\n\u003Cp>搞定。\u003C\u002Fp>\n\u003Ch2 id=\"几句碎碎念\">几句碎碎念\u003C\u002Fh2>\n\u003Cp>这次折腾下来，最大的感受是：\u003Cstrong>刷机真正难的不是刷，是理解「为什么这个分区刷不进去」\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>几个具体的教训：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>别只看报错，要看报错背后的模式\u003C\u002Fstrong>。\u003Ccode>modem\u003C\u002Fcode> 写不进去和 \u003Ccode>my_*\u003C\u002Fcode> 写不进去，报错都模棱两可，但一个是 fastbootd 没有节点，一个是分区根本不存在，解法完全不同。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>A\u002FB 机型的「另一个槽位」是个免费的时间机器\u003C\u002Fstrong>。OTA 装完别急着重启，先把新镜像 dump 出来，能省掉后面等全量包的几个小时。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>重建过 super 的机器，回官方的路不止是「刷回官方包」\u003C\u002Fstrong>。官方全量包里没有的分区，得去降级包里单独捞。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode>my_preload.img\u003C\u002Fcode> 记得留着\u003C\u002Fstrong>。以后只要重建 super（再刷非官方系统、整包线刷、回锁前重刷），都得重新创建并刷入这个分区，不然相机还会闪退。普通 OTA 不会覆盖它，不用担心。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>最后，刷机有风险，变砖、丢数据都得自己扛。命令我都实际跑过，但你的机器未必和我的完全一样，动手前先备份。\u003C\u002Fp>\n\u003Cp>就这样，去睡觉了。\u003C\u002Fp>\n",{"slug":963,"title":1147,"lang":1148,"desc":1149,"date":967,"tags":1150,"edit":967,"aiGenerated":969,"aiReview":970,"aiTranslated":1093,"readingTime":1151,"toc":1152,"content":1195},"把一加 Ace 5 從非官方系統刷回 ColorOS 16，順手把相機救了回來","zh-Hant","從非官方系統回刷 ColorOS 16 的完整踩坑記錄：三次翻車、一次相機閃退，以及 root 和 OTA 之間那點繞不開的事。",[],{"text":1095,"minutes":1096,"time":1097,"words":1098},[1153,1154,1156,1159,1162,1165,1168,1171,1174,1177,1180,1181,1184,1187,1190,1193],{"level":978,"text":1101,"slug":1101},{"level":978,"text":1155,"slug":1155},"兩條鐵律",{"level":978,"text":1157,"slug":1158},"驗包：不解 8GB 也能先看版本","驗包-不解-8gb-也能先看版本",{"level":978,"text":1160,"slug":1161},"認鏡像：別把 EROFS 當壞包","認鏡像-別把-erofs-當壞包",{"level":978,"text":1163,"slug":1164},"刷寫：兩個模式，各幹各的","刷寫-兩個模式-各幹各的",{"level":978,"text":1166,"slug":1167},"坑一：modem 在 fastbootd 裏寫不進去","坑一-modem-在-fastbootd-裏寫不進去",{"level":978,"text":1169,"slug":1170},"坑二：一加的 `my_*` 分區壓根不存在","坑二-一加的-my_-分區壓根不存在",{"level":978,"text":1172,"slug":1173},"坑三：`getvar` 的「不存在」有三種說法","坑三-getvar-的-不存在-有三種說法",{"level":978,"text":1175,"slug":1176},"回刷完了，然後呢","回刷完了-然後呢",{"level":1006,"text":1178,"slug":1179},"OTA 之後 root 會沒","ota-之後-root-會沒",{"level":1006,"text":1129,"slug":1130},{"level":1006,"text":1182,"slug":1183},"更省事的做法：安裝到未使用的插槽","更省事的做法-安裝到未使用的插槽",{"level":978,"text":1185,"slug":1186},"相機閃退：真正的長尾","相機閃退-真正的長尾",{"level":1006,"text":1188,"slug":1189},"爲什麼偏偏缺 `my_preload`","爲什麼偏偏缺-my_preload",{"level":1006,"text":1191,"slug":1192},"搶救：只取一個分區","搶救-只取一個分區",{"level":978,"text":1194,"slug":1194},"幾句碎碎念","\u003Ch2 id=\"起因\">起因\u003C\u002Fh2>\n\u003Cp>在做了近兩個月的 Apple 生態用戶之後，我最終還是決定放棄 Apple 生態，並打算把我原來的備用機 —— 這臺一加 Ace 5 國行（代號 \u003Ccode>giuliac\u003C\u002Fcode>，型號 \u003Ccode>PKG110\u003C\u002Fcode>）—— 刷回 ColorOS 16 過渡使用，等待新機發售之後再做決定。\u003C\u002Fp>\n\u003Cp>我一開始以爲這活兒頂多是「下個包、進 fastboot、刷完重啓」，結果整個過程下來，真正花時間的不是刷，而是\u003Cstrong>搞清楚爲什麼刷不進去\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>這篇就把整個過程記一遍，順便把 root 和 OTA 的事也理一理，最後再說那個讓我折騰到凌晨的相機閃退。\u003C\u002Fp>\n\u003Ch2 id=\"兩條鐵律\">兩條鐵律\u003C\u002Fh2>\n\u003Cp>先把結論放前面，這兩條是這次用真金白銀換來的：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>固件分區在 bootloader 刷，邏輯分區在 fastbootd 刷\u003C\u002Fstrong>。混着來就會出現「分區明明在，就是寫不進去」這種靈異現象。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>回鎖放最後\u003C\u002Fstrong>。必須能開機、功能正常、能檢測到 OTA 之後，才考慮 \u003Ccode>fastboot flashing lock\u003C\u002Fcode>。這次我沒回鎖，bootloader 保持 unlocked。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"驗包-不解-8gb-也能先看版本\">驗包：不解 8GB 也能先看版本\u003C\u002Fh2>\n\u003Cp>手上的包是 ColorOS 16 全量 OTA，8.7 GB，51 個分區。刷之前總得確認一下這包對不對，但又不想先解壓一遍。\u003C\u002Fp>\n\u003Cp>好在 \u003Ccode>payload.bin\u003C\u002Fcode> 在 OTA zip 裏是\u003Cstrong>未壓縮存儲\u003C\u002Fstrong>的，可以直接按偏移量讀：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># 先讀 zip 裏的 metadata\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">tar \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">xOf .\\86d1d2fe214c4a87804106baa2d4af4a.zip \u003C\u002Fspan>\u003Cspan style=\"color:#98C379\">'META-INF\u002Fcom\u002Fandroid\u002Fmetadata'\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># payload.bin 的起始偏移由 metadata 裏的 ota-property-files 給出\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#7F848E;font-style:italic\"># 直接按字節把開頭那段 protobuf manifest 讀出來，再全詞匹配分區名\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>要看的字段就那幾個：\u003Ccode>android_version\u003C\u002Fcode>、\u003Ccode>version_name\u003C\u002Fcode>、\u003Ccode>product_name\u003C\u002Fcode>、\u003Ccode>pre-device\u003C\u002Fcode>、\u003Ccode>post-build\u003C\u002Fcode>。這臺機的 \u003Ccode>pre-device\u003C\u002Fcode> 是 \u003Ccode>OP5D2BL1\u003C\u002Fcode>，對上了纔敢往下走。\u003C\u002Fp>\n\u003Cp>解包用的是 \u003Ccode>payload-dumper-go\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">.\\\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">payload-dumper-go.exe\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">c \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">8\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">o .\\cos16_img .\\86d1d2fe214c4a87804106baa2d4af4a.zip\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>payload 裏寫了 51 個分區，但 dumper 只產出 48 個鏡像，\u003Ccode>checkpoint_gc\u003C\u002Fcode>、\u003Ccode>otapreopt_script\u003C\u002Fcode>、\u003Ccode>qti_dynamic_partitions\u003C\u002Fcode> 這三個本來就不該直接刷。\u003C\u002Fp>\n\u003Ch2 id=\"認鏡像-別把-erofs-當壞包\">認鏡像：別把 EROFS 當壞包\u003C\u002Fh2>\n\u003Cblockquote>\n\u003Cp>這一段不是我手動做的，是刷機過程中讓 \u003Ca href=\"https:\u002F\u002Fomp.sh\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">omp\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" viewBox=\"0 0 24 24\" width=\"1em\" height=\"1em\" fill=\"currentColor\" aria-hidden=\"true\" style=\"display:inline;vertical-align:middle;margin-left:2px;\">\u003Cpath d=\"M5 21q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h7v2H5v14h14v-7h2v7q0 .825-.587 1.413T19 21zm4.7-5.3l-1.4-1.4L17.6 5H14V3h7v7h-2V6.4z\"\u002F>\u003C\u002Fsvg>\u003C\u002Fa> 順手校驗鏡像時踩到的。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>解出來之後本來想校驗一下，結果差點以爲下到了壞包。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>鏡像\u003C\u002Fth>\n\u003Cth>偏移\u003C\u002Fth>\n\u003Cth>魔數\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>boot\u003C\u002Fcode> \u002F \u003Ccode>init_boot\u003C\u002Fcode> \u002F \u003Ccode>recovery\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>414E44524F494421\u003C\u002Fcode>（\u003Ccode>ANDROID!\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>vendor_boot\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>564E4452424F4F54\u003C\u002Fcode>（\u003Ccode>VNDRBOOT\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>dtbo\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>D7B7AB1E\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>vbmeta*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>41564230\u003C\u002Fcode>（\u003Ccode>AVB0\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>system\u003C\u002Fcode> \u002F \u003Ccode>vendor\u003C\u002Fcode> \u002F \u003Ccode>odm\u003C\u002Fcode> \u002F \u003Ccode>my_*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>1024\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>E2E1F5E0\u003C\u002Fcode>（\u003Cstrong>EROFS\u003C\u002Fstrong>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>坑就在最後一行：Android 16 的系統分區是 \u003Cstrong>EROFS\u003C\u002Fstrong>，不是 ext4。你要是按 ext4 的 \u003Ccode>53EF\u003C\u002Fcode> 去找，只會得出「鏡像全壞了」的結論。而且 \u003Ccode>dtbo\u003C\u002Fcode> 的 \u003Ccode>D7B7AB1E\u003C\u002Fcode> 和 ext4 也長得有點像，別搞混。\u003C\u002Fp>\n\u003Ch2 id=\"刷寫-兩個模式-各幹各的\">刷寫：兩個模式，各幹各的\u003C\u002Fh2>\n\u003Cp>進 fastbootd 之後先確認身份：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot devices\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar product      \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期望 OP5D2BL1 \u002F giuliac \u002F PKG110\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar is\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">userspace \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期望 yes\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar current\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期望 a\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>然後就是逐個刷。邏輯分區和大部分物理分區都可以 \u003Ccode>--slot=all\u003C\u002Fcode>，但這臺機的邏輯分區\u003Cstrong>只存在於 \u003Ccode>_a\u003C\u002Fcode>\u003C\u002Fstrong>，所以對這堆分區用 \u003Ccode>--slot=all\u003C\u002Fcode> 會被拒，退回單槽就好。\u003C\u002Fp>\n\u003Cp>刷完清數據、首啓：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot bootloader\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">w\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot erase metadata\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"坑一-modem-在-fastbootd-裏寫不進去\">坑一：modem 在 fastbootd 裏寫不進去\u003C\u002Fh2>\n\u003Cp>第一個讓我懷疑人生的報錯：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Sending sparse 'modem_a' 1\u002F1 (219896 KB)   OKAY\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Writing 'modem_a'                          FAILED (remote: 'No such file or directory')\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>getvar\u003C\u002Fcode> 的表現更迷惑：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>partition-size:modem_a  → FAILED (remote: 'Could not open partition')\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>partition-type:modem_a  → raw        ← 這條居然有值\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>is-logical:modem_a      → FAILED (remote: 'Partition not found')\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>分區一眼「存在」，但就是寫不進去。\u003C\u002Fp>\n\u003Cp>根因其實很簡單：\u003Cstrong>fastbootd 是用戶態，裏面沒有 \u003Ccode>modem_a\u003C\u002Fcode> 的塊設備節點\u003C\u002Fstrong>，不是分區壞了。切回 bootloader 一查，\u003Ccode>partition-size:modem_a\u003C\u002Fcode> 是 \u003Ccode>0x15E00000\u003C\u002Fcode>，好得很。\u003C\u002Fp>\n\u003Cp>所以 modem 得在 bootloader 裏刷：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot bootloader\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a modem modem.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>一次過。\u003C\u002Fp>\n\u003Ch2 id=\"坑二-一加的-my_-分區壓根不存在\">坑二：一加的 \u003Ccode>my_*\u003C\u002Fcode> 分區壓根不存在\u003C\u002Fh2>\n\u003Cp>第二個坑更離譜。刷那 8 個 \u003Ccode>my_*\u003C\u002Fcode> 分區時全部失敗，一開始我以爲是 super 空間不夠。\u003C\u002Fp>\n\u003Cp>後來纔想明白：\u003Cstrong>刷非官方系統時按 AOSP 佈局重建了 super 分區表\u003C\u002Fstrong>，只留了 \u003Ccode>system\u003C\u002Fcode>、\u003Ccode>system_ext\u003C\u002Fcode>、\u003Ccode>product\u003C\u002Fcode>、\u003Ccode>vendor\u003C\u002Fcode>、\u003Ccode>odm\u003C\u002Fcode>、\u003Ccode>system_dlkm\u003C\u002Fcode>、\u003Ccode>vendor_dlkm\u003C\u002Fcode> 這套標準佈局。一加自己那套 \u003Ccode>my_product\u003C\u002Fcode>、\u003Ccode>my_stock\u003C\u002Fcode>、\u003Ccode>my_bigball\u003C\u002Fcode>、\u003Ccode>my_carrier\u003C\u002Fcode>、\u003Ccode>my_engineering\u003C\u002Fcode>、\u003Ccode>my_heytap\u003C\u002Fcode>、\u003Ccode>my_manifest\u003C\u002Fcode>、\u003Ccode>my_region\u003C\u002Fcode> 全都沒建。\u003C\u002Fp>\n\u003Cp>不是空間不足，是\u003Cstrong>根本沒這個分區\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>解法是先創建再刷：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot create\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">logical\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">partition my_stock_a \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0x100000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a my_stock my_stock.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>佔位尺寸隨便給，\u003Ccode>fastboot flash\u003C\u002Fcode> 會自動 resize 到鏡像實際大小。\u003C\u002Fp>\n\u003Cp>順便記一下這臺機最終的尺寸，以後對得上號：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>my_product      1520168960 → 0x5A9BF000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_stock        4491419648 → 0x10BB5A000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_region        7057408  → 0x6BB000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>my_manifest       729088  → 0xB2000\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>modem           314667008 → 0x15E00000\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"坑三-getvar-的-不存在-有三種說法\">坑三：\u003Ccode>getvar\u003C\u002Fcode> 的「不存在」有三種說法\u003C\u002Fh2>\n\u003Cp>這個坑不致命，但特別容易誤判。我拿一個肯定不存在的分區名校準了一下語義：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>查詢\u003C\u002Fth>\n\u003Cth>分區存在\u003C\u002Fth>\n\u003Cth>分區不存在\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>partition-type:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>raw\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Invalid partition')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>partition-size:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>0x...\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Could not open partition')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>has-slot:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>yes\u003C\u002Fcode> \u002F \u003Ccode>no\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>no\u003C\u002Fcode>（\u003Cstrong>無報錯\u003C\u002Fstrong>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>is-logical:X\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>yes\u003C\u002Fcode> \u002F \u003Ccode>no\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>FAILED (remote: 'Partition not found')\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>所以判斷分區到底在不在，\u003Cstrong>只看 \u003Ccode>partition-type\u003C\u002Fcode> 和 \u003Ccode>is-logical\u003C\u002Fcode>\u003C\u002Fstrong>。\u003Ccode>has-slot\u003C\u002Fcode> 那個「不存在也返回 no」的設定，坑死人不償命。\u003C\u002Fp>\n\u003Ch2 id=\"回刷完了-然後呢\">回刷完了，然後呢\u003C\u002Fh2>\n\u003Cp>刷完開機，電話、短信、雙卡、Wi-Fi、藍牙、NFC、指紋、充電都正常，相機（第三方）也能用。最硬的完整性證據是：\u003Cstrong>系統更新裏能檢測到 OTA\u003C\u002Fstrong>。到這一步，回刷算是成功了。\u003C\u002Fp>\n\u003Cp>這裏順便把 root 和 OTA 的關係理一下，免得下次又忘。\u003C\u002Fp>\n\u003Ch3 id=\"ota-之後-root-會沒\">OTA 之後 root 會沒\u003C\u002Fh3>\n\u003Cp>OTA 會覆蓋 \u003Ccode>boot\u003C\u002Fcode>，KernelSU 的 patch 自然也就沒了。這不是風險，重新 patch 就行。\u003C\u002Fp>\n\u003Ch3 id=\"重新-root-窗口期\">重新 root：窗口期\u003C\u002Fh3>\n\u003Cp>刷回 ColorOS 之後，很快就收到了最新的官方 OTA 更新。裝完之後先別點「立即重啓」—— 這時候新鏡像已經寫進另一個槽位，舊系統的 root 還在，直接讀出來就是：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb shell su \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">c \u003C\u002Fspan>\u003Cspan style=\"color:#98C379\">\"dd if=\u002Fdev\u002Fblock\u002Fby-name\u002Fboot_b of=\u002Fdata\u002Flocal\u002Ftmp\u002Fboot_b.img\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb pull \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">data\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">local\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">tmp\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">boot_b.img\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>優點是不用等全量包，鏡像 100% 對版。缺點是\u003Cstrong>一重啓窗口就關了\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>Ace 5 是 Android 13+ 的 GKI 機型，KernelSU patch 的是 \u003Ccode>boot\u003C\u002Fcode>，別刷到 \u003Ccode>init_boot\u003C\u002Fcode> 上。\u003C\u002Fp>\n\u003Ch3 id=\"更省事的做法-安裝到未使用的插槽\">更省事的做法：安裝到未使用的插槽\u003C\u002Fh3>\n\u003Cp>KernelSU Manager 裏有個「安裝到未使用的插槽（OTA 後）」，直接把 patch 好的鏡像寫進另一個槽位，重啓之後就是帶 root 的新系統。比手動 dd 省事得多。\u003C\u002Fp>\n\u003Cp>代價是它不會在電腦上留下原廠鏡像的備份。真出問題想回滾，手上就沒有那份 dump 出來的 \u003Ccode>boot\u003C\u002Fcode>。所以我這次還是先手動 dd 了一份，至少心裏踏實。\u003C\u002Fp>\n\u003Cp>還有個小細節：KernelSU 下 \u003Ccode>ls \u002Fsystem\u002Fbin\u002Fsu\u003C\u002Fcode> 找不到是\u003Cstrong>正常現象\u003C\u002Fstrong>，它靠 execve hook，不是真的放了個 su 文件。別拿這個判斷 root 丟沒丟。\u003C\u002Fp>\n\u003Ch2 id=\"相機閃退-真正的長尾\">相機閃退：真正的長尾\u003C\u002Fh2>\n\u003Cp>系統刷好了，root 的事也想明白了，結果打開自帶相機——\u003Cstrong>秒退\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>第三方相機一切正常，說明攝像頭硬件、HAL、權限都沒問題。崩潰日誌也很固定：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>java.lang.UnsatisfiedLinkError: dlopen failed: library \"libCombineLutJni.so\" not found\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.camera.combinelut.CombineLutJNI.&#x3C;clinit>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.ocs.camera.CombineLutHelper.&#x3C;init>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    at com.oplus.camera.common.utils.Lut3dDataManager.&#x3C;init>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>加載原生庫失敗。那我先找這個庫在哪：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>$ adb shell find \u002F -name libCombineLutJni.so\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u002Fmy_product\u002Flib64\u002FlibCombineLutJni.so\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>庫在 \u003Ccode>\u002Fmy_product\u002Flib64\u003C\u002Fcode>，但相機是 PRODUCT App，它的 linker 命名空間只認 \u003Ccode>\u002Fproduct\u002Flib64:\u002Fsystem\u002Fproduct\u002Flib64\u003C\u002Fcode>，不會直接去看 \u003Ccode>\u002Fmy_product\u002Flib64\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>正常情況應該由 overlay 把兩者合併起來。\u003Ccode>\u002Fvendor\u002Fetc\u002Ffstab.qcom\u003C\u002Fcode> 裏也確實寫了：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlay-overlay \u002Fproduct\u002Flib64 overlay ... lowerdir=\u002Fmy_region\u002Flib64:\u002Fmy_preload\u002Flib64:\u002Fmy_product\u002Flib64:\u002Fmy_heytap\u002Flib64:\u002Fmy_stock\u002Flib64:\u002Fmy_engineering\u002Flib64:\u002Fproduct\u002Flib64\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>結果一查 \u003Ccode>\u002Fproc\u002Fmounts\u003C\u002Fcode>，\u003Ccode>\u002Fproduct\u002Fapp\u003C\u002Fcode>、\u003Ccode>\u002Fproduct\u002Fpriv-app\u003C\u002Fcode>、\u003Ccode>\u002Fproduct\u002Flib64\u003C\u002Fcode>、\u003Ccode>\u002Fproduct\u002Flib\u003C\u002Fcode> 這\u003Cstrong>四個 overlay 全部沒掛上\u003C\u002Fstrong>。同一份 fstab 裏 \u003Ccode>\u002Fproduct\u002Fetc\u002Fpermissions\u003C\u002Fcode> 和 \u003Ccode>\u002Fproduct\u002Fframework\u003C\u002Fcode> 倒是正常的。\u003C\u002Fp>\n\u003Cp>手動復現掛載，內核直接告訴我原因：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlayfs: failed to resolve '\u002Fmy_preload\u002Flib64': -2\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>-2\u003C\u002Fcode> 就是 ENOENT。\u003Cstrong>\u003Ccode>\u002Fmy_preload\u003C\u002Fcode> 這個分區不存在。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>去掉它再掛，立刻成功，\u003Ccode>\u002Fproduct\u002Flib64\u002FlibCombineLutJni.so\u003C\u002Fcode> 馬上出現，相機也就能啓動了。\u003C\u002Fp>\n\u003Cp>完整因果鏈是這樣的：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>my_preload 分區缺失\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → \u002Fproduct\u002F{app,priv-app,lib64,lib} 四個 overlay 掛載失敗\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → \u002Fproduct\u002Flib64 看不到 \u002Fmy_product\u002Flib64 裏的庫\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  → 相機加載 libCombineLutJni.so 失敗 → 閃退\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>fstab 裏其實寫了 \u003Ccode>overlayfs_remove_missing_lowerdir\u003C\u002Fcode>，按理說缺目錄應該自動跳過。但本機啓動路徑實際執行的是原始 lowerdir 列表，那個濾波函數壓根沒被調用，所以缺目錄就是硬 ENOENT。\u003C\u002Fp>\n\u003Ch3 id=\"爲什麼偏偏缺-my_preload\">爲什麼偏偏缺 \u003Ccode>my_preload\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>回到前面那個坑：刷非官方系統時，super 分區表按 AOSP 佈局被重建，原廠的 \u003Ccode>my_preload\u003C\u002Fcode>、\u003Ccode>my_company\u003C\u002Fcode> 一起被刪了。回刷的時候我只創建了 ColorOS 16 全量 OTA 裏存在的分區，而\u003Cstrong>全量包不包含 \u003Ccode>my_preload\u003C\u002Fcode>\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>這兩個是歐加的預裝分區（社區叫「雙廣分區」），不隨全量 OTA 更新，只在降級包 \u002F 回退包 \u002F 出廠鏡像裏有。\u003C\u002Fp>\n\u003Cp>本機還留着證據：\u003Ccode>\u002Fmy_manifest\u002Fetc\u002Fmy_preload.map\u003C\u002Fcode> 裏全是 \u003Ccode>\u002Fmy_preload\u002Fdel-app\u002F...\u003C\u002Fcode> 的預裝清單，說明出廠佈局裏本來就有它。\u003C\u002Fp>\n\u003Ch3 id=\"搶救-只取一個分區\">搶救：只取一個分區\u003C\u002Fh3>\n\u003Cp>我重新去下載了一箇舊的全量降級包 \u003Ccode>PKG110_16.0.9.400(CN01)\u003C\u002Fcode>，裏面帶 \u003Ccode>my_preload\u003C\u002Fcode>（1.8 GB）和 \u003Ccode>my_company\u003C\u002Fcode>（336 kB）。\u003C\u002Fp>\n\u003Cp>注意：\u003Cstrong>只抽分區，不刷整包\u003C\u002Fstrong>。這個包比當前系統舊，還帶 \u003Ccode>wipe=1\u003C\u002Fcode> 和 \u003Ccode>ota-downgrade=yes\u003C\u002Fcode>，整包刷進去就是主動踩 ARB。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">.\\\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">payload-dumper-go.exe\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\"> -\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">p my_preload \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">o .\\my_preload_16.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0.9\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">400\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> .\\670f63e2a29741578637222c6b7be73b.zip\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>抽出來的鏡像 1,784,397,824 字節，EROFS，AVB 公鑰 SHA-1 是 \u003Ccode>1a130f9a...66f6\u003C\u002Fcode>，和設備上 \u003Ccode>\u002Fvendor\u002Fetc\u002Foplus_avb.pubkey\u003C\u002Fcode> 完全一致 —— 官方簽名，能過 \u003Ccode>avb_keys\u003C\u002Fcode> 校驗。\u003C\u002Fp>\n\u003Cp>刷之前先看空間：super 總容量 13.58 GiB，已用 10.0 GiB 左右，剩 3.58 GiB，放 1.66 GiB 綽綽有餘。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-powershell\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">adb reboot fastboot\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot getvar is\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">userspace   \u003C\u002Fspan>\u003Cspan style=\"color:#7F848E;font-style:italic\"># 期望 yes\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot create\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">logical\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">partition my_preload_a \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1784397824\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot flash \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">--\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">slot\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">a my_preload .\\my_preload_16.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0.9\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">400\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">\\my_preload.img\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">fastboot reboot\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>刷完 \u003Ccode>my_preload_a\u003C\u002Fcode> 掛在 dm-verity 上（\u003Ccode>dm-29\u003C\u002Fcode>，erofs），說明 hashtree 校驗過了。再查 overlay：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>overlay-overlay \u002Fproduct\u002Flib64 ... lowerdir=\u002Fmy_region\u002Flib64:\u002Fmy_preload\u002Flib64:\u002Fmy_product\u002Flib64:...\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>四個 overlay 全回來了，庫可見，相機啓動後進程穩定存活，\u003Ccode>dumpsys media.camera\u003C\u002Fcode> 裏能看到 active session，crash buffer 也沒有新增記錄。\u003C\u002Fp>\n\u003Cp>搞定。\u003C\u002Fp>\n\u003Ch2 id=\"幾句碎碎念\">幾句碎碎念\u003C\u002Fh2>\n\u003Cp>這次折騰下來，最大的感受是：\u003Cstrong>刷機真正難的不是刷，是理解「爲什麼這個分區刷不進去」\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>幾個具體的教訓：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>別隻看報錯，要看報錯背後的模式\u003C\u002Fstrong>。\u003Ccode>modem\u003C\u002Fcode> 寫不進去和 \u003Ccode>my_*\u003C\u002Fcode> 寫不進去，報錯都模棱兩可，但一個是 fastbootd 沒有節點，一個是分區根本不存在，解法完全不同。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>A\u002FB 機型的「另一個槽位」是個免費的時間機器\u003C\u002Fstrong>。OTA 裝完別急着重啓，先把新鏡像 dump 出來，能省掉後面等全量包的幾個小時。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>重建過 super 的機器，回官方的路不止是「刷回官方包」\u003C\u002Fstrong>。官方全量包裏沒有的分區，得去降級包裏單獨撈。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode>my_preload.img\u003C\u002Fcode> 記得留着\u003C\u002Fstrong>。以後只要重建 super（再刷非官方系統、整包線刷、回鎖前重刷），都得重新創建並刷入這個分區，不然相機還會閃退。普通 OTA 不會覆蓋它，不用擔心。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>最後，刷機有風險，變磚、丟數據都得自己扛。命令我都實際跑過，但你的機器未必和我的完全一樣，動手前先備份。\u003C\u002Fp>\n\u003Cp>就這樣，去睡覺了。\u003C\u002Fp>\n",[1197,1217,1237,1257,1277,1326,1363,1391,1415,1433,1448,1461,1471,1494,1517,1536,1551,1564],{"slug":963,"title":964,"lang":965,"desc":966,"date":967,"tags":1198,"edit":967,"aiGenerated":969,"aiReview":970,"aiTranslated":969,"readingTime":1199,"toc":1200},[],{"text":972,"minutes":973,"time":974,"words":975},[1201,1202,1203,1204,1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1215,1216],{"level":978,"text":979,"slug":980},{"level":978,"text":982,"slug":983},{"level":978,"text":985,"slug":986},{"level":978,"text":988,"slug":989},{"level":978,"text":991,"slug":992},{"level":978,"text":994,"slug":995},{"level":978,"text":997,"slug":998},{"level":978,"text":1000,"slug":1001},{"level":978,"text":1003,"slug":1004},{"level":1006,"text":1007,"slug":1008},{"level":1006,"text":1010,"slug":1011},{"level":1006,"text":1013,"slug":1014},{"level":978,"text":1016,"slug":1017},{"level":1006,"text":1019,"slug":1020},{"level":1006,"text":1022,"slug":1023},{"level":978,"text":1025,"slug":1026},{"slug":963,"title":1029,"lang":1030,"desc":1031,"date":967,"tags":1218,"edit":967,"aiGenerated":969,"aiReview":970,"aiTranslated":969,"readingTime":1219,"toc":1220},[],{"text":1034,"minutes":1035,"time":1036,"words":1037},[1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236],{"level":978,"text":1040,"slug":1041},{"level":978,"text":1043,"slug":1044},{"level":978,"text":1046,"slug":1047},{"level":978,"text":1049,"slug":1050},{"level":978,"text":1052,"slug":1053},{"level":978,"text":1055,"slug":1056},{"level":978,"text":1058,"slug":1059},{"level":978,"text":1061,"slug":1062},{"level":978,"text":1064,"slug":1065},{"level":1006,"text":1067,"slug":1068},{"level":1006,"text":1070,"slug":1071},{"level":1006,"text":1073,"slug":1074},{"level":978,"text":1076,"slug":1077},{"level":1006,"text":1079,"slug":1080},{"level":1006,"text":1082,"slug":1083},{"level":978,"text":1085,"slug":1086},{"slug":963,"title":1089,"lang":1090,"desc":1091,"date":967,"tags":1238,"edit":967,"aiGenerated":969,"aiReview":970,"aiTranslated":1093,"readingTime":1239,"toc":1240},[],{"text":1095,"minutes":1096,"time":1097,"words":1098},[1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1255,1256],{"level":978,"text":1101,"slug":1101},{"level":978,"text":1103,"slug":1103},{"level":978,"text":1105,"slug":1106},{"level":978,"text":1108,"slug":1109},{"level":978,"text":1111,"slug":1112},{"level":978,"text":1114,"slug":1115},{"level":978,"text":1117,"slug":1118},{"level":978,"text":1120,"slug":1121},{"level":978,"text":1123,"slug":1124},{"level":1006,"text":1126,"slug":1127},{"level":1006,"text":1129,"slug":1130},{"level":1006,"text":1132,"slug":1133},{"level":978,"text":1135,"slug":1136},{"level":1006,"text":1138,"slug":1139},{"level":1006,"text":1141,"slug":1142},{"level":978,"text":1144,"slug":1144},{"slug":963,"title":1147,"lang":1148,"desc":1149,"date":967,"tags":1258,"edit":967,"aiGenerated":969,"aiReview":970,"aiTranslated":1093,"readingTime":1259,"toc":1260},[],{"text":1095,"minutes":1096,"time":1097,"words":1098},[1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272,1273,1274,1275,1276],{"level":978,"text":1101,"slug":1101},{"level":978,"text":1155,"slug":1155},{"level":978,"text":1157,"slug":1158},{"level":978,"text":1160,"slug":1161},{"level":978,"text":1163,"slug":1164},{"level":978,"text":1166,"slug":1167},{"level":978,"text":1169,"slug":1170},{"level":978,"text":1172,"slug":1173},{"level":978,"text":1175,"slug":1176},{"level":1006,"text":1178,"slug":1179},{"level":1006,"text":1129,"slug":1130},{"level":1006,"text":1182,"slug":1183},{"level":978,"text":1185,"slug":1186},{"level":1006,"text":1188,"slug":1189},{"level":1006,"text":1191,"slug":1192},{"level":978,"text":1194,"slug":1194},{"slug":1278,"title":1279,"lang":965,"desc":1280,"date":1281,"tags":1282,"edit":1281,"aiGenerated":969,"aiReview":1283,"aiTranslated":969,"readingTime":1284,"toc":1289},"sorting-sins","A Whimsical \"Sin of Sorting\": What If You Use a Random Comparator for Sorting?","What happens if I use a random function as a comparator when sorting?","2026-07-15T00:00:00.000Z",[],"充分核对",{"text":1285,"minutes":1286,"time":1287,"words":1288},"7 min read",6.67,400200,1334,[1290,1293,1296,1299,1302,1305,1308,1311,1314,1317,1320,1323],{"level":978,"text":1291,"slug":1292},"A Whimsical Thought","a-whimsical-thought",{"level":978,"text":1294,"slug":1295},"Initial Guesses","initial-guesses",{"level":978,"text":1297,"slug":1298},"Behavior by Language","behavior-by-language",{"level":1006,"text":1300,"slug":1301},"JavaScript (Node.js v24.14.0 \u002F Bun.js 1.3.14 \u002F Deno.js 2.9.2)","javascript-node-js-v24-14-0-bun-js-1-3-14-deno-js-2-9-2",{"level":1006,"text":1303,"slug":1304},"Python (Python 3.14.3)","python-python-3-14-3",{"level":1006,"text":1306,"slug":1307},"Go (go 1.26.2)","go-go-1-26-2",{"level":1006,"text":1309,"slug":1310},"C++ (clang 22.1.8)","c-clang-22-1-8",{"level":1006,"text":1312,"slug":1313},"C# (.NET 10.0.301)","c-net-10-0-301",{"level":1006,"text":1315,"slug":1316},"Java (JDK 26.0.1)","java-jdk-26-0-1",{"level":1006,"text":1318,"slug":1319},"Rust (rustc 1.97.0)","rust-rustc-1-97-0",{"level":978,"text":1321,"slug":1322},"Rust: But Is That Really the Whole Story?","rust-but-is-that-really-the-whole-story",{"level":978,"text":1324,"slug":1325},"Summary","summary",{"slug":1278,"title":1327,"lang":1030,"desc":1328,"date":1281,"tags":1329,"edit":1281,"aiGenerated":969,"aiReview":1283,"aiTranslated":969,"readingTime":1330,"toc":1334},"突発的な思いつき「ソートの罪」：ソートにランダムコンパレータを使ったらどうなるか","ソートの際に比較関数としてランダム関数を使ったら、何が起こるのか？",[],{"text":972,"minutes":1331,"time":1332,"words":1333},10.49,629400,2098,[1335,1338,1341,1344,1346,1348,1350,1352,1354,1356,1358,1361],{"level":978,"text":1336,"slug":1337},"突発的な思いつき","突発的-思",{"level":978,"text":1339,"slug":1340},"結果の予想","結果-予想",{"level":978,"text":1342,"slug":1343},"各言語の振る舞い","各言語-振-舞",{"level":1006,"text":1345,"slug":1301},"JavaScript（Node.js v24.14.0 \u002F Bun.js 1.3.14 \u002F Deno.js 2.9.2）",{"level":1006,"text":1347,"slug":1304},"Python（Python 3.14.3）",{"level":1006,"text":1349,"slug":1307},"Go（go 1.26.2）",{"level":1006,"text":1351,"slug":1310},"C++（clang 22.1.8）",{"level":1006,"text":1353,"slug":1313},"C#（.NET 10.0.301）",{"level":1006,"text":1355,"slug":1316},"Java（JDK 26.0.1）",{"level":1006,"text":1357,"slug":1319},"Rust（rustc 1.97.0）",{"level":978,"text":1359,"slug":1360},"Rust：だが、本当にそうなのか？","rust-本当",{"level":978,"text":1362,"slug":1041},"まとめ",{"slug":1278,"title":1364,"lang":1090,"desc":1365,"date":1281,"tags":1366,"edit":1281,"aiGenerated":969,"aiReview":1283,"aiTranslated":1093,"readingTime":1367,"toc":1372},"一次突发奇想的「排序之罪」：如果对排序使用随机比较器","如果我在排序的时候用一个随机函数做比较器，会发生什么？",[],{"text":1368,"minutes":1369,"time":1370,"words":1371},"10 min read",9.145,548700,1829,[1373,1375,1377,1379,1380,1381,1382,1383,1384,1385,1386,1389],{"level":978,"text":1374,"slug":1374},"突发奇想",{"level":978,"text":1376,"slug":1376},"对结果的猜想",{"level":978,"text":1378,"slug":1378},"各语言表现",{"level":1006,"text":1345,"slug":1301},{"level":1006,"text":1347,"slug":1304},{"level":1006,"text":1349,"slug":1307},{"level":1006,"text":1351,"slug":1310},{"level":1006,"text":1353,"slug":1313},{"level":1006,"text":1355,"slug":1316},{"level":1006,"text":1357,"slug":1319},{"level":978,"text":1387,"slug":1388},"Rust：可是，当真如此吗？","rust-可是-当真如此吗",{"level":978,"text":1390,"slug":1390},"总结",{"slug":1278,"title":1392,"lang":1148,"desc":1393,"date":1281,"tags":1394,"edit":1281,"aiGenerated":969,"aiReview":1283,"aiTranslated":1093,"readingTime":1395,"toc":1396},"一次突發奇想的「排序之罪」：如果對排序使用隨機比較器","如果我在排序的時候用一個隨機函數做比較器，會發生什麼？",[],{"text":1368,"minutes":1369,"time":1370,"words":1371},[1397,1399,1401,1403,1404,1405,1406,1407,1408,1409,1410,1413],{"level":978,"text":1398,"slug":1398},"突發奇想",{"level":978,"text":1400,"slug":1400},"對結果的猜想",{"level":978,"text":1402,"slug":1402},"各語言表現",{"level":1006,"text":1345,"slug":1301},{"level":1006,"text":1347,"slug":1304},{"level":1006,"text":1349,"slug":1307},{"level":1006,"text":1351,"slug":1310},{"level":1006,"text":1353,"slug":1313},{"level":1006,"text":1355,"slug":1316},{"level":1006,"text":1357,"slug":1319},{"level":978,"text":1411,"slug":1412},"Rust：可是，當真如此嗎？","rust-可是-當真如此嗎",{"level":978,"text":1414,"slug":1414},"總結",{"slug":1416,"title":1417,"lang":965,"desc":1418,"date":1419,"tags":1420,"edit":1419,"aiGenerated":1093,"aiReview":1041,"aiTranslated":969,"readingTime":1421,"toc":1426},"cf-network","How to Display the Current Cloudflare Network Used on a Website","By parsing Cloudflare's \u002Fcdn-cgi\u002Ftrace endpoint to obtain the current network node ID, and then matching the node name using Cloudflare Status's public API, you can dynamically display the name of the Cloudflare network that the visitor is using on your website.","2026-05-24T00:00:00.000Z",[],{"text":1422,"minutes":1423,"time":1424,"words":1425},"2 min read",1.88,112800,376,[1427,1430],{"level":1006,"text":1428,"slug":1429},"Implementation Approach","implementation-approach",{"level":1006,"text":1431,"slug":1432},"Code Implementation","code-implementation",{"slug":1416,"title":1434,"lang":1030,"desc":1435,"date":1419,"tags":1436,"edit":1419,"aiGenerated":1093,"aiReview":1041,"aiTranslated":969,"readingTime":1437,"toc":1442},"ウェブサイトに現在使用中の Cloudflare ネットワークを表示する方法","Cloudflare の \u002Fcdn-cgi\u002Ftrace エンドポイントを解析して現在のネットワークノード ID を取得し、Cloudflare Status の公開 API でノード名を照合することで、訪問者が使用中の Cloudflare ネットワーク名をウェブサイトに動的に表示します。",[],{"text":1438,"minutes":1439,"time":1440,"words":1441},"3 min read",2.645,158700,529,[1443,1445],{"level":1006,"text":1444,"slug":1444},"実装方法",{"level":1006,"text":1446,"slug":1447},"コード実装","実装",{"slug":1416,"title":1449,"lang":1090,"desc":1450,"date":1419,"tags":1451,"edit":1419,"aiGenerated":1093,"aiReview":1041,"aiTranslated":1093,"readingTime":1452,"toc":1456},"如何在网站显示当前使用的 Cloudflare 网络","通过解析 Cloudflare 的 \u002Fcdn-cgi\u002Ftrace 接口获取当前网络节点 ID，再结合 Cloudflare Status 的公共 API 匹配出节点名称，从而在网站上动态显示访客正在使用的 Cloudflare 网络名称。",[],{"text":1438,"minutes":1453,"time":1454,"words":1455},2.57,154200,514,[1457,1459],{"level":1006,"text":1458,"slug":1458},"实现方式",{"level":1006,"text":1460,"slug":1460},"代码实现",{"slug":1416,"title":1462,"lang":1148,"desc":1463,"date":1419,"tags":1464,"edit":1419,"aiGenerated":1093,"aiReview":1041,"aiTranslated":1093,"readingTime":1465,"toc":1466},"如何在網站顯示當前使用的 Cloudflare 網絡","通過解析 Cloudflare 的 \u002Fcdn-cgi\u002Ftrace 接口獲取當前網絡節點 ID，再結合 Cloudflare Status 的公共 API 匹配出節點名稱，從而在網站上動態顯示訪客正在使用的 Cloudflare 網絡名稱。",[],{"text":1438,"minutes":1453,"time":1454,"words":1455},[1467,1469],{"level":1006,"text":1468,"slug":1468},"實現方式",{"level":1006,"text":1470,"slug":1470},"代碼實現",{"slug":1472,"title":1473,"lang":965,"desc":1474,"date":1475,"tags":1476,"edit":1475,"aiGenerated":1093,"aiReview":1041,"aiTranslated":1093,"readingTime":1477,"toc":1481},"about-name","About the Name of Weixi","The origin of the name \"Weixi\"","2026-05-18T00:00:00.000Z",[],{"text":1438,"minutes":1478,"time":1479,"words":1480},2.025,121500,405,[1482,1485,1488,1491],{"level":1006,"text":1483,"slug":1484},"My Names","my-names",{"level":1006,"text":1486,"slug":1487},"About \"Kuriyona\"","about-kuriyona",{"level":1006,"text":1489,"slug":1490},"About \"Weixi\" (未晞)","about-weixi-未晞",{"level":1006,"text":1492,"slug":1493},"A Short Note","a-short-note",{"slug":1472,"title":1495,"lang":1030,"desc":1496,"date":1475,"tags":1497,"edit":1498,"aiGenerated":1093,"aiReview":1041,"aiTranslated":1093,"readingTime":1499,"toc":1504},"未晞の名前について","未晞（ウェイシー）という名前の由来",[],"2026-06-20T00:00:00.000Z",{"text":1500,"minutes":1501,"time":1502,"words":1503},"4 min read",3.1,186000,620,[1505,1508,1511,1514],{"level":1006,"text":1506,"slug":1507},"わたしの名前","名前",{"level":1006,"text":1509,"slug":1510},"「Kuriyona」について","kuriyona",{"level":1006,"text":1512,"slug":1513},"「未晞」について","未晞",{"level":1006,"text":1515,"slug":1516},"小さな記","小-記",{"slug":1472,"title":1518,"lang":1090,"desc":1519,"date":1475,"tags":1520,"edit":1475,"aiGenerated":1093,"aiReview":1041,"aiTranslated":1093,"readingTime":1521,"toc":1525},"关于未晞的名字","未晞名字的由来",[],{"text":1422,"minutes":1522,"time":1523,"words":1524},1.61,96600,322,[1526,1528,1531,1534],{"level":1006,"text":1527,"slug":1527},"我的名字",{"level":1006,"text":1529,"slug":1530},"关于「Kuriyona」","关于-kuriyona",{"level":1006,"text":1532,"slug":1533},"关于「未晞」","关于-未晞",{"level":1006,"text":1535,"slug":1535},"小记",{"slug":1472,"title":1537,"lang":1148,"desc":1538,"date":1475,"tags":1539,"edit":1475,"aiGenerated":1093,"aiReview":1041,"aiTranslated":1093,"readingTime":1540,"toc":1541},"關於未晞的名字","未晞名字的由來",[],{"text":1422,"minutes":1522,"time":1523,"words":1524},[1542,1543,1546,1549],{"level":1006,"text":1527,"slug":1527},{"level":1006,"text":1544,"slug":1545},"關於「Kuriyona」","關於-kuriyona",{"level":1006,"text":1547,"slug":1548},"關於「未晞」","關於-未晞",{"level":1006,"text":1550,"slug":1550},"小記",{"slug":1552,"title":1553,"lang":1090,"desc":1554,"date":1555,"tags":1556,"edit":1557,"aiGenerated":1093,"aiReview":1041,"aiTranslated":1093,"readingTime":1558,"toc":1563},"hello-world","Hello World","这是未晞的第一个博客喵~","2026-05-16T00:00:00.000Z",[],"2026-05-17T00:00:00.000Z",{"text":1559,"minutes":1560,"time":1561,"words":1562},"1 min read",0.125,7500,25,[],{"slug":1552,"title":1553,"lang":1148,"desc":1565,"date":1555,"tags":1566,"edit":1557,"aiGenerated":1093,"aiReview":1041,"aiTranslated":1093,"readingTime":1567,"toc":1568},"這是未晞的第一個博客喵~",[],{"text":1559,"minutes":1560,"time":1561,"words":1562},[],1792358334971]